PHPUnit Mock multiple different methods - php

I'm new to Unit test and I'm experimenting with PHPUnit framework.
I have a function that calls two others function:
class Dummy{
public function dummyFunction(){
$this->anotherDummyFunction();
.....
$this->yetAnotherDummyFunction();
}
public function anotherDummyFunction(){
.....
}
public function yetAnotherDummyFunction(){
.....
}
}
I want test that the two functions be called when I invoke dummyFunction().
Here the test class:
class TestDummyClass {
public function testDummyFunction(){
$dummyClassMock = $this->getMockBuilder('Dummy')
->setMethods( array( 'anotherDummyFunction','yetAnotherDummyFunction' ) )
->getMock();
$dummyClassMock->expects($this->once())
->method( 'anotherDummyFunction' );
$dummyClassMock->expects($this->once())
->method( 'yetAnotherDummyFunction' );
$dummyClassMock->dummyFunction();
}
}
Now I've notice that if I mock the Dummy class in this way, the result is
Expectation failed for method name is equal to anotherDummyFunction
when invoked 1 time(s). Method was expected to be called 1 times,
actually called 0 times.
But If I set the Mock Object in this way
$dummyClassMock = $this->getMockBuilder('Dummy')
->setMethods( array( 'anotherDummyFunction' ) )
->getMock();
the test pass. Lastly, if I set the mock object with setMethods(null), the test fails again
It seems that I could pass an array with only one element, the method that I want check if is called. But I've seen here: https://jtreminio.com/2013/03/unit-testing-tutorial-part-5-mock-methods-and-overriding-constructors/ that setMethods is used for inject the return value of the method passed, so this wouldn't have an effect on the call itself, unless I put the dummyFunction in setMethods (In this case the function will return null without call the other two methods, and in this way yes, the test have to fail)
I've made something wrong? I've seen pieces of code where are more than one methods in setMethods()... Why the test fails if I put tho methods in setMethods?
Thanks

You are using $this->once(), which means that the test can only pass if the method is called exactly once – in every test case where the mock is used!
What you probably want is $this->any() which does not require the method to be called every time.

Related

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.

Mock a method within a model method in CakePHP

I am running CakePHP 2.8.X, and am trying to write a unit test for a Model function.
Let's call the model Item, and I'm trying to test its getStatus method.
However, that model makes a call to its find within the getStatus method.
So something like this:
class Item extends Model
{
public function getStatus($id) {
// Calls our `$this->Item-find` method
$item = $this->find('first', [
'fields' => ['status'],
'conditions' => ['Item.id' => $id]
]);
$status = $item['status'];
$new_status = null;
// Some logic below sets `$new_status` based on `$status`
// ...
return $new_status;
}
}
The logic to set "$new_status" is a bit complex, which is why I want to write some tests for it.
However, I'm not entirely sure how to override the find call within Item::getStatus.
Normally when I want to mock a Model's function, I use $this->getMock coupled with method('find')->will($this->returnValue($val_here)), but I don't want to completely mock my Item since I want to test its actual getStatus function.
That is, in my test function, I'm going to be calling:
// This doesn't work since `$this->Item->getStatus` calls out to
// `$this->Item->find`, which my test suite doesn't know how to compute.
$returned_status = $this->Item->getStatus($id);
$this->assertEquals($expected_status, $returned_status);
So how do I communicate to my real Item model within my test that it should override its internal call to its find method?
I knew this had to be an issue others have faced, and it turns out PHPUnit has a very easy way to address this!
This tutorial essentially gave me the answer.
I do need to create a mock, but by only passing in 'find' as the methods I'd like to mock, PHPUnit helpfully leaves all other methods in my Model alone and does not override them.
The relevant part from the above tutorial is:
Passing an array of method names to your getMock second argument produces a mock object where the methods you have identified
Are all stubs,
All return null by default,
Are easily overridable
Whereas methods you did not identify
Are all mocks,
Run the actual code contained within the method when called (emphasis mine),
Do not allow you to override the return value
Meaning, I can take that mocked model, and call my getStatus method directly from it. That method will run its real code, and when it gets to find(), it'll just return whatever I passed into $this->returnValue.
I use a dataProvider to pass in what I want the find method to return, as well as the result to test against in my assertEquals call.
So my test function looks something like:
/**
* #dataProvider provideGetItemStatus
*/
public function testGetItemStatus($item, $status_to_test) {
// Only mock the `find` method, leave all other methods as is
$item_model = $this->getMock('Item', ['find']);
// Override our `find` method (should only be called once)
$item_model
->expects($this->once())
->method('find')
->will($this->returnValue($item));
// Call `getStatus` from our mocked model.
//
// The key part here is I am only mocking the `find` method,
// so when I call `$item_model->getStatus` it is actually
// going to run the real `getStatus` code. The only method
// that will return an overridden value is `find`.
//
// NOTE: the param for `getStatus` doesn't matter since I only use it in my `find` call, which I'm overriding
$result = $item_model->getStatus('dummy_id');
$this->assertEquals($status_to_test, $result);
}
public function provideGetItemStatus() {
return [
[
// $item
['Item' => ['id' = 1, 'status' => 1, /* etc. */]],
// status_to_test
1
],
// etc...
];
}
one way to mock find could be to use a test specific subclass.
You could create a TestItem that extends item and overrides find so it doesn't perform a db call.
Another way could be to encapsulate the new_status logic and unittests it independent of the model

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()

