Is there a way to make PHPUnit throw an exception if a method being stubbed does not originally exist in the class being stubbed?
Here's a crude example:
class User {
function getId() { ... }
}
class LoginTest extends PHPUnit_Framework_TestCase {
function testLogin() {
...
$userMock = $this->getMockBuilder('User')
->setMethods(['getID']) // getId is misspelled; error should occur
->getMock();
...
}
}
class Login {
function login($user) {
...
$id = $user->getID(); // tests will pass even though this is misspelled
...
}
}
#Schleis is right that you cannot do it in PHPUnit directly.
But as of PHPUnit 4.5 you can use Prophecy to create test doubles. Prophecy will not tolerate this behavior. You won't be able to mock a non-existing method with it.
No, you can't.
PHPUnit doesn't need to have the class available to mock it. And you can even set the mock to not use any autoloading. When this happens, PHPUnit creates a fake class on the fly. This fake class doesn't have any defined methods which would cause an exception to be thrown, failing your tests suite.
Your tests should fail because of issues with the code that is being tested. Issues with the mock are outside of the scope of your test. The issue in your example would be caught during your functional testing.
There really isn't an easy way to tell between an misspelled function and one that hasn't been implemented yet.
Related
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()
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
}
}
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?
Using PHPUnit 3.6 I'm trying to test the exec() method in the below controller class. This method does two things:
Determines the name of the method to call based on the object's existing properties, and ...
If the determined controller method is callable it is executed and if not the method throws an exception
The (simplified) source code looks like this:
abstract class CLIController extends Controller
{
/* irrelevant class details here */
public function exec()
{
$action = ! empty($this->opts->args[0])
? $this->opts->args[0]
: $this->default_action;
if ( ! $action || ! is_callable(array($this, $action))) {
$msg = 'Invalid controller action specified';
throw new LogicException($msg);
} else {
$this->$action(); // <---- trying to get code coverage on this line!
}
}
}
So my problem is ...
I can't figure out how to get coverage on this part of the code:
} else {
$this->$action();
}
because I'm not sure how to (or that it's even possible to) test the invocation of a method whose name is not known in the context of the abstract class. Again: the method to be called is declared in child classes. Normally I would just mock an abstract method but I can't in this case because the method doesn't exist yet -- it will be specified by a child class.
What might be the answer ...
??? It may be possible that this line doesn't even need to be covered because it essentially relies on PHP's ability to correctly invoke a callable class method. If I successfully test that exec() throws an exception when it's supposed to, I know that correct functioning of the line in question depends on PHP functioning correctly. Does this invalidate the need to test it in the first place ???
If there is some way to mock the abstract class and create a method with a known name to add to the mocked class this would solve my problem and is what I've been trying unsuccessfully to do so far.
I know I could create a child class with a known method name but I don't believe it's a good idea to create a concrete child class just to test an abstract parent.
It could be that I need to refactor. One thing I don't want to do is leave child classes to implement the exec() function on their own.
What I've tried ...
Use some of PHP's reflection capabilities to no avail -- this may perhaps be due to my own inexperience with reflection and not its inability to handle this case, though.
Going back and forth through the PHPUnit manual and API docs. Unfortunately, as awesome as PHPUnit is, I often find the API documentation a bit light.
I would really appreciate any guidance on how best to proceed here. Thanks in advance.
I disagree with your stipulation that "it's [not] a good idea to create a concrete child class just to test an abstract parent." I do this quite often when testing abstract classes and usually name the concrete subclass after the test to make it clear.
class CLIControllerTest extends PHPUnit_Framework_TestCase
{
public function testCallsActionMethod()
{
$controller = new CLIControllerTest_WithActionMethod(...);
// set $controller->opts->args[0] to 'action'
$controller->exec();
self::assertTrue($controller->called, 'Action method was called');
}
}
class CLIControllerTest_WithActionMethod extends CLIController
{
public $called = false;
public function action() {
$this->called = true;
}
}
The code to make this test happen is trivial and can be easily verified by inspection.
I'm curious, why use is_callable instead of method_exists to avoid creating the array? It's probably just personal preference, but I'm wondering if there are any semantic differences.
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.