PHPUnit - Mocked method expects to be called once fails [duplicate] - php

It may seem silly, hope not, but I want to create a service that will return mock objects for people that uses my project so they can mock all the classes from my project and test their code.
My idea was to offer this kind of service so it can be called inside other project's test cases and obtain the appropriate mock for each test.
Is that possible? Or there are other ways to do that. Btw, I can't use any mocking library because of project's limitations.

Yes, it is possible. Under the hood the getMock method uses the PHPUnit_Framework_MockObject_Generator class. So you can use it directly:
PHPUnit_Framework_MockObject_Generator::getMock($originalClassName, $methods)
But you will lose all the expectation shortcuts like $this->once(). You will have to instantiate the expectations on your own:
$mock->expects(\PHPUnit_Framework_TestCase::once())
Look at the PHPUnit source code to see how the mocks are build

If you build the mock outside the TestCase, it will not be accounted as a mock and it's expectations won't be checked automatically.
If your mock builder service is supposed to be used exclusively from PHPUnit tests (even if those tests are not related) you can have the testcase instance passed to the mock builder, so you can build the mock the usual way:
class MockBuilderService
{
private $test;
public function __construct(PHPUnit_Framework_TestCase $test)
{
$this->test = $test;
}
public function buildVeryComplexMock()
{
$mock = $this->test->getMock('MyClass');
$mock->expects($this->test->once())
->method('foo')
->willReturn(1);
return $mock;
}
}
So you can use it from your test:
class ATest extends PHPUnit_Framework_TestCase
{
public function testFoo()
{
$mock_builder = new MockBuilderService($this);
$complex_mock = $mock_builder->buildVeryComplexMock($mock_configuration);
// the mock expectations will be checked as usual
}
}

Related

How to test this without mocks?

I've been reading about testing without mocks and in general I like it. However, I've been struggling what to do when there's some third-party class included. For example if we have this class:
<?php
use External\ThirdPartyService;
use External\ThirdPartyException;
class AdapterForExternalService implements OurInterface
{
private ThirdPartyService $external;
public function __construct(ThirdPartyService $external)
{
$this->external = $external;
}
public function something(): int
{
try {
return $this->external->someMethod();
} catch (ThirdPartyException $e) {
return 1;
}
}
}
I know how to test it by mocking the external class, but is it possible to do it without mocking too?
If mocking is non-avoidable here, what if the ThirdPartyService class is final?
The something() method is not changing observable state. It only wraps the call to the third party service and turns the exception into an integer return value. The thing that is observable and that you could (in theory) assert on, is the call to the service and the returned results. This means, if you are not mocking, you will have to call that service.
To test this at all, you have to mock the dependency. If that class is really final, you can create a non-final facade for it and mock that.

PHPUnit mock class that has named static constructor

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.

Is it possible to create a mock outside a test case in PhpUnit?

It may seem silly, hope not, but I want to create a service that will return mock objects for people that uses my project so they can mock all the classes from my project and test their code.
My idea was to offer this kind of service so it can be called inside other project's test cases and obtain the appropriate mock for each test.
Is that possible? Or there are other ways to do that. Btw, I can't use any mocking library because of project's limitations.
Yes, it is possible. Under the hood the getMock method uses the PHPUnit_Framework_MockObject_Generator class. So you can use it directly:
PHPUnit_Framework_MockObject_Generator::getMock($originalClassName, $methods)
But you will lose all the expectation shortcuts like $this->once(). You will have to instantiate the expectations on your own:
$mock->expects(\PHPUnit_Framework_TestCase::once())
Look at the PHPUnit source code to see how the mocks are build
If you build the mock outside the TestCase, it will not be accounted as a mock and it's expectations won't be checked automatically.
If your mock builder service is supposed to be used exclusively from PHPUnit tests (even if those tests are not related) you can have the testcase instance passed to the mock builder, so you can build the mock the usual way:
class MockBuilderService
{
private $test;
public function __construct(PHPUnit_Framework_TestCase $test)
{
$this->test = $test;
}
public function buildVeryComplexMock()
{
$mock = $this->test->getMock('MyClass');
$mock->expects($this->test->once())
->method('foo')
->willReturn(1);
return $mock;
}
}
So you can use it from your test:
class ATest extends PHPUnit_Framework_TestCase
{
public function testFoo()
{
$mock_builder = new MockBuilderService($this);
$complex_mock = $mock_builder->buildVeryComplexMock($mock_configuration);
// the mock expectations will be checked as usual
}
}

How to add mocked expectations to a class under test

The code to be tested
abstract class Parent
{
public function getSomething(){} //this has to be mocked
}
class Child extends Parent
{
public function methodWhichIsTested()
{
$something = $this->getSomething(); //call to parent method
}
}
The test
public function setUp()
{
$this->child = new Child;
}
public function testTheChildMethod()
{
$this->child->methodWhichIsTested();
}
How can I add mock expectations to the instantiated class Child?
I would like to do something like:
MockFramework->takeExistingClass('Child')->shouldRecieve('getSomething')->andReturn('whatever');
My problem is, that in the real case (not the example), the getSomething method returns a dependency, which I need to mock!
I am using Mockery but if you know how to do this with phpUnit mocks, go ahead! Maybe I'm making a basic thinking mistake, please give me a hand! Thanks.
After you clarfied in chat that the returned value of getSomething holds a dependency that is
a protected property of the abstract class, and it is injected into that abstract via another public method of the abstract
the solution is inject a mock of that dependency via that other method.
In general, you should never have the need to mock or stub behavior of the TestSubject. It is only when you are making lookups to the Global Scope or mix/hard code object creation into the TestSubject, that you might see the need for that, but these would be code smells and should be refactored instead. See Sebastian Bergmann's articles on untestable code:
Testing private methods
Testing code that uses singletons
Stubbing static methods
Stubbing hard-coded dependencies

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