phpunit with mockery - how to test a mocked object is constructed with proper arguments

So I'm writing tests for a php api consumer library. In one of the main libraries main functions I have:
public function __call($name, $args) {
return new Schema($name, $this);
}
In my test I'm using mockery and doing something like:
$schemaMock = m::mock('overload:Mynamespace\Schema');
this is properly overloading my Schema class with a mock. So later when I do:
$myclass->movies()
It should call the __call method, thus calling the mocked Schema class. This all seems good so far, but I would like to assert that the $schemaMock is being constructed with the name of the function, in this case movies as well as the instance of the class being passed in. What I've tried is:
$schemaMock->shouldReceive('__construct')->with('movies');
However my tests pass regardless of what the "with" function argument states. IE I can change movies to foobar and tests still pass. I'm sure I'm missing something simple about how to run these assertions. Thanks for any help!
Short and final answer: you can't.
A mock is defined by cobbling together code for a new class. It contains methods for all mocked methods, and for all others, it is a child class that implements the original class as its parent. The mock class contains no constructor - for normal mock objects, the parent gets called. That is the way constructor arguments passed to Mockery::mock($class, $args) get considered.
But for instance mocks, a constructor for copied mocks is needed. It is hidden pretty deep inside the source, but if you look at mockery/library/Mockery/Generator/StringManipulation/Pass/InstanceMockPass.php, you can see that the only purpose of that method is to copy properties and expectations over to the new instance. Arguments are simply ignored.
The best approximation you could get would be to wrap your instantiation call in a mockable method that does nothing besides creating an instance with the arguments from the method - like here:
public function instantiate ( $name, $args=array() ) {
if ( empty($args) )
return new $name();
else {
$ref = new ReflectionClass( $name );
return $ref->newInstanceArgs( $args );
}
}
or to delegate the real instantiation task from the constructor to a mockable method:
class Schema {
public function __construct () {
call_user_func_array( array( $this, 'init' ), func_get_args() );
}
public function init ($name, $obj) {
//...
}
}

Why would mocked returnValue not work in tearDown of phpunit?

In the setup of my test class, I create a mock object for a user. When the mock is getting created, it does something like this:
$other = $this->getMock( 'Other' );
$user->expects( $this->any() )
->method( '_getOtherInstance' )
->will( $this->returnValue( $other ) );
Now in the deletion of the user, it calls _getOtherInstance to remove the tertiary information.
When I run delete in my test class' tearDown, before parent::tearDown, _getOtherInstance returns null.
I know the mock is setup correctly since running delete in setUp works.
What is special about tearDown here? I would imagine that PHPUnit would unset all the mocks and what they return, but not before I call up the chain with parent::tearDown.
Both setUp and tearDown are empty in TestCase, and you don't need to bother calling them from your tests. They are template methods for your use alone. The same applies to the static versions as well.
The method that calls the above and your test method is runBare, and it calls verifyMockObjects before calling tearDown. This method calls __phpunit_verify on each mock which in turn verifies the expectations and then deletes them:
public function __phpunit_verify() {
$this->__phpunit_getInvocationMocker()->verify();
$this->__phpunit_invocationMocker = NULL;
}
Dumping the mock object in tearDown shows that the invocation mocker which contains the expectations has been set to null, making them unavailable to your tearDown method.
Danger: If you really must get at the expectation, you can override verifyMockObjects in your test case since it's a protected method. Grab what you need and then call the parent method. Just keep in mind that you're treading in the PHPUnit internals which change quite frequently.

Categories