PHPUnit - assert method call order on different mocks - php

I would like to test a method that just call a method of my entity object and persists it to the database.
public function setNameAndPersistObject($entityObject) {
$entityObject->setName("John");
$this->entityManager->persist($entityObject);
}
Both $entityObject and $entityManager are mocks, not actual objects.
I would like to test the call order of the called methods, to test that 'persist' gets called after 'setName', so the new name "John" gets saved in the database. How would I do that in PHPUnit?

Well... Not sure if this is the best/most clean way to do this, but recently I needed to check order between methods and this worked for me: you can simply have a variable and use it by reference on those methods. For example:
$name=null;
$mock1 = $this
->getMockBuilder(Entity::class)
->setMethods(['setName'])
->getMock();
$mock1->expects($this->any())
->method('setName')
->willReturnCallback(function($value) use (&$name) {
$name = $value;
});
$mock2 = $this
->getMockBuilder(EntityManager::class)
->setMethods(['persist'])
->getMock();
$mock2->expects($this->any())
->method('persist')
->willReturnCallback(function($object) use (&$name) {
$this->assertNotNull($name);
});
This should do the trick, at least. Notice it will fail if you create more mocks using the same variable to save the name. Another option is to include in the first mock a function to return the value stored in name and call that function in the second mock. This will not be perfect but still a little bit safer.

Related

Is it possible to mock a method chained through an attribute?

I have the following PHP function, for which I am trying to write a PHPUnit test:
public function getDisplayMode()
{
if($this->request->query->get('master_video'))
{
return 'listTranslations';
}
else
{
return 'default';
}
}
The line $this->request->query->get('master_video') is what I am having trouble with.
I have mocked up the request object, so if it were just $this->request->get('master_video'), I could easily use the method method to tell my system what should be returned by get.
But I don't know how to do that when the query property is assumed to be present. (I don't know of any property method on my mock object, for example.)
Is there a way to define what should be returned by the get method here?
Create a mock of query and specify what happens on ->get(). The test would end up looking something like this:
public function testGetDisplayMode() {
$mockQuery = $this->getMockBuilder('Query') // Or whatever the query class is
->setMethods(['get'])
->getMock();
$mockQuery->expects($this->once())
->method('get')
->with('master_video')
->willReturn('foo');
$mockRequest = $this->getMockBuilder('Request') // Or whatever the request is
->getMock();
$mockRequest->query = $mockQuery;
$sut = new Bar($mockRequest) // Or however you instantiate the class with the mock request.
$mode = $sut->getDisplayMode();
// Do your assertions on the returned mode here.
}
As a general rule, I have found when I am doing things like this where I have a mock object returning a mock object that it is a code smell. Your method here doesn't need the $this->request it needs the query object. You should be giving this directly to the object or passing it into this method.
Without knowing more about the class that you are making, I can't give more advice.
If something is hard to write a test for, it is a sign that you have not optimally designed your code.

PHPUnit: mock all functions except one in ZendFramework2 Controller

I need to mock a zf2 controller and keep one real function: "getStopWords".
I tried this answer adapted below:
public function createMockExecpt()
{
// create mock to get names of all functions
$mock = $this->getMockBuilder('Controller\CollectionsController')->disableOriginalConstructor()->getMock();
$reflection = new MyReflectionClass($mock);
$functionsToMock = $reflection->getAllfunctionNamesExcept(["getStopWords"]);
// create mock but don't mock one function
return $this->getMock('Controller\CollectionsController', $functionsToMock);
}
but got an error about redefining a class.
// Cannot redeclare Mock_CollectionsController_d61a5651::__clone()
I think this happens because I need an instance of the controller to find out all the functions it has. However I can't make an instance of the controller in this context, which is why I need a mock. But I can't make more than one mock of a class within the same test, so I'm stuck.
My problem was that I thought you need an instance of the class to get all the methods.
Turns out all you need is the class name!
public function testGetStopWords()
{
// get the class methods the controller has, except getStopWords
$methodsToMock = array_diff(
get_class_methods("Controller\CollectionsController"),
["getStopWords"]
);
// use setMethods to determine which methods to mock
$mockController = $this->getMockBuilder("Controller\CollectionsController")
->setMethods($methodsToMock)
->disableOriginalConstructor()
->getMock();
}

Setting value for inner method in php unit

