How do I access overloaded properties on a PHPUnit mock object? - unit-testing

When mocking an object with PHPUnit, how do I access properties of the object which are normally accessed via overloading, i.e. via __get()?
For example, in the code below I am testing a Post object. Each Post has an author, which of type Role. Each Role has a Signature property.
$author = $this->getMockBuilder('App_Model_Domain_Role')
->disableOriginalConstructor()
->getMock();
$author->expects($this->any())
->method('__get')
->will($this->returnValue('authorname'));
As you can see I mock the Role object, then configure it to return a string ('authorname') when __get() is called. The Post object which I am testing refers to $this->author->signature. I am expecting it to return 'authorname', but instead the test errors out saying that $signature is an undefined property.
I tried configuring the mock as above but without the method() call (thinking that the expects() and will() calls would then apply to all the mock's methods) but still no success.
Any ideas?
Also, if you know of a good tutorial on PHPUnit mocks I'd be keen to see it - the manual seems to assume prior testing knowledge in this particular area.

I tried the same code as you and it is working for me. My PHPUnit version is 3.7.12. Which version are you using? If you are running a older version you could try upgrading.
public function testMockingMagicGetter()
{
$myMock = $this->getMockBuilder('MyClass')
->disableOriginalConstructor()
->getMock();
$myMock
->expects($this->any())
->method('__get')
->will($this->returnValue('authorname'));
var_dump($myMock->signature);
var_dump($myMock->dummy);
var_dump($myMock->someprop);
}
Results in:
string(10) "authorname"
string(10) "authorname"
string(10) "authorname"

Related

Apache shiro unit test with checkPermission passes regardless of stubbed permission

Running spring boot 1.5.12 with Shiro starter 1.4.0
Trying to write up some unit tests to test a static class which checks permissions using the checkPermission of the Subject interface.
I'm mocking the shiro subject and stubbing the isPermitted method to return false for specific permission string... but for some reason, it passes when Subject.checkPermission is executed.
Subject subjectUnderTest = mock(Subject.class);
when(subjectUnderTest.isAuthenticated()).thenReturn(true);
when(subjectUnderTest.isPermitted(eq("review:edit:regional"))).thenReturn(false);
setSubject(subjectUnderTest);
subjectUnderTest.checkPermission("review:edit:regional");
I'm fairly new to Mockito but in this case was expecting AuthorizationException to be thrown by checkPermission given the isPermitted stub returning false.
If i change the implementation to use isPermitted, then test runs as expected..but current implementation is using checkPermission...
The method checkPermission() of the Subject interface will not call isPermitted() on itself but on the abstract AuthorizingRealm. The callchain for checkPermission() is like the following:
Subject.checkPermissions()->
DelegatingSubject.checkPermissions()->
Authoriser.checkPermission()->
AuthorizingRealm.checkPermission()->
AuthorizingRealm.isPermitted()
Therefore, mocking isPermitted() on the Subject will have no effect since checkPermission() will never call it on this object. To achieve the expected behavior, you have to mock the method on the AuthorizingRealm or alternatively on the AuthorizingSecurityManager incase calls to your SecurityManager are available under test.

Mocks vs Stubs in PHPUnit

I know stubs verify state and the mocks verify behavior.
How can I make a mock in PHPUnit to verify the behavior of the methods? PHPUnit does not have verification methods (verify()), And I do not know how to make a mock in PHPUnit.
In the documentation, to create a stub is well explained:
// Create a stub for the SomeClass class.
$stub = $this->createMock(SomeClass::class);
// Configure the stub.
$stub
->method('doSomething')
->willReturn('foo');
// Calling $stub->doSomething() will now return 'foo'.
$this->assertEquals('foo', $stub->doSomething());
But in this case, I am verifying status, saying that return an answer.
How would be the example to create a mock and verify behavior?
PHPUnit used to support two ways of creating test doubles out of the box. Next to the legacy PHPUnit mocking framework we could choose prophecy as well.
Prophecy support was removed in PHPUnit 9, but it can be added back by installing phpspec/prophecy-phpunit.
PHPUnit Mocking Framework
The createMock method is used to create three mostly known test doubles. It's how you configure the object makes it a dummy, a stub, or a mock.
You can also create test stubs with the mock builder (getMockBuilder returns the mock builder). It's just another way of doing the same thing that lets you to tweak some additional mock options with a fluent interface (see the documentation for more).
Dummy
Dummy is passed around, but never actually called, or if it's called it responds with a default answer (mostly null). It mainly exists to satisfy a list of arguments.
$dummy = $this->createMock(SomeClass::class);
// SUT - System Under Test
$sut->action($dummy);
Stub
Stubs are used with query like methods - methods that return things, but it's not important if they're actually called.
$stub = $this->createMock(SomeClass::class);
$stub->method('getSomething')
->willReturn('foo');
$sut->action($stub);
Mock
Mocks are used with command like methods - it's important that they're called, and we don't care much about their return value (command methods don't usually return any value).
$mock = $this->createMock(SomeClass::class);
$mock->expects($this->once())
->method('doSomething')
->with('bar');
$sut->action($mock);
Expectations will be verified automatically after your test method finished executing. In the example above, the test will fail if the method doSomething wasn't called on SomeClass, or it was called with arguments different to the ones you configured.
Spy
Not supported.
Prophecy
Prophecy is now supported by PHPUnit out of the box, so you can use it as an alternative to the legacy mocking framework. Again, it's the way you configure the object makes it becomes a specific type of a test double.
Dummy
$dummy = $this->prophesize(SomeClass::class);
$sut->action($dummy->reveal());
Stub
$stub = $this->prophesize(SomeClass::class);
$stub->getSomething()->willReturn('foo');
$sut->action($stub->reveal());
Mock
$mock = $this->prophesize(SomeClass::class);
$mock->doSomething('bar')->shouldBeCalled();
$sut->action($mock->reveal());
Spy
$spy = $this->prophesize(SomeClass::class);
// execute the action on system under test
$sut->action($spy->reveal());
// verify expectations after
$spy->doSomething('bar')->shouldHaveBeenCalled();
Dummies
First, look at dummies. A dummy object is both what I look like if you ask me to remember where I left the car keys... and also the object you get if you add an argument with a type-hint in phpspec to get a test double... then do absolutely nothing with it. So if we get a test double and add no behavior and make no assertions on its methods, it's called a "dummy object".
Oh, and inside of their documentation, you'll see things like $prophecy->reveal(). That's a detail that we don't need to worry about because phpspec takes care of that for us. Score!
Stubs
As soon as you start controlling even one return value of even one method... boom! This object is suddenly known as a stub. From the docs: "a stub is an object double" - all of these things are known as test doubles, or object doubles - that when put in a specific environment, behaves in a specific way. That's a fancy way of saying: as soon as we add one of these willReturn() things, it becomes a stub.
And actually, most of the documentation is spent talking about stubs and the different ways to control exactly how it behaves, including the Argument wildcarding that we saw earlier.
Mocks
If you keep reading down, the next thing you'll find are "mocks". An object becomes a mock when you call shouldBeCalled(). So, if you want to add an assertion that a method is called a certain number of times and you want to put that assertion before the actual code - using shouldBeCalledTimes() or shouldBeCalled() - congratulations! Your object is now known as a mock.
Spies
And finally, at the bottom, we have spies. A spy is the exact same thing as a mock, except it's when you add the expectation after the code - like with shouldHaveBeenCalledTimes().
https://symfonycasts.com/screencast/phpspec/doubles-dummies-mocks-spies

