-
-
Save mvriel/3981617 to your computer and use it in GitHub Desktop.
Class IpRange | |
{ | |
public function setStartingAddress($startingAddress) | |
{ | |
InputValidator::assertValidAddress($startingAddress); | |
$this->startingAddress = (int) $startingAddress; | |
return $this; | |
} | |
} |
Clarification; by stubbing I mean having a non-functional mock with which you can measure whether a method of it is called, see: http://www.phpunit.de/manual/3.0/en/mock-objects.html
Well, you test the behaviors you expect; it's just that one of those behaviors is encapsulated in the static method call. That said, you'd likely need a shared data provider for your individual test cases to ensure that the various behaviors are matched to inputs.
It'd be no different than if you used extension to encapsulate the assertion method.
I think you are overstating the value of a mock here. YAGNI, right? With PHP 5.4 I would have used a trait...
Let's say I ditch the "staticness" of the InputValidator class... without dependency injection, how would I be able to check if the method was invoked?
Keep in mind that the implementation that the snippet is taken from is from the Modbus protocol, so it is fixed. Allowing dependency injection for the validator class, just for the sake of being able to mock it, and see if it is called seems like a bad way to go too.
Practical vs academical.
Hmm.. I generally test my abstract classes individually and then in child classes I stub the child class and only stub the expected abstract calls.
Example:
class Super
{
protected function assertTrue()
{
...
}
}
class Child extends Super
{
public function valid()
{
return $this->assertTrue();
}
}
I would test the above using the following test:
class ChildTestCase
{
function testValid()
{
$fixture = $this->getMock('Child', array('assertTrue'));
$fixture->expects($this->once())->method('assertTrue')->will($this->returnValue(true));
$this->assertTrue($fixture->valid());
}
function testValidWithInvalidInput()
{
$fixture = $this->getMock('Child', array('assertTrue'));
$fixture->expects($this->once())->method('assertTrue')->will($this->returnValue(false));
$this->assertFalse($fixture->valid());
}
}
This test case only tests the actual behaviour of valid; I consider the behaviour of the assertTrue()
to be unimportant to my unit test since that is governed by my abstract class (which is tested in a single location).
I think you are overstating the value of a mock here. YAGNI, right? With PHP 5.4 I would have used a trait...
I don't see how a trait would make any difference; a trait adds the same testing issues as a static, global function, singleton or any other piece of global state
Let's say I ditch the "staticness" of the InputValidator class... without dependency injection, how would I be able to check if the method was invoked?
That depends on how you make your validation methods visible; when using extension you can Stub your fixture as shown in the previous example. As a trait? You can't. Traits are effectively global state as well. SOLID has Dependency Inversion as a principle because it allows for things like testability.
Extension is not an option in my case, given multiple inheritance is (unfortunately) not a language feature of PHP.
The above method
setStartingAddress
is impossible to accurately test due to the static call to assertValidAddress.Try to follow along:
WAIT: let's repeat:
test whether all rainy day scenario's of the assertValidAddress method
This is the unit test contents for that method; why should I even WANT to test that here again? Can't we trust that the
InputValidator::assertValidAddress
does what it is supposed to do? Doesn't this lead to duplication in every location I use that assertion?You do not want to test this; you want to assert that the assertValidAddress method of the InputValidator is called. How are you going to do that?
You can't. It is a static.
Conclusion: you either