Blackbox或whitebox单元测试?

时间:2015-03-16 19:28:51

标签: unit-testing

假设我有这样的代码:

class SomeEntity
{
    // ... some properties and methods here

    public function saveAndChangeState()
    {
        $result = $this->save(); // method for saving object somehow

        $this->changeState(); // changes state of object - e.g., sets some properties e.t.c.

        return $result;
    }

    public function save() { /* ... */ }

    public function changeState() { /* ... */ }
}

我可以为这种方法编写两种单元测试。

白盒:

class SomeEntityTest extends TestCase 
{
    public function testSaveAndChangeState()
    {
        $expected = true;

        $SomeEntity = $this->getMock('SomeEntity', [
            // replace only these methods
            'save', 
            'changeState',
        ]);
        $SomeEntity->expects($this->once()) // should be called once
            ->method('save')
            ->willReturn($expected); // stub implementation will return $expected
        $SomeEntity->expects($this->once())
            ->method('changeState');

        $actual = $SomeEntity->saveAndChangeState();    

        $this->assertEquals($expected, $actual);
    }
}

和blackbox:

class SomeEntityTest extends TestCase 
{
    public function testSaveAndChangeState()
    {
        $SomeEntity = new SomeEntity();

        $result = $SomeEntity->saveAndChangeState();    

        // here come assertions about state - that I can see a persisted object, 
        // some properties of $SomeEntity have changed e.t.c.
    }
}

如果我选择whitebox:

  • testSaveAndChangeState()会更短
  • 如果我不恰当地更改了testSaveAndChangeState()的实施,
  • changeState()会失败 - 所以我的单元测试不会提醒我saveAndChangeState()工作不正确

如果我选择blackbox:

  • testSaveAndChangeState()会更加困难,因为我需要检查save()changeState()
  • 所触及的应用的每个部分 如果我更改了testSaveAndChangeState()的执行情况,
  • saveAndChangeState()会提醒我changeState()的问题
  • 我将有大量的代码重复 - 因为在testSaveAndChangeState()我将主要重复来自save()changeState()
  • 的测试的断言

我的问题是 - 我应该选择什么?

2 个答案:

答案 0 :(得分:0)

您应该使用whitebox,并为save()和changeState()添加测试。 我曾经认为blackbox是要走的路,但是现在我使用whitebox,因为当我更改代码并且测试失败时,测试会告诉我该方法如何工作并帮助我记住我需要考虑的所有情况帐户。

答案 1 :(得分:0)

最佳做法是分离您的业务问题并测试对象之间的交互。

将域对象(实体)与域交互者(实体服务)分开,并将域交互者与数据持久性(数据库)等外部问题分开。

然后你可以针对Mock对象进行黑盒测试,并确保你的EntityService正确调用Data Persistence层。

如果你保持代码高度耦合,并将业务逻辑与检索/持久性/验证/其他问题结合起来,那么很难测试你的系统,很难发现bug并且很难改变。

样本单元测试伪代码

[TestMethod]
public void MyEntityService_SaveEntity_DataAccessSaveIsCalled()
{
    var inMemoryDatabase = new FakeDatabase(); 
    var service = new MyEntityService(inMemoryDatabase);
    var sampleEntity = new MyEntity { Name = "sampleObject" };

    service.Add(sampleEntity);

    Assert.IsTrue(inMemoryDatabase.LastSavedEntity == sampleEntity); 
}