35

一連のテストをSimpleTestからPHPUnitに移行しようとしていますが、SimpleTestの部分的なモックに相当するものがあるかどうか疑問に思いました。

この機能が利用可能であることを示唆するドキュメントには何も見つからないようですが、サブクラスを使用できることに気づきました。これは良い考えですか、それとも悪い考えですか?

class StuffDoer {
    protected function doesLongRunningThing() {
        sleep(10);
        return "stuff";
    }
    public function doStuff() {
        return $this->doesLongRunningThing();
    }
}

class StuffDoerTest {
    protected function doesLongRunningThing() {
        return "test stuff";
    }
}

class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
    public function testStuffDoer() {
        $sd = new StuffDoerTest();
        $result = $sd->doStuff();
        $this->assertEquals($result, "test stuff");
    }
}
4

5 に答える 5

55

リンクされたページを読むと、SimpleTest の部分モックは、一部のメソッドのみがオーバーライドされているモックのようです。これが正しければ、その機能は通常の PHPUnit モックによって処理されます。

の中でPHPUnit_Framework_TestCase、あなたはでモックを作成します

$mock = $this->getMock('Class_To_Mock');

これにより、すべてのメソッドが何もせずに null を返すモック インスタンスが作成されます。一部のメソッドのみをオーバーライドしたい場合、 の 2 番目のパラメータgetMockはオーバーライドするメソッドの配列です。

$mock = $this->getMock('Class_To_Mock', array('insert', 'update'));

関数と関数を削除Class_To_Mockした のモック インスタンスを作成し、戻り値を指定できるようにします。insertupdate

この情報はphpunit docsにあります。

この回答は、5.4 以降の PHPUnit バージョンの最新のコード例を示しています。

于 2009-07-27T05:48:25.693 に答える
3

I don't think PHPUnit supports partial mocks for the system under test. If you're trying to isolate methods then I'm sure your implementation works - I've done that too.

However, I try to avoid doing this, for a couple of reasons.

First, it couples your test very tightly to the internal implementation of the class. Do you really care whether a method called doesLongRunningThing was called, or is it more important that the "LongRunningThing" got done?

Second, when I run into this it always makes me wonder whether I've got one class doing the job of two. An extract class refactoring might be in order. The testing becomes much easier if doesLongRunningThing() becomes its own class, even with a single method.

I believe the solution is to inject the services your SUT depends on (http://en.wikipedia.org/wiki/Dependency_injection). This also makes the DoesLongRunningThing implementation more testable.

Without jumping into interfaces, here's what I would do:

class DoesLongRunningThing {
    public function execute() {
        sleep(10);
        return "stuff";
    }
}

class StuffDoer {
    protected $doesLongRunningThing;

    public function setLongRunningThinger(DoesLongRunningThing $obj) {
        $this->doesLongRunningThing = $obj;
    }

    public function doStuff() {
        return $this->doesLongRunningThing->execute();
    }
}

Now it's easy to mock:

class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
    public function testStuffDoer() {
        $dlrtMock = $this->getMock('DoesLongRunningThing');
        $dlrtMock->expects($this->any())->will($this->returnValue("test stuff"));

        $sd = new StuffDoer();
        $sd->setLongRunningThinger($dlrtMock);
        $result = $sd->doStuff();
        $this->assertEquals($result, "test stuff");
    }
}
于 2011-03-10T18:20:13.623 に答える