Reputation: 71
My question is about unit testing. Assume we have the class below;
class X
{
public function p1(){
//logic
$a = $this->p2();
//more logic
}
public function p2(){
//even more logic
}
}
When writing a unit test for p1 method, should I mock p2 method?
What I am thinking is that, the test that is written for p1 method should only execute and test the p1 method not p2. But in order to realize that I should get a mock of Class X and call p1 method on that mock instance like below.
$xMock = $this->getMockBuilder('\X')
->setMethods(array('p2'))
->getMock();
$xMock->expects($this->any())
->method('p2')
->will($this->returnValue($value));
$resultTobeAsserted = $xMock->p1();
Unfortunately doing that feels a little wrongish to me. I discussed the topic with my colleagues and it boiled down to how you define your SUT(system under test). If a tester considers the particular method that is being tested as the SUT, then other methods that are called from the SUT would seem as dependencies and naturally tester will want to mock them. On the other hand if tester considers the whole class as the SUT, then those method calls will become part of the test so there won't be any reason to mock them.
Is that conclusion correct? Which kind of thinking would yield more robust unit tests?
Upvotes: 6
Views: 2109
Reputation: 38961
When writing a unit test for p1 method, should I mock p2 method?
No.
You are calling a method on a class and you expects that things happen.
With mocking p2 you make exceptions on the implementation details on the class.
Unfortunately doing that feels a little wrongish to me.
I say the felling it spot on.
Which kind of thinking would yield more robust unit tests?
If you test the observable behaviors of a class you make sure that class still does what it was supposed to do when you change the implementation of the class. Thats robustness.
if you test one method and mock out part of the implementation of that method (the internal method call) then you test a specif implementation and if the test fails you don't know if the external behavior changed.
I've written about this in some more detail in:
The UNIT in unit testing.
which details a couple more points about why i think it's important to test behaviors and not methods.
In short
Unit testing, in PHP, is about testing the observable behaviors of a class!
Behaviors:
Test those things. Disregard implementation details.
Upvotes: 3
Reputation: 11364
Your thinking is correct. If you want test method p1
you don't care about p2
because you assume that it's been tested in another test. So, you can just mock/stub p2
. But you have to remember that you should mock/stub ONLY p2
. So you example should look more like:
$xMock = $this->getMockBuilder('\X')
->setMethods(array('p2'))
->getMock();
$xMock->expects($this->any())
->method('p2')
->will($this->returnValue($value));
$resultTobeAsserted = $xMock->p1();
Upvotes: 1
Reputation: 24551
If a tester considers the particular method that is being tested as the SUT, then other methods that are called from the SUT would seem as dependencies and naturally tester will want to mock them.
If there are arguments that support this separation, the same arguments can be used to refactor the class into two classes and this is exactly what you should to then.
Upvotes: 2