BDD 和由外而内的方法,如何开始测试
总之,
我正在努力掌握所有由外而内的 TDD 和 BDD 内容,并希望您能帮助我获得它。
假设我需要实现配置参数功能,工作方式如下:
- 文件和数据库中有参数,
- 这两个组都必须合并为一个参数集,
- 数据库中的参数应该覆盖文件中的参数
现在我想用外部来实现这一点在方法上,我只是停留在开始处。希望你能帮助我继续前进。 我的问题是:
我应该从什么测试开始?我只是有如下内容:
class ConfigurationAssemblerTest {
@Test
public void itShouldResultWithEmptyConfigurationWhenBothSourcesAreEmpty() {
ConfigurationAssembler assembler = new ConfigurationAssembler();
// what to put here ?
Configuration config = assembler.getConfiguration();
assertTrue(config.isEmpty());
}
}
我还不知道我将以什么依赖关系结束。我还不知道如何写出所有这些东西等等。 我应该在这个测试中加入什么才能使其有效?我应该嘲笑什么吗?如果是这样,如何定义这些依赖关系?
如果你可以的话,请告诉我解决这个问题的方法,写一些计划,一些测试框架,做什么以及按什么顺序,这将是非常酷的。我知道要写很多东西,所以也许你可以给我指出一些资源?我发现的所有关于由外向内方法的资源都是关于没有依赖关系等的简单案例
。还有两个关于模拟方法的问题。
- 如果模拟是关于交互及其验证,这是否意味着此类测试中不应该有状态断言(仅模拟验证)?
- 如果我们只是为了测试而用模拟替换了还不存在的东西,那么我们稍后会用真实版本替换它吗?
提前致谢。
All,
I'm trying to grasp all the outside-in TDD and BDD stuff and would like you to help me to get it.
Let's say I need to implement Config Parameters functionality working as follows:
- there are parameters in file and in database
- both groups have to be merged into one parameters set
- parameters from database should override those from files
Now I'd like to implement this with outside-in approach, and I stuck just at the beginning. Hope you can help me to get going.
My questions are:
What test should I start with? I just have sth as follows:
class ConfigurationAssemblerTest {
@Test
public void itShouldResultWithEmptyConfigurationWhenBothSourcesAreEmpty() {
ConfigurationAssembler assembler = new ConfigurationAssembler();
// what to put here ?
Configuration config = assembler.getConfiguration();
assertTrue(config.isEmpty());
}
}
I don't know yet what dependencies I'll end with. I don't know how I'm gonna write all that stuff yet and so on.
What should I put in this test to make it valid? Should I mock something? If so how to define those dependencies?
If you could please show me the path to go with this, write some plan, some tests skeletons, what to do and in what order it'd be super-cool. I know it's a lot of writing, so maybe you can point me to any resources? All the resources about outside-in approach I've found were about simple cases with no dependencies etc.
And two questions to mocking approach.
- if mocking is about interactions and their verification, does it mean that there should not be state assertions in such tests (only mock verifications) ?
- if we replace something that doesn't exist yet with mock just for test, do we replace it later with real version?
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,这确实是很多东西。让我们从最后开始:
模拟不仅仅是“交互及其验证”,这只是故事的一半。事实上,您以两种不同的方式使用它:
检查是否进行了某个调用,并最终检查调用的参数(这是“交互和验证”部分)。
使用模拟来替换被测类 (CUT) 的依赖项,最终根据需要在模拟对象上设置返回值。在这里,您使用模拟对象将 CUT 与系统的其余部分隔离(以便您可以将 CUT 作为一个隔离的“单元”来处理,该单元在沙箱中运行)。
我将第一种形式称为动态或“基于交互”的单元测试,它使用 Mocking 框架调用验证方法。第二种是更传统的“静态”单元测试,它断言一个事实。
您不应该需要“替换尚不存在的东西”(除了从逻辑上看这是完全不可能的事实)。如果您觉得需要这样做,那么这清楚地表明您正在尝试在第一步之前进行第二步。
关于你的“由外向内方法”的概念:说实话,我以前从未听说过这个,所以它似乎不是一个非常突出的概念 - 显然不是一个很有帮助的概念,因为它似乎比澄清事情更令人困惑(至少目前如此)。
现在讨论您的第一个问题:(我应该从什么测试开始?):
首先要做的事情 - 您需要某种机制来从文件和数据库读取配置值,并且应该封装此功能在单独的帮助程序类中(除其他外,您需要一个干净的关注点分离才能有效地进行 TDD - 这通常是完全在引入 TDD/BDD 时没有得到重视)。我建议一个接口(例如
IConfigurationReader
),它有两种实现(一种用于文件内容,一种用于数据库,例如FileConfigurationReader
和DatabaseConfigurationReader
>)。在 TDD 中(不一定采用 BDD 方法),您还会有相应的测试装置。这些固定装置将涵盖诸如“如果底层数据存储不包含/无效/有效/其他特殊值会发生什么?”之类的测试用例。这就是我建议您开始的事情。只有这样 - 当读取机制运行并且您的
ConfigurationAssembler
类具有必要的依赖项时 - 您才可以开始为ConfigurationAssembler
类编写测试/实现它。然后您的测试可能如下所示(因为我是 C#/.NET 人员,所以我不知道合适的 Java 工具。所以我在这里使用伪代码):类 ConfigurationAssemblerTest {
<前><代码>@测试
公共无效itShouldResultWithEmptyConfigurationWhenBothSourcesAreEmpty(){
IConfigurationReader fileConfigMock = new [FileConfigurationReader 的模拟];
fileConfigMock.[WhenAskedForConfigValues].[ReturnEmpty];
IConfigurationReader dbConfigMock = new [DatabaseConfigurationReader 的模拟];
dbConfigMock.[WhenAskedForConfigValues].[ReturnEmpty];
ConfigurationAssembler 汇编器 = new ConfigurationAssembler(fileConfigMock, dbConfigMock);
配置config = assembler.getConfiguration();
assertTrue(config.isEmpty());
}
}
这里有两件事很重要:
这两个读取器对象通过其构造函数从外部注入到
ConfigurationAssembler
- 这种技术称为 依赖注入。这是非常有用且重要的架构原则,通常会带来更好、更清晰的架构(并且对单元测试有很大帮助,尤其是在使用模拟对象时)。现在,测试准确地断言了它所声明的内容:当底层读取机制返回空结果集时,
ConfigurationAssembler
返回(“组装”)一个空配置。由于我们使用模拟对象来提供配置值,因此测试在完全隔离的情况下运行。我们可以确定我们只测试ConfigurationAssembler
类的正确功能(即它对空值的处理),而不是其他任何东西。哦,也许您从 TDD 而不是 BDD 开始更容易,因为 BDD 只是 TDD 的一个子集,并且构建在 TDD 概念之上。因此,只有了解 TDD,您才能有效地执行(并理解)BDD。
哈!
Ok, that's indeed a lot of stuff. Let's start from the end:
Mocking is not only about 'interactions and their verification', this would be only one half of the story. In fact, you're using it in two different ways:
Checking, if a certain call was made, and eventually also checking the arguments of the call (this is the 'interactions and verification' part).
Using mocks to replace dependencies of the class-under-test (CUT), eventually setting up return values on the mock objects as required. Here, you use mock objects to isolate the CUT from the rest of the system (so that you can handle the CUT as an isolated 'unit', which sort of runs in a sandbox).
I'd call the first form dynamic or 'interaction-based' unit testing, it uses the Mocking frameworks call verification methods. The second one is more traditional, 'static' unit testing which asserts a fact.
You shouldn't ever have the need to 'replace something that doesn't exist yet' (apart from the fact that this is - logically seen - completely impossible). If you feel like you need to do this, then this is a clear indication that you're trying to make the second step before the first.
Regarding your notion of 'outside-in approach': To be honest, I've never heard of this before, so it doesn't seem to be a very prominent concept - and obviously not a very helpful one, because it seems to confuse things more than clarifying them (at least for the moment).
Now onto your first question: (What test should I start with?):
First things first - you need some mechanism to read the configuration values from file and database, and this functionality should be encapsulated in separate helper classes (you need, among other things, a clean Separation of concerns for effectively doing TDD - this usually is totally underemphasized when introducing TDD/BDD). I'd suggest an interface (e.g.
IConfigurationReader
) which has two implementations (one for the file stuff and one for the database, e.g.FileConfigurationReader
andDatabaseConfigurationReader
). In TDD (not necessarily with a BDD approach) you would also have corresponding test fixtures. These fixtures would cover test cases like 'What happens if the underlying data store contains no/invalid/valid/other special values?'. This is what I'd advice you to start with.Only then - with the reading mechanism in operation and your
ConfigurationAssembler
class having the necessary dependencies - you would start to write tests for/implement theConfigurationAssembler
class. Your test then could look like this (Because I'm a C#/.NET guy, I don't know the appropriate Java tools. So I'm using pseudo-code here):class ConfigurationAssemblerTest {
}
Two things are important here:
The two reader objects are injected to the
ConfigurationAssembler
from outside via its constructor - this technique is called Dependency Injection. It is very helpful and important architectural principle, which generally leads to a better and cleaner architecture (and greatly helps in unit testing, especially when using mock objects).The test now asserts exactly what it states: The
ConfigurationAssembler
returns ('assembles') an empty config when the underlying reading mechanisms on their part return an empty result set. And because we're using mock objects to provide the config values, the test runs in complete isolation. We can be sure that we're testing only the correct functioning of theConfigurationAssembler
class (its handling of empty values, namely), and nothing else.Oh, and maybe it's easier for you to start with TDD instead of BDD, because BDD is only a subset of TDD and builds on top of the concepts of TDD. So you can only do (and understand) BDD effectively when you know TDD.
HTH!