PHP method mock check for an argument passed to the method - php

I am using the Phpunit for unit testing and its mocking framework. I have a mock for a method:
$myProcessor
->expects($this->once())
->method("myMockedMethodName");
I would like to validate one of the arguments passed to it.
For example, my function takes arg1, arg2, arg3. I would like to check only for the arg2.
How to do with PHP mocks?

You'd use the with() method as explained in the docs (https://phpunit.readthedocs.io/en/latest/test-doubles.html#test-doubles-mock-objects-examples-subjecttest2-php)

Related

How does PHP function dependancy work?

I have an example here from the framework Laravel.
class UserController extends Controller
{
public function store(Request $request)
{
$name = $request->input("name");
}
}
What I don't understand is that Request is explicitly defined within the function signature of store().
Couldn't php's type inference figure out what is being passed into the function?
Would this even be considered dependency injection?
This is method level dependency injection. The Request is being passed into the store method, instead of the store method knowing how to create the Request object.
The type hint in the method signature does two things.
First, for plain PHP, it enforces the fact that the parameter passed to the store method must be a Request object, or an object that inherits from Request. If you attempt to call the store() method with anything other than a Request object (or descendant), you will get an error.
Because of this strict type enforcement, we know that the $request parameter must be a Request object (or descendant), and we can depend on its public interface being available for use (you know the input() method will exist).
Second, for Laravel, it tells the Laravel container what type of object this method needs, so that the correct object can be resolved out of the container and passed to the method. Laravel's controller methods are called through the container, which allows automatic dependency resolution of method calls.
If you were to remove the type hint, this would cause two issues. At an abstract level, it would allow you to pass any type of object, or even scalar values, to the store() method. If you attempt to call input() on a string, you're going to have problems. At a more concrete level, this will break Laravel's automatic dependency resolution of controller methods. Without the type hint, Laravel can't know what object the method requires, so it won't pass in anything, so in reality you'd get an error saying that you can't call input() on null (or an error saying the store method requires one parameter; not sure, didn't test).

phpunit mock - method does not exist

I recently updated PHPunit from 5.3 to 5.5 in an IntegrationTestCase of an app that is CakePhp 3.x based. and I don't understand how to update my mock generation scripts.
Originally I created my mock like this:
$stub = $this->getMock('SomeClass', array('execute'));
$stub->method('execute')
->will($this->returnValue($this->returnUrl));
After the change to PHPUnit 5.5 this got me the following warning:
PHPUnit_Framework_TestCase::getMock() is deprecated,
use PHPUnit_Framework_TestCase::createMock()
or PHPUnit_Framework_TestCase::getMockBuilder() instead
In order to fix this warning I changed the mock-generation to:
$stub = $this->getMockBuilder('SomeClass', array('execute'))->getMock();
$stub->method('execute')
->will($this->returnValue($this->returnUrl));```
Now I get the following error message when running the test:
exception 'PHPUnit_Framework_MockObject_RuntimeException'
with message 'Trying to configure method "execute" which cannot be
configured because it does not exist, has not been specified,
is final, or is static'
Anybody know, how to avoid this error? Thank you.
PHPUnit_Framework_TestCase::getMockBuilder() only takes one (1) argument, the class name. The methods to mock are ment to be defined via the returned mock builder objects setMethods() method.
$stub = $this
->getMockBuilder('SomeClass')
->setMethods(['execute'])
->getMock();
See also
PHPUnit Manual > Test Doubles > Mock Objects
I will leave this as an answer to myself, when i reach this problem again:
The mocked method may not be private.
First of all, it's just
$stub = $this->getMockBuilder('SomeClass')->getMock();
Second, error states that method execute does exist in your class SomeClass.
So, check if it really exists and it's public and not final.
If everything's good, check a full classname, if it real and specified with correct namespace.
To avoid stupid errors with classname, it's better to use this syntax:
$stub = $this->getMockBuilder(SomeClass::class)->getMock();
In this case, if SomeClass doesn't exist or namespace is missed, you will get a clear error about it.
Addition to upper messages: split mock method declarations
Instead of this:
$mock
->method('persist')
->with($this->isInstanceOf(Bucket::class))
->willReturnSelf()
->method('save')
->willReturnSelf()
;
Use this:
$mock
->method('persist')
->willReturnSelf()
;
$mock
->method('save')
->willReturnSelf()
;
Perhaps , the method does not exist in the class that you mock .

What is the difference between createMock and getMockBuilder in phpUnit?

For the love of my life I can't figure out the difference between createMock($type) and getMockBuilder($type)
I am going through the original documentation and there is just a one liner which I didn't understand.
... you can use the getMockBuilder($type) method to customize the test double generation using
a fluent interface.
If you can provide me an example, I would be grateful. Thanks.
createMock($type) uses getMockBuilder() internally:
protected function createMock($originalClassName)
{
return $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->disallowMockingUnknownTypes()
->getMock();
}
So the createMock() method will return you a mock built with the general best-practice defaults.
But with getMockBuilder($type), you can create a mock with your own requirements.
From the manual
https://phpunit.de/manual/current/en/test-doubles.html
The createMock($type) and getMockBuilder($type) methods provided by
PHPUnit can be used in a test to automatically generate an object that
can act as a test double for the specified original type (interface or
class name). This test double object can be used in every context
where an object of the original type is expected or required.
The createMock($type) method immediately returns a test double object
for the specified type (interface or class). The creation of this test
double is performed using best practice defaults (the __construct()
and __clone() methods of the original class are not executed and the
arguments passed to a method of the test double will not be cloned.
If these defaults are not what you need then you can use the
getMockBuilder($type) method to customize the test double generation
using a fluent interface.
They are already plenty answers on stack overflow what are fluent interfaces.

PHPUnit prophesize a method without exact arguments

I'm mocking a UserRepository class using prophecy to ensure that when a POST request to /user is sent, that the create() method on UserRepository is fired.
$repository = $this->prophesize(UserRepository::class);
$repository->create()->shouldBeCalled()
The only problem is that the create() method sends Request data as an argument to the repository for some serious tweaking of the inputs before doing anything. How do I mock the create() call without telling prophecy what the arguments will be?
Or is this just really bad practice on my end and the Request data should never be passed to the repository?
use Prophecy\Argument;
$repository->create(Argument::any())->shouldBeCalled()
use Prophecy\Argument;
$repository->create(Argument::cetera())->shouldBeCalled()
any() matches any single value where cetera matches all values to the rest of the signature.

Mocking Laravel Model::increment() with Mockery

I have a line of code in a Laravel 5 event handler which looks like this:
$this->event->batch->increment('attempted_jobs');
$this->event is the event which calls the handler and $this->event->batch contains my Batch model. All this does in increment the attempted_jobs column within my database, so it's fairly basic stuff.
I would like to be able to test this event handler, I'm using Codeception and Mockery. My mock for $this->event->batch looks like this:
$batch = m::mock('MyVendor\MyApp\Batch');
$batch->shouldReceive('increment')->once()->with('attempted_jobs');
This however causes issues - increment() is a protected method of Model and therefore cannot be mocked. Here's the exact error:
InvalidArgumentException: increment() cannot be mocked as it a protected method and mocking protected methods is not allowed for this mock
It appears to be implemented using the __call() PHP magic method, so how to I mock this? I've attempted creating a __call() mock, but this churns out tonnes of errors related to the increment() method not being implemented.
The issue was because, as stated, increment() is a protected method of Illuminate\Database\Eloquent\Model(). The way to get around this is to mock the __call() method directly, like so:
$batch = m::mock('MyVendor\MyApp\Batch');
$batch->shouldReceive('__call')->with('increment')->once();
(I'm not sure why this didn't work when I first tried it though)

Categories