Sometimes there are sections of code that are difficult to test, such as accessing a database, or interacting with the user. You can stub out those sections of code, allowing the rest of the code to be tested.
Let's start with a class that prompts the user. For simplicity, it has only two methods, one that actually prompts the user (which would be used by all the other methods) and the one we are going to test, which prompts and filters out only yes and no answers. Please note this code is overly simplistic for demonstration purposes.
class Answer
{
// prompt the user and check if the answer is yes or no, anything else, return null
public function getYesNoAnswer($prompt) {
$answer = $this->readUserInput($prompt);
$answer = strtolower($answer);
if (($answer === "yes") || ($answer === "no")) {
return $answer;
} else {
return null;
}
}
// Simply prompt the user and return the answer
public function readUserInput($prompt) {
return readline($prompt);
}
}
To test getYesNoAnswer
, the readUserInput
needs to be stubbed out to mimic answers from a user.
class AnswerTest extends PHPUnit_Framework_TestCase
{
public function test_yes_no_answer() {
$stub = $this->getMockBuilder(Answer::class)
->setMethods(["readUserInput"])
->getMock();
$stub->method('readUserInput')
->will($this->onConsecutiveCalls("yes","junk"));
// stub will return "yes"
$answer = $stub->getYesNoAnswer("Student? (yes/no)");
$this->assertSame("yes",$answer);
// stub will return "junk"
$answer = $stub->getYesNoAnswer("Student? (yes/no)");
$this->assertNull($answer);
}
}
The first line of code creates the stub and it uses getMockBuilder
instead of createMock
. createMock
is a shortcut for calling getMockBuilder
with defaults. One of these defaults is to stub out all the methods. For this example, we want to test getYesNoAnswer
, so it can't be stubbed out. The getMockBuilder
invokes setMethods
to request that only readUserInput
be stubbed out.
The second line of code creates the stubbing behavior. It stubs out readUserInput
method and sets up two return values upon subsequent calls, "yes" followed by "junk".
The third and fourth lines of code test getYesNoAnswer
. The first time, the fake person responds with "yes" and the tested code correctly returns "yes", since it is a valid selection. The second time, the fake person responds with "junk" and the tested code correctly returns null.