Mocking SecurityContext in Symfony2 using PHPUnit - php

These are my mocks:
$this->user->method('getCustomerId')
->willReturn($this->customerId);
$this->userToken->method('getUser')
->willReturn($this->user);
$this->securityContext->method('getToken')
->willReturn($this->userToken);
$this->securityContext->expects($this->once())
->method('isGranted')
->will($this->returnValue(true));
And this is the code of the class I am testing:
$token = $securityContext->getToken();
$isFullyAuthenticated = $securityContext->isGranted('IS_AUTHENTICATED_FULLY');
It throws the error:
Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException: The security context contains no authentication token. One possible reason may be that there is no firewall configured for this URL.
I have no idea what to do at this point, I though mocks intercepted the call to methods and returned whatever I wanted. But in this case is seems the isGranted methods is not being mock

I figured out that the problem is that the isGranted method is final, so it's imposible to mock, the workaround the problem was to mock all the attributes of SecurityContext so that when the method is call it returns my desired output, instead of intercepting the call with a mock method.

Try using willReturn instead of will and add the with
So try this:
$this->securityContext->expects($this->once())
->method('isGranted')
->with('IS_AUTHENTICATED_FULLY')
->willReturn(true);
Instead of this:
$this->securityContext->expects($this->once())
->method('isGranted')
->will($this->returnValue(true));
Hope this help

Related

phpunit mock - method does not exist

