简单的存根

有时,有一些代码难以测试,例如访问数据库或与用户交互。你可以删除代码部分,以便测试其余代码。

让我们从一个提示用户的类开始。为简单起见,它只有两个方法,一个实际上提示用户(将由所有其他方法使用)和我们要测试的方法,它提示和过滤掉只有是和没有答案。请注意,此代码过于简单化以用于演示目的。

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);
    }

}

要测试 getYesNoAnswer,需要删除 readUserInput 以模仿用户的答案。

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);

    }

}

第一行代码创建存根,它使用 getMockBuilder 而不是 createMockcreateMock 是使用默认值调用 getMockBuilder 的快捷方式。其中一个默认设置是删除所有方法。对于这个例子,我们想测试 getYesNoAnswer,所以它不能被删除。getMockBuilder 调用 setMethods 来请求只删除 readUserInput

第二行代码创建了存根行为。它会截断 readUserInput 方法并在后续调用时设置两个返回值,yes 后跟 junk

第三行和第四行代码测试 getYesNoAnswer。第一次,假人回答并且测试的代码正确地返回,因为它是有效的选择。第二次,假人响应垃圾,测试的代码正确返回 null。