I want to use a mock object (Mockery) in my PHPUnit test. The mock object needs to have both some public methods and some public properties set. The class is a Laravel Eloquent model. I tried this:
$mock = Mockery::mock('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true); //works fine
$mock->roles = 2; //how to do this? currently returns an error
$this->assertTrue(someTest($mock));
... but setting the public property returns this error:
BadMethodCallException: Method Mockery_0_User::setAttribute() does not exist on this mock object
This error is not returned when mocking a simple class, but is returned when I try to mock an Eloquent model. What am I doing wrong?
If you want getting this property with this value, just use it:
$mock->shouldReceive('getAttribute')
->with('role')
->andReturn(2);
If you call $user->role you will get - 2
($user - its User mock class)
This answer is a bit late but hopefully it will help someone. You can currently set a static property on mocked Eloquent objects by using the 'alias' keyword:
$mocked_model = Mockery::mock('alias:Namespace\For\Model');
$mocked_model->foo = 'bar';
$this->assertEquals('bar', $mocked_model->foo);
This is also helpful for mocking external vendor classes like some of the Stripe objects.
Read about 'alias' and 'overload' keywords:
http://docs.mockery.io/en/latest/reference/startup_methods.html
To answer your question, you could also try something like this:
$mock = Mockery::mock('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true); //works fine
$mock->shouldReceive('setAttribute')->passthru();
$mock->roles = 2;
$mock->shouldReceive('getAttribute')->passthru();
$this->assertEquals(2, $mock->roles);
Or, as suggested by seblaze, use a partial mock:
$mock = Mockery::mock('User[hasRole]');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2;
$this->assertEquals(2, $mock->roles);
But, from your code snippet, if you're writing unit tests, you should really only make one assertion per each test:
function test_someFunctionWhichCallsHasRole_CallsHasRole() {
$mock = Mockery::mock('User');
$mock->shouldReceive('hasRole')->once();
$mock->someFunctionWhichCallsHasRole();
}
function test_someFunctionWhichCallsHasRole_hasRoleReturnsTrue_ReturnsTrue() {
$mock = Mockery::mock('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$result = $mock->someFunctionWhichCallsHasRole();
$this->assertTrue($result);
}
Tried this? It should cover you issue.
https://github.com/padraic/mockery/blob/master/docs/11-MOCKING-PUBLIC-PROPERTIES.md
I'd say implement these
protected $roles = array();
public function setRoles($roles)
{
$this->roles = $roles;
}
public function addRole($role)
{
$this->roles[] = $role;
}
Then you can test using:
$mock = Mockery::mock('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->addRole(2);
$this->assertTrue(someTest($mock));
This apse gives you the opportunity to promise a format when you do a getRoles() which would be array of Role object if you do SOLID OOP, or if you rather use array, then at least you know it's always an array you get.
Spy is your friend on this:
$mock = Mockery::spy('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2;
$this->assertTrue(someTest($mock));
Did you tried to do partial mocks ?
You can try something like ( NOT TESTED ) :
$mock = Mockery::mock('User[hasRole]');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2;
$this->assertTrue(someTest($mock));
Partial Mock in Laravel 5.3
$mock = Mockery::mock(App\User::class)->makePartial();
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2;
$this->assertEquals(2, $mock->roles);
You need to use a partial mock so the rest of the eloquent model will function as normal. You're just wanting to override a certain method basically.
$this->partialMock(User::class, function ($mock) {
$mock->shouldReceive('roles')->once();
});
you can use stdclass in this case.
$obj = new stdClass();
$obj->roles = 2;
$mock = Mockery::mock('User');
// return stdclass here.
$mock->shouldReceive('hasRole')->once()->andReturn($obj);
$mock->roles = 2;
$this->assertEquals(2, $mock->roles);
Related
In the below example, I want to mock the calling of getBaseValue() inside the multipliedValue(). But I cannot figure it out.
class Sample
{
function multipliedValue()
{
$value = $this->getBaseValue();
return $value * 2;
}
function getBaseValue()
{
return 2;
}
}
I have used PHPUnit mocking, but it didn't work. So, I used the following code:
class SampleTest extends PHPUnit_Framework_TestCase
{
function testMultipliedValueIfBaseValueIsFalse()
{
$mockedObject = $this->getMockBuilder(Sample::class)
->setMethods(['multipliedValue', 'getBaseValue'])
->getMock();
$mockedObject->expects($this->any())
->method("getBaseValue")
->willReturn(false);
$result = $mockedObject->multipliedValue();
$this->assertFalse($result);
}
}
I tried to create a global function, but only force one of the method to return my desired value, the rest just go as they are. How should I approach this test?
The error I am currently getting is for the $this in the multipliedValue() method, which treats it as the stubbed object.
All the methods listed in the ->setMethods() will be stubbed and return null by default so if you only want to stub getBaseValue then do:
$mockedObject = $this->getMockBuilder(Sample::class)
->setMethods(['getBaseValue'])
->getMock();
$mockedObject->expects($this->any())
->method("getBaseValue")
->willReturn(false);
$result = $mockedObject->multipliedValue();
$this->assertFalse($result);
I'm trying to write a test for a method in the class below. However, when I run the test I get the error that get_b64 is never run? I don't see how this is not running.
I've had a little look into the mockery documentation for testing static methods, but as far as I can tell this error isn't due to that?
What do I need to change with my testing strategy or be able to mock the function call in the mocked object?
Class:
namespace App\Services\Steam;
use App\Services\Steam\Utils;
class Steam
{
public function profile(string $steamID)
{
$b64 = Utils::get_b64($steamID);
if ($b64 === null) {
throw new \App\Exceptions\InvalidSteamId();
}
return new Profile($b64);
}
}
TestCase:
public function test_create_user_object()
{
$id = "123"
$utilsMock = Mockery::mock(\App\Services\Steam\Utils::class);
$utilsMock->shouldReceive('get_b64')
->once()
->with($id)
->andReturn($id);
$steam = new \App\Services\Steam\Steam();
$steam->profile($id);
}
You call get_b64 statically, which means it is called from the class, not an object.
To mock such calls you need to use aliases:
public function test_create_user_object()
{
$id = "123"
$utilsMock = Mockery::mock('alias:\App\Services\Steam\Utils');
$utilsMock->shouldReceive('get_b64')
->once()
->with($id)
->andReturn($id);
$steam = new \App\Services\Steam\Steam();
$steam->profile($id);
}
Bear in mind that it completely replaces the Utils class, so if you have more static functions called from the class, you need to mock them as well.
I am having trouble understanding how I should go about unit testing a method which utilizes \Guzzle\Common\Event and has no return. I have a function
public function setRequest(\Guzzle\Common\Event $e) {
$e['request']->getQuery()->set('key', $this->getKey());
}
I cannot get the methods described at Guzzles mock object documentation to produce a successful test. What all needs to be mocked for this to work? getQuery() is part of the \Guzzle\Http\Message\Request I guess? What about the set()?
Edit: What I did so far is this, but I don't know if this is the correct approach. It succeeds, but it does not assert any test. I don't know that I can if the method is not returning anything.
public function testSetRequest()
{
$collection = $this->getMock('Guzzle\\Common\\Collection');
$collection->expects($this->once())
->method('set')
->will($this->returnValue(array('key' => 321)));
$request = $this->getMockBuilder('Guzzle\\Http\\Message\\Request')
->disableOriginalConstructor()
->getMock();
$request->expects($this->once())
->method('getQuery')
->will($this->returnValue($collection));
$event = $this->getMock('Guzzle\\Common\\Event');
$event->expects($this->once())
->method('offsetGet')
->with('request')
->will($this->returnValue($request));
$result = $this->place->setRequest($event);
}
I tracked set() down to the guzzle common collection. btw, $this->place simply refers to the instance of the object being tested, set in the setUp() function.
I have question about mocking object...
I have class "Example", and I need to test callMethod()
public function callMethod() {
$item = 0;
foreach($this->returnSomething() as $list) {
$item = $item + $list->sum;
}
return $item;
}
I have test method where I mock "returnSomething" to return me some data, but problem is than It doesn't call mocked method.
This is part of test method where im mocking "returnSomething" and call "callMethod".
$mock = mock("Example");
$mock->shouldReceive("returnSomething")->once()->withNoArgs()->andReturn($returnItems);
$result = $mock->callMethod();
Is it possible to call mocked "returnSomething" without changing "callMethod" definition and forwarding $mock object into that method?
I am writing this because today I found the answer here, but setMethods() is deprecated (phpunit 8.5), the alternative is onlyMethods() and it can be used as follows:
$mock = $this->getMockBuilder(Example::class)
->onlyMethods(['yourOnlyMethodYouWantToMock'])
->getMock();
$mock->method('yourOnlyMethodYouWantToMock')
->withAnyParameters()
->willReturn($yourReturnValue);
It is possible to mock only specified method.
Examples:
Mockery:
$mock = \Mockery::mock("Example[returnSomething]");
PHPUnit:
$mock = $this->getMock('Example', array('returnSomething'));
or
$mock = $this->getMockBuilder('Example')
->setMethods(array('returnSomething'))
->getMock();
With above cases a framework will mock only returnSomething method and leave the rest methods as in original object.
I have a simple use case. I want to have a setUp method which will cause my mock object to return a default value:
$this->myservice
->expects($this->any())
->method('checkUniqueness')
->will($this->returnValue(true));
But then in some tests, I want to return a different value:
$this->myservice
->expects($this->exactly(1))
->method('checkUniqueness')
->will($this->returnValue(false));
I've used GoogleMock for C++ in the past and it had "returnByDefault" or something to handle that. I couldn't figure out if this is possible in PHPUnit (there is no api documentation and the code is difficult to read through to find what I want).
Now I can't just change $this->myservice to a new mock, because in setup, I pass it into other things that need to be mocked or tested.
My only other solution is that I lose the benefit of the setup and instead have to build up all of my mocks for every test.
You could move the setUp() code into another method, which has parameters. This method gets then called from setUp(), and you may call it also from your test method, but with parameters different to the default ones.
Continue building the mock in setUp() but set the expectation separately in each test:
class FooTest extends PHPUnit_Framework_TestCase {
private $myservice;
private $foo;
public function setUp(){
$this->myService = $this->getMockBuilder('myservice')->getMock();
$this->foo = new Foo($this->myService);
}
public function testUniqueThing(){
$this->myservice
->expects($this->any())
->method('checkUniqueness')
->will($this->returnValue(true));
$this->assertEqual('baz', $this->foo->calculateTheThing());
}
public function testNonUniqueThing(){
$this->myservice
->expects($this->any())
->method('checkUniqueness')
->will($this->returnValue(false));
$this->assertEqual('bar', $this->foo->calculateTheThing());
}
}
The two expectations will not interfere with each other because PHPUnit instantiates a new instance of FooTest to run each test.
Another little trick is to pass the variable by reference. That way you can manipulate the value:
public function callApi(string $endpoint):bool
{
// some logic ...
}
public function getCurlInfo():array
{
// returns curl info about the last request
}
The above code has 2 public methods: callApi() that calls the API, and a second getCurlInfo()-method that provides information about the last request that's been done. We can mock the output of getCurlInfo() according to the arguments provided / mocked for callApi() by passing a variable as reference:
$mockedHttpCode = 0;
$this->mockedApi
->method('callApi')
->will(
// pass variable by reference:
$this->returnCallback(function () use (&$mockedHttpCode) {
$args = func_get_args();
$maps = [
['endpoint/x', true, 200],
['endpoint/y', false, 404],
['endpoint/z', false, 403],
];
foreach ($maps as $map) {
if ($args == array_slice($map, 0, count($args))) {
// change variable:
$mockedHttpCode = $map[count($args) + 1];
return $map[count($args)];
}
}
return [];
})
);
$this->mockedApi
->method('getCurlInfo')
// pass variable by reference:
->willReturn(['http_code' => &$mockedHttpCode]);
If you look closely, the returnCallback()-logic actually does the same thing as returnValueMap(), only in our case we can add a 3rd argument: the expected response code from the server.