I can not set value for inner method when I try to test. Here I have written a sample class. I have created mock object for same class but does not effect.
class A
{
public function OneTest()
{
if($this->TwoTest()){
return true;
}
}
public function TwoTest()
{
// return somethings
}
}
I am new at phpunit test writing. if some one expert help me that good for me. I want to test this method. I have tried with:
class ATest extends \PHPUnit_Framework_TestCase
{
public function testOne()
{
$helper = new testOne();
// trying to set TwoTest() method value but does not effect.
$mock = $this->createMock(A::class);
$mock->method("TwoTest")
->willReturn(true);
$this->assertTrue($helper->OneTest();
}
}
Actually I do not know how to use my mocking method result. My actual implementation in twoTest method contains some db related code. I do not want to run db code in testing time.
You are pretty close with your mock. What you want to do is called partial mocking. This is done by creating a mock of A with only TwoTest being mocked, i.e. it will now always return true and never actually call the real code inside the original implementation in A, whereas all other methods still act as before. Therefore calling $mock->OneTest() should return the expected result. Since you make both calls on the (partially) mocked instance, you won't need $helper. So your test would probably look something like this:
public function testOneWhenTwoTestReturnsTrue()
{
$mock = $this->getMockBuilder(A::class)
->setMethods(["TwoTest"])
->getMock();
$mock->method("TwoTest")
->willReturn(true);
$this->assertTrue($mock->OneTest();
}
Notice that I use getMockBuilder() instead of just createMock() and setMethods() is what we need for your test. We only overwrite the one method we want to mock, the rest will behave as defined in the original class. To quote the docs:
setMethods(array $methods) can be called on the Mock Builder object to specify the methods that are to be replaced with a configurable test double. The behavior of the other methods is not changed. If you call setMethods(null), then no methods will be replaced.

How to mock based on method parameter

One of my stubbed mock objects has a method which will be called twice in a method that I want to test. How can I write the tests so that both branches in my test method will be coveraged? Code sample (The stubbed object is the cache):
public function myMethodToTest($param, $default) {
if ($this->cache->has($param)) {
return 'A';
} else if ($this->cache->has($default)) {
return 'B';
}
}
Lifted from the phpunit documentation, we can start with this example:
public function testObserversAreUpdated()
{
// Create a mock for the Observer class,
// only mock the update() method.
$observer = $this->getMockBuilder('Observer')
->setMethods(array('update'))
->getMock();
// Set up the expectation for the update() method
// to be called only once and with the string 'something'
// as its parameter.
$observer->expects($this->once())
->method('update')
->with($this->equalTo('something'));
// Create a Subject object and attach the mocked
// Observer object to it.
$subject = new Subject('My subject');
$subject->attach($observer);
// Call the doSomething() method on the $subject object
// which we expect to call the mocked Observer object's
// update() method with the string 'something'.
$subject->doSomething();
}
Pay attention to the with() method call. You can use that to specify an expectation that the method will be called with a specific parameter value and dictate what to return when it happens. In your case, you should be able to do something like this:
$cacheStub->method('has')
->with($this->equalTo('testParam1Value'))
->willReturn(true);
Do that in one test, and you'll test one branch of your code. In a separate test, you can set up the mock differently:
$cacheStub->method('has')
->with($this->equalTo('testParam2Value'))
->willReturn(true);
This test will test your other branch. You can combine them into a single test if you like, you may have to recreate the mock in between assertions.
See also this short article that has some alternative ways of calling with() other than $this->equalTo()

Stuck on writing PHPUnit getMock unit test

I'm admittedly very rusty with unit testing.
I have the following function in my Registration.php:
protected function _showModal($request) {
if (Store::isUserSupported()) {
return false;
}
return true;
}
I started writing the following test but I know I'm missing some key items:
public function testUserSupported() {
$mockStore = $this->getMockClass('Store', array('isUserSupported'));
$mockStore::staticExpects($this->once())
->method('isUserSupported')
->will($this->returnValue(true));
$mockStore::isUserSupported();
$plugin = $this->getMockBuilder('Registration')
->setMethods(array('_showModal'))
->disableOriginalConstructor()
->getMock();
$plugin = $this->getPublicClass($plugin);
$plugin->expects($this->once())
->method('_showTermsModal')
->will($this->returnValue(true));
}
The $mockStore part is getting called, but not sure how to tie it into my $plugin call.. I want to write a test to mock Store::isUserSupported() returning true within the showModal function. Any words of advice?
You should avoid static calls in your code, because they make your code coupled together and hard to change and hard to maintain. It also makes it harder to tests, as you are suffering here.
Instead of static calls, pass the collaborators needed to work. In your code example, the class you want to test would receive as parameter the Store class, instead of calling it statically.
By doing this change, you can now create a mock for the method isUserSupported() of the Store class, and pass the mock to the object under test, that will efectively use the mocked object.
$mockStore = $this->getMock('Store', array('isUserSupported'));
$mockStore->expects($this->once())
->method('isUserSupported')
->will($this->returnValue(true));
$object_under_test = new Registration( $mockStore );
$object_under_test->showModal($request); // Only in case showModal is public, otherwise call public method
If the Store class is only needed for that method, and you don't want to pass it as dependency in the constructor, you can pass it on the method itself.
$mockStore = $this->getMock('Store', array('isUserSupported'));
$mockStore->expects($this->once())
->method('isUserSupported')
->will($this->returnValue(true));
$object_under_test = new Registration();
$object_under_test->showModal($mockStore, $request); // Only in case showModal is public, otherwise call public method
Also, you shouldn't test protected/private methods of your classes, since they are low level implementation details that your tests don't need to know about. You should only make calls to public methods in your tests. Otherwise, the tests become very coupled with the real implementation, and if you refactor your code, you will most likely have to change the tests too.

Categories