NoMatchingExpectationException even with $this->anything() - laravel-4

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'));

Related

gettin request/query parameters with less code

Is this:
$paginate = $request->get('paginate');
Equivalent to this, for getting a query param if it is present or assign to the associated variable "null" it it is not present:
if ($request->has('paginate')) {
$paginate = $request->get('paginate');
} else {
$paginate=null;
}
According to get() method documentation:
This method belongs to Symfony HttpFoundation and is not usually needed when using Laravel.
Alternatively you can use filled and $request->paginate
So it checks if the request has the "item"and it has value.
$paginate = null;
if ($request->filled('paginate')){
$paginate = $request->paginate;
}

Model events not fire in laravel testing

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.

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);
});

Magento2: Argument 1 [...] must be an instance of Magento\Framework\App\Helper\Context

First of all, I'm quite new to Magento 2, but I've used Magento 1.x for some time.
I've read a lot about how to solve DI-related problems, but I'm stuck on this one:
Exception #0 (Exception): Recoverable Error: Argument 1 passed to Cefar\AO\Helper\Ao::__construct() must be an instance of Magento\Framework\App\Helper\Context, instance of Magento\Framework\ObjectManager\ObjectManager given, called in .../vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php on line 93 and defined in .../Cefar/AO/Helper/Ao.php on line 11
Many other answers have suggested deleting the var/di and var/generation folders, sometimes var/cache also. While this solves the problem, it occurs again once bin/magento setup:di:compile is run, which means the code cannot be used in a production environment.
I've checked that the Ao class does not instantiate any objects. It also doesn't try to re-make any objects that could be provided by the context given. Here's the code:
namespace Cefar\AO\Helper;
class Ao extends \Magento\Framework\App\Helper\AbstractHelper
{
const DEFAULT_GRID_COLS = 4;
protected $_session;
public function __construct(
\Magento\Framework\App\Helper\Context $context,
\Magento\Customer\Model\Session $session
)
{
parent::__construct($context);
$this->_session = $session;
}
public function getConfig($path)
{
return $this->scopeConfig->getValue($path);
}
public function isActive($url = null, $print = true) {
$active = ($url && strstr($_SERVER['REQUEST_URI'], $url) !== false);
if ($active && $print) {
echo "active";
} else {
return $active;
}
}
public function isLoggedIn()
{
return $this->_session->isLoggedIn();
}
public function limitWords($text = '', $limit = 10, $showDots = true)
{
$words = explode(' ', $text);
$limited = array_slice($words, 0, $limit);
$newText = implode(' ', $limited);
if (count($words) > $limit && $showDots) {
$newText .= '...';
}
return $newText;
}
public function getCurrentGrid()
{
return ($this->_getRequest()->getParam('grid'))
? $this->_getRequest()->getParam('grid')
: self::DEFAULT_GRID_COLS;
}
}
There's nothing particularly special here. I'm confused as to how this is even happening; every other defined class in the extension is getting its DI parameters correctly. Why is the ObjectManager apparatus providing an unwanted argument? The relevant call is given in the error report as:
.../vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php(93): Cefar\AO\Helper\Ao->__construct(Object(Magento\Framework\ObjectManager\ObjectManager))
So it isn't even providing two arguments!
I've also read about providing type hints in a di.xml, but it doesn't seem to be relevant here as both types are part of the Magento libraries? I note that there is an entry for Magento\Framework\App\Helper\Context but not one for Magento\Customer\Model\Session... but that there are framework classes that use ID to import Magento\Customer\Model\Session already which work.
Long story short, this was because of a typo.
Sometimes when the helper was being included, it was being referred to as Cefar\AO\Helper\Ao, and other times, Cefar\AO\Helper\AO. Essentially, the ObjectManager was resolving both of these references to the same class, but it only had type hints for one of the names so it didn't know what to provide to the incorrect one.
A little help would have been nice, Magento! Maybe an error report that the requested class wasn't found? Still, at least this is finally over with.

Dynamically switching the error message on validation?

With the new validator object - is it possible to replace the validation error inside the validation rule triggered? to not only return the static error message but maybe some dynamically genereted one?
public function validateLength($data) {
...
$length = mb_strlen($data['name']);
$this->validator()->getField('name')->setRule('validateLength', array('message' => $length . 'chars'));
...
}
does not work, of course (too late I guess)
I want to actually return the lenght of the string (you used 111 chars from 100 allowed) for example - but for this I would need to be able to replace the message from inside the (custom) validation method
$this->validate['name']['validateLength']['message'] = $length . 'chars';
also never worked so far. it would always return the previous (static) error message from the $validate array
public function customValidator($data) {
....
if ($validates) {
return true;
} else {
return 'my new error message';
}
}
The following snippet should do the trick:
public function validateLength($data) {
...
$length = mb_strlen($data['name']);
$this->validator()->getField('name')->getRule('validateLength')->message = $length . 'chars';
...
}

Resources