Error when trying to mock CakePHP 3 component

I am trying to mock a CakePHP 3 component which checks if the user is allowed to view the page or not.
I tried this:
$authComponent = $this->getMockBuilder(App\Controller\Component\AuthorizationComponent::class)
->getMock();
$authComponent
->method('isAuthorized')
->willReturn($this->returnValue(true));
However, when running the test, it says:
Trying to configure method "isAuthorized" which cannot be configured because it does not exist, has not been specified, is final, or is static
Most probably I wrongly created the mock. Can anyone tell me how to do it correctly?
Specify mocked methods with setMethods() before getMock():
$authComponent = $this
->getMockBuilder(App\Controller\Component\AuthorizationComponent::class)
->setMethods(['isAuthorized'])
->getMock();

Symfony2: Mocked service is set in the container but not used by the controller (it still uses the original service)

I'm writing the functional tests for a controller.
It uses a class to import some data from third party websites and to do this I wrote a class that I use into Symfony setting it a service.
Now, in my functional tests, I want to substitute this service with a mocked one, set it in the container and use it in my functional tests.
So my code is the following:
// Mock the ImportDataManager and substitute it in the services container
$mockDataImportManager = $this->getMockBuilder('\AppBundle\Manager\DataImportManager')->disableOriginalConstructor()->getMock();
$client->getContainer()->set('shq.manager.DataImport', $mockDataImportManager);
$client->submit($form);
$crawler = $client->followRedirect();
As I know that between each request the client reboots the kernel and I have to set again the mocked class, I set the mock immediately before the calling to $client->submit.
But this approach seems not working for me and the controller still continue to use the real version of the service instead of the mocked one.
How can I use the mocked class to avoid to call the remote website during my functional test?
If I dump the set mocked service, I can see it is correctly set:
dump($client->getContainer()->get('shq.manager.DataImport'));die;
returns
.SetUpControllerTest.php on line 145:
Mock_DataImportManager_d2bab1e7 {#4807
-__phpunit_invocationMocker: null
-__phpunit_originalObject: null
-em: null
-remotes: null
-tokenGenerator: null
-passwordEncoder: null
-userManager: null
}
But it is not used during the $form->submit($form) call and, instead, is used the original service.
UPDATE
Continuing searching for a solution, I landed on this GitHub page from the Symfony project, where a user asks for a solution to my same problem.
The second call doesn't use the mocked/substituted version of his class, but, instead, the original one.
Is this the correct behavior? So, is it true that I cannot modify the service container on a second call to the client?
Yet, I don't understand why the service is not substituted in the container and I haven't a real solution to that problem.
Anyway I found some sort of workaround, in reality more correct as solution (also if it remains unclear why the service is not substituted and this is a curiosity I'd like to solve - maybe because the $client->submit() method uses the POST method?).
My workaround is a simple test double.
I create a new class in AppBundle/Tests/TestDouble and called it DataImportManagerTestDouble.php.
It contains the unique method used by the controller:
namespace AppBundle\Tests\TestDouble;
use AppBundle\Entity\User;
class DataImportManagerTestDouble
{
public function importData(User $user)
{
return true;
}
}
Then, I instantiate it in the config_test.yml (app/config/config_test.yml) file in the following way:
services:
shq.manager.DataImport:
class: AppBundle\Tests\TestDouble\DataImportManagerTestDouble
This way, during the tests, and only during the tests, the class loaded as service is the TestDouble and not the original one.
So the test pass and I'm (relatively) happy. For the moment, at least.

Mocking a repository [duplicate]

This question already exists:
How to mock a repository
Closed 8 years ago.
I am trying to mock a repository in symfony 2, so that I can work on a test data. The problem is that the entity manager returns empty entity object, with all values as NULL, as if it's from the real database. What's wrong here? This is the code I've written so far:
$user = $this->getMock('\Acme\AdminBundle\Entity\Users');
$user->expects($this->once())
->method('getId')
->will($this->returnValue(1));
$user->expects($this->once())
->method('getEmail')
->will($this->returnValue('aaa'));
$userRepository = $this->getMockBuilder('\Doctrine\ORM\EntityRepository')
->disableOriginalConstructor()
->getMock();
$userRepository->expects($this->once())
->method('find')
->will($this->returnValue($user));
$entityManager = $this->getMockBuilder('\Doctrine\Common\Persistence\ObjectManager')
->disableOriginalConstructor()
->getMock();
$entityManager->expects($this->once())
->method('getRepository')
->will($this->returnValue($userRepository));
var_dump($entityManager->getRepository('Acme:Users')->find(1)); die();
When you Mock an object like that, using var_dump won't work. Read this tutorial for a full explanation of mocking. Another good explanation is the PHPUnit documentation itself. Here's the relevant information to note:
By default, all methods of the original class are replaced with a dummy implementation that just returns null (without calling the original method). Please note that final, private and static methods cannot be stubbed or mocked. They are ignored by PHPUnit's test double functionality and retain their original behavior.
Think of your mocked entity as an entity that behaves like your User, except every variable and every function is null. That is why you had to stub individual methods to return the values that you wanted - however, they only return those values when you call those functions directly. So, everything is absolutely correct in your Mocking. If you do:
var_dump($theUser->getId());
var_dump($theUser->getEmail());
You will get the following result:
int(1)
string(3) "aaa"
This is correct - it is exactly what you Mocked. So if you wanted to add an assertion you could do this (after removing the var_dump calls):
$this->assertSame(1, $theUser->getId());
$this->assertSame("aaa", $theUser->getEmail());
Note that those 2 tests wouldn't be very useful on their own because you'd essentially just be testing that PHPUnit itself works. The point of your Mocks would be in combination with some other methods that rely on a user being fetched from the database and having a particular id or email value.