PHPunit: Can I mock class that I test? - php

I have a class like this:
<?php
class Apple {
public function foo($arg1){
if ($arg1 == 0){
$this->bar($arg1);
}
}
public function bar($arg){
//do something
}
}
And I have a unit test like this:
class AppleTest extends TestCase{
/**
* it's unit test for method Apple::foo
*/
public function testFoo(){
$mock = $this->getMockBuilder('Apple')
->setMethods(['bar'])
->getMock();
$mock->expects($this->once())
->method('bar')
->with($this->equalTo(0));
$mock->foo(0);
}
}
I was told, that I can't use a mock for the class that is being tested. I was told that I should use instance of the class instead of its mock, because when I use mock it's not actual testing. Can some one argue with that.

It isn't that you "can't", but that you really shouldn't. Obviously from your example test, you can mock the object that you are testing.
But this isn't necessarily a good idea. The issue with this is that you are specifying exactly what the code should look like. This can make refactoring more difficult. Suppose that the bar function needs to change and you want to move the functionality the foo needs into a private function. Now your test will fail because foo is no longer calling bar. In this case, it should pass because the functionality hasn't changed. This is one of the great benefits of unit tests, you are able to refactor the code and ensure that things still work correctly.
However, there are times when it is necessary to mock the class that you are testing. Generally this is when you are trying to write new tests for existing code and you aren't able to refactor it to use dependency injection. Then you will have to mock the other function in order to isolate things. This isn't ideal and should be avoided but writing a "bad" test can be better than having no test at all.

Related

PHPUnit mock parent protected method, which is owerriden

How could I test method Bar::foo?
class Foo {
protected function foo() {
// Do something
}
}
class Bar extends Foo {
protected function foo() {
parent::foo();
// Do something else
}
}
I need to mock Foo::foo and not to have
Fatal error: Access level to Bar::foo() must be public (as in class Foo)...
??
You don't need to mock Foo::foo to test your method. The fact that it calls the parent's foo method is an implementation detail. You don't need to make sure that a Bar object calls any of Foo's methods. The only thing that you would assert is that Bar is an instance of Foo.
From your example, you don't even need to explicitly test Bar::foo either because it is a protected method. If Bar::foo is complicated enough that you think that you need to write a test for it. Consider refactoring it to its own class and using the Strategy pattern for Bar.
You don't want your tests checking whether or not internal methods are called. This makes refactoring your classes much more difficult. Your test should pass if you were to copy all the lines from Foo::foo into Bar::foo. It isn't good coding practice. But tests don't ensure good code. They ensure that code works correctly.

PHP: Retain static methods AND maintain testability

