New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Creating a partial mock with all the methods mocked but one #4652
Comments
This is the first time I hear about this approach and my initial reaction is: it does not make sense to me. To me, the "unit under test" is not the method, but the object on which it is called. Also note that |
Thank you for your input. It would be interesting to know if this point of view is largely accepted by object-oriented developers, and especially by mockists. Just to be clear, I was thinking about something like this: /**
* Returns a partial mock object for the specified class
* with all of its methods mocked but one.
*/
protected function createExclusivePartialMock(string $originalClassName, string $method): MockObject
{
$classMethods = get_class_methods($originalClassName);
if (!in_array($method, $classMethods)) {
throw new Exception(
sprintf(
'The method "%s" does not exist in the class "%s"',
$method,
$originalClassName
)
);
}
unset($classMethods[array_search($method, $classMethods)]);
return $this->createPartialMock($originalClassName, $classMethods);
} |
I would suggest you put into that |
This is a common pattern if you are working with bad/legacy code to get a harness around a particular unit which may not be "the object". It seems a shame to remove |
Looking for some guidance on best practices going forward. We want to test the response of a single method in a class. The class constructor is a mess, so we don't want to instantiate it. Refactoring that code is not an option right now. Previously, we used the soon-to-be-removed $ExampleClass= $this->getMockBuilder('\ExampleClass')
->disableOriginalConstructor()
->setMethodsExcept(['keepThisMethod'])
->getMock(); Besides being sloppy, are there reasons not to call $ExampleClass= $this->getMockBuilder('\ExampleClass')
->disableOriginalConstructor()
->onlyMethods([])
->getMock(); Another solution might be to retrieve all class methods, then remove the methods to keep: $methods = get_class_methods(ExampleClass::class);
$methods = array_diff( $methods, ['keepThisMethod']);
$ExampleClass= $this->getMockBuilder('\ExampleClass')
->disableOriginalConstructor()
->onlyMethods($methods)
->getMock(); The last example isn't too verbose, and seems like the best choice. |
Coming back to my own question 18 months later while refactoring tests for PHPUnit 10, our solution was not to create mock at all, but rather to use a PHP's Based on the above example code, we ended up with this: $ref = new \ReflectionClass('\ExampleClass');
$ExampleClass = $ref->newInstanceWithoutConstructor(); This avoids the instantiation side-effects present in |
In my company, we are asked by our manager and tech leaders to write a unit test as follows (simplified example):
The idea is to be as restrictive as possible by mocking everything that can be mocked, including the class under test to begin with, and all its methods except the one being called. I guess this can be seen as some sort of pure white box testing.
I was wondering if this is common practice or not.
If it is the case, we could expect a method in the
TestCase
class to create a partial mock with all the methods mocked but one; something likecreateExclusivePartialMock
or whatever that would callsetMethodsExcept
instead ofsetMethods
(or achieve the same result with the newonlyMethods)
. Yet, S. Bergmann wrote somewhere that the MockBuilder API was more of an internal API that developers shouldn't rely on (not annotated as such, though), as opposed to its wrappers fromTestCase
— namelycreateMock
andcreatePartialMock
.What do you think? Am I missing something? Are we doing things wrong? Would such a dedicated method be actually a good solution?
(My deepest apologies if the subject has already been discussed. If that is the case, I did not find it in the issues.)
The text was updated successfully, but these errors were encountered: