Model events not fire in laravel testing - laravel

I'm writing some tests for my model when it was created/updated/deleted. When I test it manually, it worked. But when I test in test file, asserting a event dispatch always fail.
This is my test function:
public function test_booted_method_ran()
{
$model = AcCard::factory()->create();
$this->expectsEvents('eloquent.created: \App\Models\AcCard');
}
This test case always fails. I've tried to inspect expectsEvents and added dd() to check event list:
public function expectsEvents($events)
{
$events = is_array($events) ? $events : func_get_args();
$this->withoutEvents();
$this->beforeApplicationDestroyed(function () use ($events) {
$fired = $this->getFiredEvents($events);
dd($fired); // Added this code line
$this->assertEmpty(
$eventsNotFired = array_diff($events, $fired),
'These expected events were not fired: ['.implode(', ', $eventsNotFired).']'
);
});
return $this;
}
And result is an empty array.
What I tried
I tried to add this code lines above model creating
AcCard::flushEventListeners();
AcCard::boot();
AcCard::booted();
But it doesn't work.

Related

Opening request value-based view without repeating code

I am trying to open different views based upon a request value. For example, if the value of $request is set to one, then view one should open. If the $request value is two, then view two should open.
My code is working fine, but right now, I will have to repeat code for each view. How can I do it without repeating the if condition?
Scenario
public function printreports(Request $request)
{
$reports = $request->get('reports');
if ($reports === 1) {
return view('teachers.report1', compact('anything'));
}
if ($reports === 2) {
return view('teachers.report2', compact('anything'));
}
}
For large amount of files with similar name pattern:
$viewName = sprintf('teachers.report%d', $request->get('reports', 1))
if (!\View::exists($viewName)) {
___ throw an error or return default view ____
}
return view($viewName, compact('anything'));

How to test the code logic of ngOnInit in Jasmine and Angular

My component looks for presence of a parameter in the route in ngOnInit. If the parameter is not present, it shows error. I want to test this logic.
ngOnInit() {
this.id = this.route.snapshot.paramMap.get("id");
if(this.id != null) {
... } else{
console.log("didn't get id from the route");
this.showDialog(...);
}
}
I wrote the following spec. In the spec, the parameter is not passed in the route
beforeEach(async() => {
fixture = TestBed.createComponent(QuestionDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fit('should show error if question details of a question can\'t be retrieved', () => {
spyOn(component,'showDialog');
expect(componetn.showDialog).toHaveBeenCalled();
});
but my test case fails with reason Expected spy showDialog to have been called.
I suppose the issue is that showDialog gets called when the component gets created before the it is called.
How do I test the logic in ngOnInit? I need the component before I can test it (i.e. call it) and I want to test the logic which gets executed while the component is being created.
To test the ngOnInit method, you just need to call it:
component.ngOnInit();
And the route value can be spied:
spyOn(component.route.snapshot.paramMap,"get").and.returnValue("some_id");
Also, you can change the returned value. For example:
fit("should ...", () => {
let mock_id = null;
spyOn(component,"showDialog");
spyOn(component.route.snapshot.paramMap,"get").and.callFake(() => {
return mock_id;
});
component.ngOnInit();
expect(componetn.showDialog).toHaveBeenCalled();
expect(componetn.showDialog).toHaveBeenCalledTimes(1);
mock_id = "some_value";
component.ngOnInit();
expect(...).to..
...
expect(componetn.showDialog).toHaveBeenCalledTimes(1);
mock_id = "another_value";
component.ngOnInit();
expect(...).to..
...
expect(componetn.showDialog).toHaveBeenCalledTimes(1);
});

Single transaction on multiple model function with CodeIgniter

I need to insert into 2 tables if anything goes wrong while inserting in any of the table I want to rollback commited queries.
I wrote queries inside controller
for example:
$this->db->trans_start();
$this->db->insert_batch('market_users_mapping', $marketData);
$this->db->insert_batch('maincategory_users_mapping', $maincategoryData);
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
throw error
}
This works perfectly. But I think it's not good practice to write queries inside controller. So I did this, called model function and I wrote those insert_batch queries in respective model function.
$this->db->trans_start();
$this->maincategory_model->function_name()
$this->market_model->function_name();
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
throw error
`enter code here`
}
but this didnt work as expected
You changed places of queries in those examples regarding names in case it matters. I think that you can't have tied transactions between different methods (your second example). But you can and should set your DB related code to model.
So make those queries in model:
// controller code
$this->db->trans_start();
$this->maincategory_model->first_function($maincategoryData);
$this->market_model->second_function($marketData);
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
throw error
`enter code here`
}
// Maincategory_model code
public function first_function($maincategoryData)
{
return $this->db->insert_batch('maincategory_users_mapping', $maincategoryData);
}
// Market_model code
public function second_function($marketData)
{
return $this->db->insert_batch('market_users_mapping', $marketData);
}
First shift your db related operation in module and then start transaction.
Sample code in module,
First module :
function insert_data_market_maincategory($marketData,$maincategoryData)
{
$status = TRUE;
$this->db->trans_start();
$this->db->insert_batch('market_users_mapping', $marketData);
$this->second_module_name->maincategoryData($maincategoryData)
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
$status = FALSE;
}
return $status;
}
second module :
function maincategoryData($data)
{
$this->db->insert_batch('table_name', $data);
}
and your controller call this function
sample code in controller,
function inser_user_data()
{
$result = $this->module_name->maincategoryData($marketData,$maincategoryData)
if($result == FALSE)
{
throw error
`enter code here`
}
else
{
//data inserted successfully
}
}
I am using CI 3 and I can use single transaction on multiple model in Controller.
I have tried to insert error data to test if rollback or not, the transaction is successfully rollback.
I did not use Tpojka's anwser but my model methods return true or false. Everything seems okay.

