I have run into a problem when mocking Interfaces using Mockery in PHP (im using the laravel framework but I'm not sure this is relevant.
I have defined an interface
<?php namespace MyNamespace\SomeSubFolder;
interface MyInterface {
public function method1();
}
And I have a class that typehints that interface on one of the methods...
<?php namespace MyNamespace\SomeSubFolder;
use MyNamespace\SomeSubFolder\MyInterface;
class MyClass {
public function execute(MyInterface $interface)
{
//does some stuff here
}
}
...and I am trying to test MyClass. I have created a test that looks something like this:
public function testExecute()
{
$mock = Mockery::mock('MyNamespace\SomeSubFolder\MyInterface');
$mock->shouldReceive('method1')
->andReturn('foo');
$myClass = new MyClass();
$myClass->execute($mock);
}
When I run the test I receive the message
'ErrorException: Argument 1 passed to MyClass::execute must be an instance of MyNamespace\SomeSubFolder\MyInterface, instance of Mockery_123465456 given....'
I have no idea why.
Within the test I have tried the following :
$this->assertTrue(interface_exists('MyNamespace\SomeSubFolder\MyInterface'));
$this->assertTrue($mock instanceof MyInterface);
and both return true, so it appears as if I have created and instance that implements the interface, but when I call the method on the class it disagrees. Any ideas???
You should call mock() method at the and of mock declaration.
$mock = Mockery::mock('MyNamespace\SomeSubFolder\MyInterface');
$mock->shouldReceive('method1')
->andReturn('foo')
->mock();
I think the problem is with PHP class loading. Your 'MyNamespace\SomeSubFolder\MyInterface' class isn't available from your test file.
You'll need to modify your composer.json to autoload that namespace or you'll need a require_once('path/to/file/containing/namespace/MyInterface.php') at the top of your test.
Although, when you did try the interface_exists it seemed to pass. It could also be that you misspelled your namespaced class when you mocked it. I've made that mistake as well.
In any case, Mockery isn't able to see that the class exists so it's just inventing one.
Can you provide your full test source?
Related
I am having trouble setting up Mockery to work in my application. I need to make a partial mockery of a library class that has a few functions in it that need to be mocked while other functions are tested. It seems like its failing to pick up the basic ShouldRecieve() function from mockery and instead trying to dig through MyClass for an instance of it. I've created an example of what is going on below.
<?php
class MyClass {
function foo() { return $this->bar; } // to be tested
function bar() { return true; } // to be mocked
}
class MyClass_Test extends TestCase {
function testMyClassFoo() {
$mock = \Mockery::mock('MyClass')->makePartial();
$mock->shouldRecieve('bar')->once()->andReturn(true);
$result = $mock->foo();
$this->assertTrue($result);
}
}
When I try to run the tests I'll get the following error:
Mockery\Exception\BadMethodCallException: Method Mockery_0_MyClass::shouldRecieve() does not exist on this mock object
I am thinking there must be something wrong with the Mockery installation but it all seems to be there. Any ideas why this might happen?
I know you've provided an example but I'm assuming your error is the real thing.
The method is shouldReceive().
Consider this code:
<?php namespace App\Services;
use App\Services\AnotherService;
class SomeService {
public function someMethod() {
$anotherService = \App::make(AnotherService::class);
}
}
My intention is to get a class object with all its dependencies resolved. But in this specific case, I would like that the object's __construct has been executed as well. I have tried and by code above, the $anotherService object's __construct method is not executed.
Therefore I can achieve what I need by doing -
$anotherService = \App::make(AnotherService::class);
$anotherService->__construct();
Can it be done with single line instead of redundantly each time call construct method after instantiating the object? - Because that is what __construct method is made for - executing automatically. But I have noticed that for some reason Laravel's automatic dependency resolving skips the __construct execution.
Note, that new AnotherService() is not an option for me, as well as using the __construct method of SomeService class. I would like to make an object inside the someMethod method.
Class AnotherService currently does not have any dependencies. It has just some random variable updates inside the __construct, like:
public function __construct() {
$this->varA = true;
$this->varB = 'Some Value';
}
Why do I need to resolve this class (instead of using new AnotherClass())? - Simply because I want to test this call in unit tests by mocking it. For that I use this code:
$this->mock(AnotherService::class, function ($mock) {
$mock->shouldReceive('anotherMethod')->andReturn(false);
});
And then finally I have to call the method to check the response:
$anotherService = \App::make(AnotherService::class);//<-- In this moment $varA and $varB are not set because the __construct did not execute!
$response = $anotherService->anotherMethod();
// Assert...
So, in this case, for example, if anotherMethod would use any of those variables, the tests would be incorrect because their values are not set.
I believe there is either a mistake somewhere in your code or some misunderstanding.
The problem here is not in Laravel. The key here is the usage of Mockery. When creating a simple Mock for a class, its constructor is not invoked, and it's just a mock. When using $this->mock(...), you just bind a concrete instance (mock) in the container. Thus, when calling its make, that ready mock is returned. Meaning, that __construct is neither invoked by Mockery, nor by Container.
I am getting Error: Call to undefined method Mock_SimpleInterface_8a93e777::mymethod() when I call the mymethod() on the Simple class mock.
class PlaygroundTest extends \PHPUnit_Framework_TestCase
{
public function testMock()
{
$class = $this->getMockBuilder('\Playground\Simple')->getMock();
$class->mymethod();
}
}
The Simple class implementation
namespace Playground;
class Simple
{
public function mymethod()
{
print "Hey!";
}
}
According to PHPUnit docs (https://phpunit.de/manual/5.1/en/test-doubles.html), it states that "By default, all methods of the original class are replaced with a dummy implementation that just returns null (without calling the original method)."
Shouldn't I be able to call mymethod() and get a null return value? I want to avoid to specify all class methods. PHPUnit should be clever enough to know which methods can be called on the mock or not.
Is this a bug? I'm using PHPUnit 5.1.4
Your assumptions are correct, so you have an error somewhere else or did not show the real code.
The mock class name Mock_SimpleInterface_8a93e777 suggests that you don't actually mock \Playground\Simple but rather \Playground\SimpleInterface, which probably does not contain mymethod()
The method I'm testing looks like :
function X($arg)
{
// Code ...
$object = new Object;
// Code ...
}
Is there a way to mock that object ?
Not with a PHPUnit Mock Object in your unit test you can't.
Depending on how you load your classes, you would be able to create a "mock" class which would replace the object. That would look like this:
Your test file:
class Object {
<mock functionality here>
}
class TestCase extends PHPUnit_Framework_TestCase {
<your tests>
}
However this will cause problems if you want to actually test Object in another test. In which case you will get a fatal error stating that you cannot redefine the class. This is not a best practice.
Your best bet is to refactor the class so that you use Dependency Injection. Change the class so that it takes an Object as either a constructor argument or is part of the method signature.
This article may be helpful: http://misko.hevery.com/2008/07/08/how-to-think-about-the-new-operator/
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+
->getMock();
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.