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.
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.
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.
Given this class:
class MyBuilder {
public function build($param1, $param2) {
// build dependencies ...
return new MyClass($dep1, $dep2, $dep3);
}
}
How can I unit test this class?
Unit-testing it means I want to test its behavior, so I want to test it builds my object with the correct dependencies. However, the new instruction is hardcoded and I can't mock it.
For now, I've added the name of the class as a parameter (so I can provide the class name of a mock class), but it's ugly:
class MyBuilder {
public function build($classname, $param1, $param2) {
// build dependencies ...
return new $classname($dep1, $dep2, $dep3);
}
}
Is there a clean solution or design pattern to make my factories testable?
Factories are inherently testable, you are just trying to get too tight of control over the implementation.
You would check that you get an instance of your class via $this->assertInstanceOf(). Then with the resulting object, you would make sure that properties are set properly. For this you could use any public accessor methods or use $this->assertAttribute* methods that are available in PHPUnit.
http://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.assertions.assertEquals
Many of the common assertions also have the ability to check attributes for protected and private properties.
I wouldn't specify the classname in your parameter list, as your usage is that the factory will only return one type and it is only the dependencies that are changed. Making it return a mock object type is unnecessary and makes your test more complicated.
The test would end up looking like this:
public function testBuild() {
$factory = new MyBuilder();
//I would likely put the following into a data provider
$param1 = 'foo';
$param2 = 'bar';
$depen1 = 'boo';
$depen2 = 'baz';
$depen3 = 'boz';
$object = $factory->build($param1, $param2);
$this->assertInstanceOf('MyClass', $object);
//Check the object definition
//This would change depending on your actual implementation of your class
$this->assertAttributeEquals($depen1, 'attr1', $object);
$this->assertAttributeEquals($depen2, 'attr2', $object);
$this->assertAttributeEquals($depen3, 'attr3', $object);
}
You are now making sure that your factory returns a proper object. First by making sure that it is of the proper type. Then by making sure that it was initialized properly.
You are depending upon the existence of MyClass for the test to pass but that is not a bad thing. Your factory is intended to created MyClass objects so if that class is undefined then your test should definitely fail.
Having failing tests while your developing is also not a bad thing.
So what do you want to test?
so I want to test it builds my object with the correct dependencies.
I do see a problem with this. It's either possible that you can create an object with incorrect dependencies (which should not be the case in the first place or tested in other tests, not with the factory) or you want to test a detail of the factory that you should not test at all.
Otherwise - if it's not mocking the factory what you're looking for - I see no reason why a simple
$actual = $subject->build($param1, $param2);
$this->assertInstanceOf('MyClass', $actual);
would not make it. It tests the behavior of the factory build method, that it returns the correct type.
See as well Open-Close-Principle
For tests, you can just create your MockBuilder which extends from your Builder:
class MyMockBuilder extends MyBuilder {
public function build($param1, $param2) {
// build dependencies ...
return new MyMockClass($dep1, $dep2, $dep3);
}
}
Making the classname a parameter 1:1 seems not practical to me, because it turns the factory over into something different. The creating is a detail of the factory, nothing you externalize. So it should be encapsulated. Hence the MockBuilder for tests. You switch the Factory.
As I see it, you ned to verify two things for that builder:
the correct instance is returned
values, that are injected are the right ones.
Checking instance is the easy part. Verifying values needs a bit of trickery.
The simples way to do this would be altering the autoloader. You need to make sure that when MyClass is requested for autoloader to fetch, instead of /src/app/myclass.php file it loads /test/app/myclass.php, which actually contains a "transparent" mock (where you with simple getters can verify the values).
bad idea
Update:
Also, if you do not want to mess with autoloader, you can just at th top of your myBuilderTest.php file include the mock class file, which contains definition for MyClass.
... this actually seems like a cleaner way.
namespace Foo\Bar;
use PHPUnit_Framework_TestCase;
require TEST_ROOT . '/mocks/myclass.php'
class MyBuilderTest extends PHPUnit_Framework_TestCase
{
public function MyBuilder_verify_injected_params_test()
{
$target = new MyBuilder;
$instance = $target->build('a', 'b');
$this->assertEquals('a', $instance->getFirstConstructorParam();
}
}
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.