Say I want to replace a method in an object that gets database from a database with one that has the data pre-populated. How would I do this?
According to https://phpunit.de/manual/current/en/test-doubles.html ...
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.
Great. So that tells phpunit which methods I want to replace but where do I tell it what I'm replacing them with?
I found this example:
protected function createSSHMock()
{
return $this->getMockBuilder('Net_SSH2')
->disableOriginalConstructor()
->setMethods(array('__destruct'))
->getMock();
}
Great - so the __destruct method is being replaced. But what is it being replaced with? I have no idea. Here's the source for that:
https://github.com/phpseclib/phpseclib/blob/master/tests/Unit/Net/SSH2Test.php
With a method that doesn't do anything, but whose behaviour you can configure later. Although I'm not sure you fully understood how mocking works.
You're not supposed to mock the class you're testing, you're supposed to mock objects on which the class being tested relies on. For example:
// class I want to test
class TaxCalculator
{
public function calculateSalesTax(Product $product)
{
$price = $product->getPrice();
return $price / 5; // whatever calculation
}
}
// class I need to mock for testing purposes
class Product
{
public function getPrice()
{
// connect to the database, read the product and return the price
}
}
// test
class TaxCalculatorTest extends \PHPUnit_Framework_TestCase
{
public function testCalculateSalesTax()
{
// since I want to test the logic inside the calculateSalesTax method
// I mock a product and configure the methods to return some predefined
// values that will allow me to check that everything is okay
$mock = $this->getMock('Product');
$mock->method('getPrice')
->willReturn(10);
$taxCalculator = new TaxCalculator();
$this->assertEquals(2, $taxCalculator->calculateSalesTax($mock));
}
}
Your test mocks the exact class you're trying to test, which might be a mistake, since some methods might be overridden during mocking.
Related
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();
}
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.
Given I have a FruitSalad class (the system under test):
class FruitSalad
{
protected $fruits = [];
public function addFruit(Fruit $fruit)
{
$this->fruits[] = $fruit;
return $this;
}
}
And I have a Fruit class:
class Fruit
{
public static function withName($name)
{
$instance = new MyDependencyClass();
$instance->name = $name;
return $instance;
}
}
A trivial example, however you can see that the Fruit class uses a named static constructor, and the addFruit() method on the FruitSalad class type hints Fruit as its expected parameter.
When writing a test for addFruit(), I need to mock the Fruit class.
function test_it_can_add_a_fruit_to_its_list_of_fruits()
{
$fruit = $this->getMockBuilder('Fruit')
->disableOriginalConstructor()
->getMock();
$this->fruitSalad->addFruit($fruit);
// Do some assertion.
}
This creates a simple mock of the Fruit class, but I want to instantiate it via the withName() static method - and I do not want to expose a setter for the name property.
How can I create a mock for Fruit using the static named constructor?
PHPUnit used to support mocking static methods, but since PHPUnit 4.0 it's omitted. I see four options here:
1. Don't mock the method at all
You could just call the method and use it's logic, although you'd test the static method as well if you do and normally that's something you should avoid when writing unit tests.
2. Change the class
Ask yourself if this method really needs to be static and if not, change the class to test it properly. There are quite some use cases where it's better to change some of your architecture in order to write proper tests.
3. Use a spy class
Spy classes are classes that extend a class that you would usually mock, but implement some logic for testing the configuration of a class or the dependency of a tested method to another method. In the very most cases this can be avoided by mocking the class properly. Spies are simply your work around if mocks are not enough, there are very few cases in which you really need them.
However, in this case a spy could be used to overwrite a static method as a work around:
class FruitSpy extends Fruit
{
public static $return;
public static $name;
public static function withName($name) {
$expected = self::$name;
if($name == $expected) {
return self::$return;
} else {
throw new \RuntimeException("FruitSpy::withName(): Parameter 0 was $name, $expected expected");
}
}
}
This example checks for the correct $name and, if it's correct, returns your defined return. You'd use it like this in your test:
$fruitSpy = new FruitSpy();
$fruitSpy::$name = "Banana";
$fruitSpy::$return = new \stdClass();
$this->fruitSalad->addFruit($fruitSpy);
Not exactly a clean solution, but the only way I see if you absolutely positively don't want to change other code than the test code.
Again, you should think about changing the static method to a casual method if you need to do something like this.
4. Use PHPUni 3.*
You could simple use a deprecated version of PHPUnit to use this method. Not a preferred way either.
Conclusion
I don't see a clean way to mock a static method and ::staticExpects() was removed for a reason in 4.0
How can I create a mock for Fruit using the static named constructor?
You can't. Mocks are created by using a mocking framework.
Anyway it does not matter how mocks are created but, instead, how they behave, because they're external to the class being tested.
Just configure the mock so that it behaves the same way a real Fruit instance would when created using Fruit::withName.
I'm trying to create a pretty standard unit test where I call a method and assert it's response, however the method I'm testing calls another method inside the same class which does a little bit of heavy lifting.
I want to mock that one method but still execute the method I'm testing as is, only with the mocked value returned from the call to the other method.
I've dumbed down the example to make it as simple as possible.
class MyClass
{
// I want to test this method, but mock the handleValue method to always return a set value.
public function testMethod($arg)
{
$value = $arg->getValue();
$this->handleValue($value);
}
// This method needs to be mocked to always return a set value.
public function handleValue($value)
{
// Do a bunch of stuff...
$value += 20;
return $value;
}
}
My attempt at writing the tests.
class MyClassTest extends \PHPUnit_Framework_TestCase
{
public function testTheTestMethod()
{
// mock the object that is passed in as an arg
$arg = $this->getMockBuilder('SomeEntity')->getMock();
$arg->expects($this->any())
->method('getValue')
->will($this->returnValue(10));
// test handle document()
$myClass = new MyClass();
$result = $myClass->testMethod($arg);
// assert result is the correct
$this->assertEquals($result, 50);
}
}
I have tried mocking the MyClass object, but when I do that and call the testMethod it always returns null. I need a way to mock the one method but leave the rest of the object intact.
You can mock the class that you are testing and specify the method that you want to mock.
$mock = $this->getMockBuilder('MyClass')
->setMethods(array('handleValue'))
->getMock();
$mock->expects($this->once())
->method('handleValue')
->will($this->returnValue(23)) //Whatever value you want to return
However, IMO this is not the best idea for your tests. Testing like this will make refactoring much more difficult. You are specifying the implementation of the class rather than the behavior that the class is supposed to have. If handleValue is doing a lot of complicated work that makes testing difficult, consider moving the logic into a separate class and injecting that into your class. Then you can create a mock of that class and pass it in to testMethod. Doing so will give you the added advantage of making MyClass more extensible if handleValue needs to adapt its behavior.
http://www.oodesign.com/strategy-pattern.html
As a general rule, you should not mock the system that you are testing.
You can specify which methods to mock (partial mock) with setMethods():
// Let's do a `partial mock` of the object. By passing in an array of methods to `setMethods`
// we are telling PHPUnit to only mock the methods we specify, in this case `handleValue()`.
$csc = $this->getMockBuilder('Lightmaker\CloudSearchBundle\Controller\CloudSearchController')
->setConstructorArgs($constructor)
->setMethods(array('handleValue'))
->getMock();
// Tell the `handleValue` method to return 'bla'
$csc->expects($this->any())
->method('handleValue')
->with('bla');
Any other methods in the class not specified in the array you give setMethods() will be executed as is. If you do not use setMethods all methods will return NULL unless you specifically set them.
I read somewhere that it's a good idea to break methods down into smaller, testable functions so that the smaller methods can be tested. But I'm confused on how to test the method that calls the smaller method. Here's an example:
class MyTestableClass
{
public function myHugeFunction($list, array $list_filter_params)
{
// do a bunch of stuff, then...
foreach($list as $item) {
$this->subFunction($item);
}
}
public function subFunction()
{
// do stuff for each item
}
}
and the test class:
class MyTestableClassTest extends PHPUnit_Framework_TestCase
{
public function testSubFunction
{
// This tests the smaller, bite-size method, which is good.
}
public function testMyHugeFunction
{
// this tests the parent function *and* the subFunction. I'm trying
// to test *just* the parent function.
}
}
I know how to test the subFunction, but since I can't stub a method in the same class, I don't know how to test the parent method only. I'd like to find a way of somehow stubbing the subFunction to just return true.
Do you use Events and stub the event class? That's the only way I can think of to stub another method called in the same class.
Apart from things what #fab said in his comment (which you really should consider!), actually you can stub / mock methods in the SUT. For your example, build your SUT object may looks like:
class MyTestableClassTest extends PHPUnit_Framework_TestCase
{
public function testSubFunction
{
// This tests the smaller, bite-size method, which is good.
}
public function testMyHugeFunction
{
// first, prepare your arugments $list and $list_filter_params
// (...)
// Next build mock. The second argument means that you will mock ONLY subFunction method
$this->sut = $this->getMock('Namespace\MyTestableClass', array('subFunction'));
// now you can set your subFunction expectations:
$this->sut->expects($this->exactly(count($list))
->with($this->anything()); // of course you can make this mock better, I'm just showing a case
// start testing
$this->sut->myHugeFunction($list, $list_filter_params);
}
}
PS And again, as #fab said: if you show a specific case, I'm sure you'll get a lot of good answers from people here.