My static methods are either of the 'helper' variety, e.g. convertToCamelCase(), or of the 'get singleton' variety, e.g. getInstance(). Either way, I am happy for them to live in a helper class.
The helper class needs to be widely available so I am loading it in my layer supertypes. Now, as far as I can see, provided that the helper can be injected into the supertypes, I have maintained full flexibility over testing my code (with the exception of the helper class itself). Does that make sense? Or am I overlooking something?
To look at it another way... it seems to me that difficulty in testing code increases in proportion to the number of calls to static methods, not in proportion to actual number of static methods themselves. By putting all these calls into one class (my helper), and replacing that class with a mock, I am testing code that is free of static calls and related problems.
(I realise that I should work towards getting rid of my Singletons, but that's going to be a longer term project).
In the case of a static class that is strictly a helper function like "convertToCamelCase" I would probably just have 100% coverage for that function and then consider it a "core" function and not worry about mocking it elsewhere. What is a mock for "convertToCamelCase" going to do anyway..? Perhaps your unit tests start to smell a little like integrations tests if you do it too much, but there's always a bit of a tradeoff between abstracting everything and making your app needlessly complicated.
As far as singletons it is tricky because you usually have the name of the static class in your code so it becomes problematic to swap it out with a mock object for testing. One thing you could do is wherever you are making your static method calls, start by refactoring to call them this way:
$instance = call_user_func('MyClass::getinstance');
Then, as you increase your testing coverage, you could begin replacing with something like:
$instance = call_user_func($this->myClassName . '::getinstance');
So - once you have that, you could swap out and mock MyClass by changing $this->myClassName. You'd have to make sure you're requiring or autoloading the relevant php files dynamically as well.
Refactoring to use an abstract factory pattern would make things even easier to test but you could start implementing that over time as well
However if you need to mock a static class somewhere, you can do it with Moka library. Here is example:
class UsersController
{
public function main()
{
return json_encode(User::find(1));
}
}
This is how you can test it:
class UsersController
{
private $_userClass;
public function __construct($userClass = 'User')
{
$this->_userClass = $userClass;
}
public function find($id)
{
return json_encode($this->_userClass::find($id));
}
}
class UsersControllerTest extends \PHPUnit_Framework_TestCase
{
public function testMainReturnsUser()
{
$userClass = Moka::stubClass(null, ['::find' => 'USER']);
$controller = new UsersController($userClass);
$this->assertEquals('"USER"', $controller->find(1000));
}
public function testMainCallsFind()
{
$userClass = Moka::stubClass(null, ['::find' => 'USER']);
$controller = new UsersController($userClass);
$controller->find(1000);
// check that `find` was called with 100
$this->assertEquals([1000], $userClass::$moka->report('find')[0]);
}
}

Unit test in a PHP class method - Dependency with class attribute?

Say I have this class:
class MyExample
{
public $my_var = [1,2,3,4];
public function MyMethod()
{
//Here I use $this->my_var;
}
}
What would be a good way to test the method MyMethod, taking into account that I use the class property $my_var in it. Would you suggest passing the class property as an argument to the method? So I can "give arguments, get a result" approach?:
public function MyMethod($my_var)
{
//Here I use $my_var;
}
I have a feeling that if I don't do that, if I want to test MyMethod, I would have to create several instances of MyExample with different values of $my_var and that would be somehow testing the class itself, and not just the method.
What would be a good approach?
The 'Unit' in 'Unit Test' is best thought of as the class itself. You should be able to refactor the internal implementation of the class extensively, while leaving its public API the same, without breaking your tests. In fact, that's basically what they're for: to allow you to make changes to the implentation of the unit, safe in the knowledge you will be told if you have changed the way it works from the perspective of other classes in your system.

Using PHPUnit to test helper functions

Let's say I want to test a simple helper that takes a class name as an argument and makes a redirection.
How am I supposed to test this if the function is called in many places from inside a couple of controllers? Should I test every class name that was passed as a parameter in the whole code (write them in the provider function myself)? Or is there a magical function which does that for me?
Your question is the exact reason why dependency injection -- when done correctly (not how most popular frameworks "implement" it) -- is touted as the ultimate in code testability.
To understand why, lets look at how "helper functions" and class-oriented programming make your controllers difficult to test.
class Helpers {
public static function myHelper() {
return 42;
}
}
class MyController {
public function doSomething() {
return Helpers::myHelper() + 100;
}
}
The entire point of unit testing is to verify that "units" of code work in isolation. If you can't isolate functionality, your tests are meaningless because their results could be tainted by the behavior of the other code involved. This can result in what statisticians call Type I and Type II errors: basically, this means you can get test results that might be lying to you.
In the code above, the helper cannot be easily mocked to determine that MyController::doSomething works in complete isolation from outside influences. Why not? Because we can't "mock" the behavior of the helper method to guarantee our doSomething method actually adds 100 to the helper result. We're stuck with the helper's exact behavior (returning 42). This is a problem that correct object-orientation and inversion of control eliminate entirely. Let's consider an example of how:
If MyController asks for it's dependencies instead of using the static helper function , it becomes trivial to mock the outside influences. Consider:
interface AnswerMachine {
public function getAnswer();
}
class UltimateAnswerer implements AnswerMachine {
public function getAnswer() {
return 42;
}
}
class MyController {
private $answerer;
public function __construct(AnswerMachine $answerer) {
$this->answerer = $answerer;
}
public function doSomething() {
return $this->answerer->getAnswer() + 100;
}
}
Now, it's trivially simple to test that MyController::doSomething does in fact add 100 to whatever it gets back from the answer machine:
// test file
class StubAnswerer implements AnswerMachine {
public function getAnswer() {
return 50;
}
}
$stubAnswer = new StubAnswerer();
$testController = new MyController($stubAnswerer);
assert($testController->doSomething() === 150);
This example also demonstrates how the correct use of interfaces in your code can greatly simplify the testing process. Test frameworks like PHPUnit make it very easy to mock interface definitions to perform exactly what you want them to in order to test the isolated functionality of code units.
So I hope these very simple examples demonstrate how powerful dependency injection is when it comes to testing your code. But more importantly, I hope they demonstrate why you should be wary if your framework of choice is using static (just another name for global), singletons, and helper functions.
You cannot test each possible combination of parameters to all the functions you need to test; it will take you longer than the life of the universe. So you use Human Intelligence (some might call it cheating ;-). Test it just once, in this case with a mock controller as the parameter.
Then look at your code and ask yourself if any other object passed in is really going to have it behave differently. For something you describe as a "simple helper" maybe the answer is no. But, if yes, how? Create another mock controller class that simulates that different behaviour. E.g. this second controller might not have the function your helper class expects to call. You expect an exception to be thrown. Create the unit test for that.
Repeat until satisfied.

Mock a method in the same class being tested

I want to mock a method in the same class that I am testing.
ClassA {
function hardToTest($arg) {
// difficult to test code
}
function underTest() {
return $this->hardToTest('foo');
}
}
I was thinking that I could use reflection to do this, but maybe it is just a sign that I should move hardToTest into another object.
This test will succeed if underTest() passes 'foo' to hardToTest(). This is known as a partial mock in PHPUnit's documentation because you are mocking only some of the methods.
ClassATest {
function testUnderTest() {
$mock = $this->getMock('ClassA', ['hardToTest']);
$mock->expects($this->once())
->method('hardToTest')
->with('foo');
$mock->underTest();
}
}
I agree with your instincts that this need may be a code smell telling you that this class is doing too much.
PHPUnit 5.4+
Since getMock() was deprecated in 5.4, use getMockBuilder() instead:.
$mock = $this->getMockBuilder('ClassA')
->setMethods(['hardToTest']) // onlyMethods in 8.4+
->ge‌​tMock();
What I resorted was creating a sub class of my system under test with the relevant method stubbed out.
What exactly is the reason what method is hard to test ?
If the method is protected, and you really really want to test it, then you can just extend your ClassA and make the hardToTest($arg) public.
The bottom line is that you shouldn't modify the class only because you need to write unittest for it. And in general, the private and protected methods should not be tested - you should test only the public interface.

Categories