PHPUnit袖珍指南 第十一章 残根

 
第十一章残根
相比有多个错误来源的测试,只测试一件事情的测试提供了更好的信息。如何隔离测试的外部影响呢?用来自简单的PHP对象的残根替代昂贵, 杂乱, 不可靠, 缓慢, 复杂的资源。例如,为了单项测试的目的,你可以通过返回常数的方法来代表实际上很复杂的计算。
 
残根很好的解决了分配昂贵的外部资源的问题。例如,共享资源,在测试之间可以使用PHPUnit2 _ Extensions_TestSetup共享数据库连接,但是根本不使用为了测试目的的数据库更好。
 
改善设计是使用残根的一项效果。各种被使用的资源通过一个简单的面来访问,这样,用残根很容易实现资源替换。例如,不要在代码中到处进行数据库调用,而是实现一个简单的Idatabase接口。然后,就可以创建一个残根,实现Idatabase接口,用它来服务测试。甚而可以创建一个选项,选择使用数据库残根还是真正的数据库,这样测试既可以作为开发时的本地测试,也可以和真实的数据库进行集成测试。
 
需要形成残根的功能倾向于聚在一个对象中,以改进内聚性。用一个简单,内聚的接口来向外展示功能,你可以减少和系统其余部分的偶合性。
 
11-1.自分流
有时需要检查对象是否被正确地调用,你可以创建对象的完整残根用于调用,但那可能使它不便于检查调用的结果是否正确。一种更加简单的方案是使用自分流模式,把测试用例自身作为残根。术语自分流是从医学实践借用的术语,它指在动脉和静脉之间安装导管,从动脉引出血液并返回静脉,这可以注射药物。
 
Here is an example: suppose we want to test that the correct method is called on an object that observes another object. First, we make our test-case class an implementor of Observer:
以下是个例子:假设我们想要测试观察其它对象的对象是否被正确方法调用。首先,实现测试用例的Observer接口:
 
class ObserverTest extends PHPUnit2_Framework_TestCase
implements Observer{
}
 
其次, 我们实现Observer的方法update(),检查当它观察的对象的状态改变时,update()是否被调用:
 
public $wasCalled = FALSE;
public function update(Subject $subject) {
        $this->wasCalled = TRUE;
}
 
现在,可以写测试了。创建一个新的Subject对象,将测试对象附带在对象上作为观察员。但Subject的状态改变时,如调用doSomething()方法,Subject对象就会调用所有注册为观察员的update()方法。通过实现update(),我们用$wasCalled变量来检查Subject是否做了它应该做的事。
 
public function testUpdate( ) {
        $subject = new Subject;
        $subject->attach($this);
        $subject->doSomething( );
        $this->assertTrue($this->wasCalled);
}
 
注意, 我们创建一个新的Subject的对象而不是依靠一个全局变量。残根模式鼓励这样的设计,它可以对象的偶合性,提高重用。
 
如果不熟悉自分流模式,测试可能难以阅读。这在做什么?为什么测试用例也是一个观察员?但是当你习惯了这些,测试就很容易阅读,所以你需要了解的测试都在一个类里。
--------------------------------------------------------------------------------------------------------------------
原文:
Chapter 11. Stubs
Tests that only test one thing are more informative than tests in which failure can come from many sources. How can you isolate your tests from external influences? Simply put, by replacing the expensive, messy, unreliable, slow, complicated resources with stubs made from plain PHP objects. For example, you can implement what is in reality a complicated computation by returning a constant, at least for the purposes of a single test.
 
Stubs solve the problem of allocating expensive external resources. For example, sharing a resource, such as a database connection, between tests by using the PHPUnit2_ Extensions_TestSetup decorator helps, but not using the database for the purposes of the tests at all is even better.
 
Design improvement is one effect of using stubs. Widely used resources are accessed through a single façade, so you can easily replace the resource with the stub. For example, instead of having direct database calls scattered throughout the code, you have a single Database objectan implementor of the IDatabase interface. Then, you can create a stub implementation of IDatabase and use it for your tests. You can even create an option for running the tests with the stub database or the real database, so you can use your tests for both local testing during development and integration testing with the real database.
 
Functionality that needs to be stubbed out tends to cluster in the same object, improving cohesion. By presenting the functionality with a single, coherent interface, you reduce the coupling with the rest of the system.
 
11-1. Self-Shunting
Sometimes you need to check that an object has been called correctly. You can create a complete stub of the object to be called, but that can make it inconvenient to check for correct results. A simpler solution is to apply the self-shunt pattern and use the test-case object itself as a stub. The term selfshunting is taken from the medical practice of installing a tube that takes blood from an artery and returns it to a vein to provide a convenient place for injecting drugs.
 
Here is an example: suppose we want to test that the correct method is called on an object that observes another object. First, we make our test-case class an implementor of Observer:
 
    class ObserverTest extends PHPUnit2_Framework_TestCase
    implements Observer{
    }
 
 
 
Next, we implement the one Observer method, update( ), to check that it is called when the state of the observed Subject object changes:
 
    public $wasCalled = FALSE;
 
    public function update(Subject $subject) {
       $this->wasCalled = TRUE;
    }
 
 
 
Now, we can write our test. We create a new Subject object and attach the test object to it as an observer. When the state of the Subject changesfor instance, by calling its doSomething( ) methodthe Subject object has to call the update( ) method on all objects that are registered as observers. We use the $wasCalled instance variable that is set by our implementation of update( ) to check whether the Subject object does what it is supposed to do:
 
    public function testUpdate( ) {
       $subject = new Subject;
       $subject->attach($this);
       $subject->doSomething( );
       $this->assertTrue($this->wasCalled);
    }
 
 
 
Notice that we create a new Subject object instead of relying on a global instance. Stubbing encourages this style of design. It reduces the coupling between objects and improves reuse.
 
If you are not familiar with the self-shunt pattern, the tests can be hard to read. What is going on here? Why is a test case also an observer? But once you get used to the idiom, the tests are easy to read. Everything you need to understand a test is in one class.
 
阅读更多
换一批

没有更多推荐了,返回首页