NoMatchingExpectationException even with $this->anything()

I'm writing a unit test for a Laravel controller method, but I keep getting NoMatchingExpectationExceptions no matter what I do with the ->with(...). The code under test:
public function destroy($id) {
$foo = FooService::foo($id); //returns a Foo object (an Eloquent model)
$fooCollection = new Collection(array($foo));
$response = FooService::archive($fooCollection);
return Response::json($response);
}
The unit test:
public function testArchiveSingle() {
$foo = Mockery::mock('Foo', array('id' => 1));
$fooCollection = new \Illuminate\Database\Eloquent\Collection();
$fooCollection->add($foo);
FooService::shouldReceive('foo')->once()
->with(1)
->andReturn($foo);
//here's the shouldReceive that's throwing the error:
FooService::shouldReceive('archive')->once()
->with($this->anything())
->andReturn(array('result'=>'here'));
$response = $this->action('DELETE', 'FoosController#destroy',
array('site' => 12345, 'foos' => 1),
array());
$this->assertResponseOk();
$this->assertTrue($response->headers->contains('Content-Type', 'application/json'));
//other tests that are proprietary in nature go here
}
In the ->with() I've tried passing in $fooCollection, $this->instanceOf('Collection'), $this->instanceOf('\Illuminate\Database\Eloquent\Collection') and a few other things. I've also tried changing the $fooCollection definition to just new Collection.
When I run the tests, I get:
Mockery\Exception\NoMatchingExpectationException : No matching handler found for Mockery_1_FooService::archive(Illuminate\Database\Eloquent\Collection). Either the method was unexpected or its arguments matched no expected argument list for this method
When I remove the ->with(...) in the troublesome shouldReceive the test runs fine, but loses value since it wouldn't catch a (theoretical) bug that would accidentally archive too much.
You should use Mockery::any() to match for any argument. PHPUnit's own mock library uses $this->anything(). See the manual.
FooService::shouldReceive('archive')->once()
->with(Mockery::any())
->andReturn(array('result'=>'here'));

Magento Custom Router loading controller but nothing else

I'm trying to get some custom routing going on in Magento using the following code (which I've only slightly modified from here https://stackoverflow.com/a/4158571/1069232):
class Company_Modulename_Controller_Router extends Mage_Core_Controller_Varien_Router_Standard {
public function match(Zend_Controller_Request_Http $request){
$path = explode('/', trim($request->getPathInfo(), '/'));
// If path doesn't match your module requirements
if ($path[1] == 'home.html' || (count($path) > 2 && $path[0] != 'portfolios')) {
return false;
}
// Define initial values for controller initialization
$module = $path[0];
$realModule = 'Company_Modulename';
$controller = 'index';
$action = 'index';
$controllerClassName = $this->_validateControllerClassName(
$realModule,
$controller
);
// If controller was not found
if (!$controllerClassName) {
return false;
}
// Instantiate controller class
$controllerInstance = Mage::getControllerInstance(
$controllerClassName,
$request,
$this->getFront()->getResponse()
);
// If action is not found
if (!$controllerInstance->hasAction($action)) {
return false;
}
// Set request data
$request->setModuleName($module);
$request->setControllerName($controller);
$request->setActionName($action);
$request->setControllerModule($realModule);
// Set your custom request parameter
$request->setParam('url_path', $path[1]);
// dispatch action
$request->setDispatched(true);
$controllerInstance->dispatch($action);
// Indicate that our route was dispatched
return true;
}
}
The result is a page where the template has loaded but with no content. If I comment out the $this->loadLayout() / $this->renderLayout() in my controller I can print to screen. But when I try and load a Template and/or Block it breaks somewhere.
home.html also loads fine (as the method returns false if the path is home.html).
Any assistance would be greatly appreciated.
I was implementing something similar to this and came across the same problem(That makes sense, because I copypasted your code)
before $request->setDispatched(true);
I added $request->setRouteName('brands'); (brands is the frontname of my module).
And It worked.Don't know if It'll work for you, but definetely there was something missing so that magento didn't know what layout to apply, because I could tell that teh controller was being reached.

Resources