I recently updated PHPunit from 5.3 to 5.5 in an IntegrationTestCase of an app that is CakePhp 3.x based. and I don't understand how to update my mock generation scripts.
Originally I created my mock like this:
$stub = $this->getMock('SomeClass', array('execute'));
$stub->method('execute')
->will($this->returnValue($this->returnUrl));
After the change to PHPUnit 5.5 this got me the following warning:
PHPUnit_Framework_TestCase::getMock() is deprecated,
use PHPUnit_Framework_TestCase::createMock()
or PHPUnit_Framework_TestCase::getMockBuilder() instead
In order to fix this warning I changed the mock-generation to:
$stub = $this->getMockBuilder('SomeClass', array('execute'))->getMock();
$stub->method('execute')
->will($this->returnValue($this->returnUrl));```
Now I get the following error message when running the test:
exception 'PHPUnit_Framework_MockObject_RuntimeException'
with message 'Trying to configure method "execute" which cannot be
configured because it does not exist, has not been specified,
is final, or is static'
Anybody know, how to avoid this error? Thank you.
PHPUnit_Framework_TestCase::getMockBuilder() only takes one (1) argument, the class name. The methods to mock are ment to be defined via the returned mock builder objects setMethods() method.
$stub = $this
->getMockBuilder('SomeClass')
->setMethods(['execute'])
->getMock();
See also
PHPUnit Manual > Test Doubles > Mock Objects
I will leave this as an answer to myself, when i reach this problem again:
The mocked method may not be private.
First of all, it's just
$stub = $this->getMockBuilder('SomeClass')->getMock();
Second, error states that method execute does exist in your class SomeClass.
So, check if it really exists and it's public and not final.
If everything's good, check a full classname, if it real and specified with correct namespace.
To avoid stupid errors with classname, it's better to use this syntax:
$stub = $this->getMockBuilder(SomeClass::class)->getMock();
In this case, if SomeClass doesn't exist or namespace is missed, you will get a clear error about it.
Addition to upper messages: split mock method declarations
Instead of this:
$mock
->method('persist')
->with($this->isInstanceOf(Bucket::class))
->willReturnSelf()
->method('save')
->willReturnSelf()
;
Use this:
$mock
->method('persist')
->willReturnSelf()
;
$mock
->method('save')
->willReturnSelf()
;
Perhaps , the method does not exist in the class that you mock .

Inject Laravel Input property into Mockery session object

I'm working on an existing Laravel app and trying to start building out a test suite for it. I'm using the Mockery library to mock the dependencies of the class I'm testing but I've hit a roadblock I can't seem to get around.
$leadRepositoryInterface = m::mock('CRM\Storage\Lead\LeadRepositoryInterface');
$response = m::mock('ColorJar\ApiResponse\Response');
$object = new LeadsController($leadRepositoryInterface, $response);
I get an error when I run the preceding code because the parent class of LeadsController checks Input::all()[__currentUser] and crashes because Input::all() returns nothing.
I need to set the Laravel Input facade variable(I'm sure I'm misusing terminology there, sorry) to any user in my system but I don't see anywhere in the Mockery docs on how to set a property of a mocked object, I only see how to set expectations and return values. How would I do this? Will Laravel allow me to set the return of Input::all()?
You can't mock the response from the Input facade but you can mock the underlying class which is Request. You would then do something like this:
Request::shouldReceive('input->all')->andReturn('someValue');

Mocking Laravel Model::increment() with Mockery

I have a line of code in a Laravel 5 event handler which looks like this:
$this->event->batch->increment('attempted_jobs');
$this->event is the event which calls the handler and $this->event->batch contains my Batch model. All this does in increment the attempted_jobs column within my database, so it's fairly basic stuff.
I would like to be able to test this event handler, I'm using Codeception and Mockery. My mock for $this->event->batch looks like this:
$batch = m::mock('MyVendor\MyApp\Batch');
$batch->shouldReceive('increment')->once()->with('attempted_jobs');
This however causes issues - increment() is a protected method of Model and therefore cannot be mocked. Here's the exact error:
InvalidArgumentException: increment() cannot be mocked as it a protected method and mocking protected methods is not allowed for this mock
It appears to be implemented using the __call() PHP magic method, so how to I mock this? I've attempted creating a __call() mock, but this churns out tonnes of errors related to the increment() method not being implemented.
The issue was because, as stated, increment() is a protected method of Illuminate\Database\Eloquent\Model(). The way to get around this is to mock the __call() method directly, like so:
$batch = m::mock('MyVendor\MyApp\Batch');
$batch->shouldReceive('__call')->with('increment')->once();
(I'm not sure why this didn't work when I first tried it though)

Mockery "shouldReceive" yet method doesn't exist

I'm trying to understand Tests and Mockery a bit more with Laravel. I have a repository pattern setup, which my controller users. I want to test my basic getAllUsers()method:
public function test_get_all_users_method()
{
$repo = Mockery::mock('Acme\Repositories\User\UserRepository');
$repo->shouldReceive('all')->once()->andReturn('foo');
$controller = new Acme\Controllers\Api\UserController($repo);
$response = $controller->getComponents();
$this->assertEquals('foo', $response);
}
As I understand it, I'm mocking my UserRepository, and I expect my UserRepository to have it's all() method hit. This returns some dummy data and I expect to see this in my response output.
So that works fine. The all() method exists in my Eloquent implementation of the repository. However, if I remove the all() method, the test still passes... Why would it? Surely the test should fail.
If this is normal, I'm struggling to understand why I'd test my controller like this, since I could pass any old method name into it even if it exists or not.
Cheers
That's how mockery operates by default, I like it that way because it allows me to develop by wishful thinking, i.e. I wish my UserRepository interface had an all method.
You can tell mockery to disallow it though, it's a bit ugly, but you can put this in your test bootstrap file:
\Mockery::getConfiguration()->allowMockingNonExistentMethods(false);
You could also set this up to control it with an environment variable or something, so you allow mocking non-existent methods during normal use, but prevent it on your continuous integration run etc.

How to get Mockery to work with a demeter chain

I have a Slim application that has some Middleware.
It performs authentication for the route, and retrieves the route like so:
$route = $this->getApplication()->router()->getCurrentRoute();
I'm now testing it and am attempting to use Mockery to mock the result of the chained call, so I can effectively specify the route.
$mock = M::mock('\Api\SessionMiddleware[getApplication]');
$mock->shouldReceive('router->getCurrentRoute')->andReturn('myRoute');
This doesn't work. It tries to call: require('lib/demeter/router.php') and fails as this doesn't exist.
I've also tried:
$mock = M::mock('\Api\SessionMiddleware')->shouldDeferMissing();
$mock->shouldReceive('getApplication->router->getCurrentRoute')->andReturn('myRoute');
This doesn't work either, failing with:
Failed opening required 'lib/demeter/getApplication.php'
What am I missing?
Even though there's an accepted answer, I wanted to provide future users with another solution.
There should be no need to mock the router if it's only used as an intermediate step of the demeter chain. Try this:
$mock = M::mock('\Api\SessionMiddleware');
$mock->shouldReceive('getApplication->router->getCurrentRoute')->andReturn('myRoute');
The key is removing the call to shouldDeferMissing(), which in this case seems to interfere with the demeter chain.
This way, a "pure" mock, which doesn't forward anything to the real implementation of SessionMiddleware, is returned. It should be able to reply with 'myRoute' when $mock->getApplication()->getRouter()->getCurrentRoute() is invoked.
You also need to mock the router. and let the router() method return the router mock in turn.
$mock = M::mock('\Api\SessionMiddleware[getApplication]');
$routerMock = M::mock('My\Router');
$routerMock->shouldReceive('getCurrentRoute')->andReturn('myRoute');
$mock->shouldReceive('router')->andReturn($routerMock);

Categories