BDD和从外到内的方法,如何开始测试

时间:2011-10-28 12:38:43

标签: testing tdd mocking bdd

所有

我正在尝试掌握所有外部TDD和BDD的东西,并希望你能帮我搞定。

假设我需要实现Config Parameters功能,如下所示:

  • 文件和数据库中有参数
  • 两个组必须合并为一个参数集
  • 数据库中的参数应覆盖文件中的参数

现在我想通过从外到内的方式来实现这一点,而我刚刚开始时就陷入困境。希望你能帮我走开。 我的问题是:

我应该从哪个测试开始?我只是如下:

class ConfigurationAssemblerTest {

    @Test
    public void itShouldResultWithEmptyConfigurationWhenBothSourcesAreEmpty() {
        ConfigurationAssembler assembler = new ConfigurationAssembler();            
        // what to put here ?
        Configuration config = assembler.getConfiguration();            
        assertTrue(config.isEmpty());
    }

}

我还不知道我将以什么依赖关系结束。我不知道我将如何编写所有这些东西等等。 我应该在此测试中加入什么才能使其有效?我应该嘲笑什么?如果是这样,如何定义这些依赖关系?

如果你能告诉我这条路,请写一些计划,一些测试骷髅,做什么,以及它的超酷程度。我知道这是很多写作,所以也许你可以指点我的资源?我发现的所有关于从外到内的方法的资源都是关于没有依赖性的简单案例等。

还有两个嘲弄方法的问题。

  • 如果模拟是关于交互及其验证,是否意味着在这些测试中不应该有状态断言(只有模拟验证)?
  • 如果我们用mock替换一些尚不存在的东西进行测试,我们会在以后用真实版本替换吗?

提前致谢。

1 个答案:

答案 0 :(得分:1)

好的,这确实很多东西。让我们从最后开始:

  • 模拟不仅仅是'互动及其验证',这只是故事的一半。事实上,您以两种不同的方式使用它:

    1. 检查是否进行了某次调用,并最终检查了呼叫的参数(这是“互动和验证”部分)。

    2. 使用模拟替换被测试类(CUT)的依赖关系,最终根据需要在模拟对象上设置返回值。在这里,您使用模拟对象将CUT与系统的其余部分隔离开来(这样您就可以将CUT作为一个独立的“单元”处理,这种单元在沙箱中运行)。

我将第一种形式称为动态或“基于交互”的单元测试,它使用Mocking框架调用验证方法。第二个是更传统的“静态”单元测试,它断言了一个事实。

  • 你不应该'需要'替换尚不存在的东西'(除了这个 - 逻辑上看 - 完全不可能)。如果您觉得需要这样做,那么这清楚地表明您正试图在第一步之前迈出第二步。

  • 关于你的“从外到底”的概念:说实话,我以前从未听说过这个,所以它似乎不是一个非常突出的概念 - 显然不是一个非常有用的概念,因为它似乎混淆了事情而不是澄清它们(至少暂时)。

现在回答你的第一个问题:(我应该从哪个测试开始?):

  1. 首先要做的事情 - 您需要一些机制来从文件和数据库中读取配置值,并且此功能应该封装在单独的帮助程序类中(除其他外,您需要一个干净的Separation of concerns for有效地进行TDD - 在引入TDD / BDD时,这通常完全没有得到强调。我建议使用一个接口(例如IConfigurationReader),它有两个实现(一个用于文件填充,一个用于数据库,例如FileConfigurationReaderDatabaseConfigurationReader)。在TDD(不一定采用BDD方法)中,您还可以使用相应的测试装置。这些灯具将涵盖测试案例,例如'如果基础数据存储包含无/无效/有效/其他特殊值,会发生什么?'。这就是我建议你开始的。

  2. 只有这样 - 运行中的读取机制和您的ConfigurationAssembler类具有必要的依赖关系 - 您将开始编写/实现ConfigurationAssembler类的测试。然后你的测试看起来像这样(因为我是一个C#/。NET的人,我不知道相应的Java工具。所以我在这里使用伪代码):

    class ConfigurationAssemblerTest {

    @Test
    public void itShouldResultWithEmptyConfigurationWhenBothSourcesAreEmpty() {
    
        IConfigurationReader fileConfigMock = new [Mock of FileConfigurationReader];
        fileConfigMock.[WhenAskedForConfigValues].[ReturnEmpty];
    
        IConfigurationReader dbConfigMock = new [Mock of DatabaseConfigurationReader];
        dbConfigMock.[WhenAskedForConfigValues].[ReturnEmpty];
    
        ConfigurationAssembler assembler = new ConfigurationAssembler(fileConfigMock, dbConfigMock);            
    
        Configuration config = assembler.getConfiguration();            
    
        assertTrue(config.isEmpty());
    }
    

    }

  3. 这里有两件事很重要:

    • 两个读者对象通过其构造函数从外部注入ConfigurationAssembler - 此技术称为Dependency Injection。这是一个非常有用和重要的架构原则,通常会导致更好更清洁的架构(并且在单元测试中有很大帮助,特别是在使用模拟对象时)。

    • 现在测试确切地断言它所说的内容:ConfigurationAssembler返回('汇编')空配置,当它们的基础读取机制返回空结果集时。因为我们使用模拟对象来提供配置值,所以测试完全隔离。我们可以肯定,我们只测试ConfigurationAssembler类的正确功能(它处理空值,即没有其他功能)。

    哦,也许你更容易从TDD而不是BDD开始,因为BDD只是TDD的一个子集,并且建立在TDD的概念之上。因此,当您了解TDD时,您只能有效地(并理解)BDD。

    HTH!