Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPUnit: Mock methods of existing object

Tags:

PHPUnit's getMock($classname, $mockmethods) creates a new object based on the given class name and lets me change/test the behavior of the methods I specified.

I long for something different; it's changing the behavior of methods of an existing object - without constructing a new object.

Is that possible? If yes, how?


When thinking about the problem I came to the conclusion that it would be possible by serializing the object, changing the serialized string to let the object be instance of a new class that extends the old class plus the mocked methods. I'd like some code for that - or maybe there is such code already somewhere.


While it would certainly be possible to create the to-be-mocked object again, it's just too complicated to do it in my test. Thus I don't want to do that if I don't really really really have to. It's a TYPO3 TSFE instance, and setting that up once in the bootstrapping process is hard enough already.

like image 389
cweiske Avatar asked Aug 17 '11 06:08

cweiske


People also ask

How to mock method in PHPUnit?

PHPUnit provides methods that are used to automatically create objects that will replace the original object in our test. createMock($type) and getMockBuilder($type) methods are used to create mock object. The createMock method immediately returns a mock object of the specified type.

What is stub in PHPUnit?

Stub. Stubs are used with query like methods - methods that return things, but it's not important if they're actually called. $stub = $this->createMock(SomeClass::class); $stub->method('getSomething') ->willReturn('foo'); $sut->action($stub);

Should be called exactly 1 times but called 0 times mockery?

Mockery is by default a stubbing library, not a mocking one (which is confusing because of its name). That means that ->shouldReceive(...) by default is "zero or more times". When using ->once(), you say it should be called zero or one time, but not more. This means it'll always pass.


1 Answers

I know this answer is rather late, but I feel for future viewers of this question there now exists a simpler solution (which has some drawbacks, but depending on your needs can be much easier to implement). Mockery has support for mocking pre-existing objects with what they call a "proxied partial mock." They say that this is for classes with final methods, but it can be used in this case (although the docs do caution that it should be a "last resort").

$existingObjectMock = \Mockery::mock($existingObject); $existingObjectMock->shouldReceive('someAction')->andReturn('foobar'); 

It acts by creating a proxy object which hands all method calls and attribute gets/sets to the existing object unless they are mocked.

It should be noted, though, that the proxy suffers from the obvious issue of failing any typechecks or typehints. But this can be usually avoided, because the $existingObject can still be passed around. You should only use the $existingObjectMock when you need the mock capabilities.

like image 143
Bailey Parker Avatar answered Oct 11 '22 20:10

Bailey Parker