如何在 BDD 下使用 case 语句进行有效的单元测试?
我对单元测试相对较新,并且当我有 case 语句时,我的测试代码的数量使我的生产代码相形见绌,其中重复对外部函数的某些调用,但具有不同的参数和随后不同的结果,我对此感到担忧。在我的问题之后是一些名为calculateDeltas 的公共函数的javascript 伪代码(如果格式不正确,敬请原谅)。在下面的代码中,populateDeltas 是一个私有函数,我通过对calculateDeltas 的测试间接测试它。
我正在努力解决的问题如下:
1.我正在以 4 种不同的方式测试calculateDeltas以覆盖4个不同的分支。
2. 通过这种方式,我有效地测试了 populateDeltas 3 次。 populateDeltas 的每个测试都有一些断言,并且这些测试实际上用不同的参数重复了 3 次。
3. 我的一个想法是单独测试 populateDeltas。在这种情况下,我现在不是正在测试实现吗?更重要的是,如果我不将它作为一个整体进行测试,我怎么知道整个函数calculateDeltas 是否有效?
4. 即使这是一个好主意,它如何简化我对calculateDeltas 的测试?我想我只是以某种方式验证 populateDeltas 被调用了。
5. 在这种情况下,我如何验证calculateDeltas的输出是否是我在调用它的上下文中实际期望的?
6. 换句话说,如果不对私有函数运行集成测试,我如何知道calculateDeltas 是否有效?
7. 这是否违背了BDD的精神,即保护外部行为,而不是内部行为,并且populateDeltas代表内部行为,而calculateDeltas代表外部行为?
感谢您的任何见解,我有点迷失了。代码如下:
function calculateDeltas(){
var deltas = {status: this.rowStatus};
switch(this.rowStatus){
case 'new':
this.populateDeltas(deltas, 'new');
return deltas;
case 'changed':
deltas.key = this.key();
this.populateDeltas(deltas, 'changed');
return deltas;
case 'unchanged':
deltas.key = this.key();
this.populateDeltas(deltas, 'unchanged');
return deltas;
case 'deleted':
deltas.status = 'deleted';
return deltas;
}
I'm relatively new to unit testing, and have become concerned by the fact that the quantity of my test code is dwarfing my production code when I have case statements where certain calls to external functions are repeated but with different arguments and subsequently different outcomes. After my questions is some javascript pseudocode (apologies if this is formatted incorrectly) for a public function called calculateDeltas. In the code below, populateDeltas is a private function that I am testing indirectly through the test for calculateDeltas.
The problems I'm struggling with are the following:
1. I'm testing calculateDeltas in 4 different ways to cover the 4 different branches.
2. In this way, I'm effectively testing populateDeltas 3 times. There are a handful of assertions for each test of populateDeltas and these tests are effectively being repeated 3 times with different arguments.
3. One thought I had was to test populateDeltas in isolation. In that case aren't I now testing implementation and, more importantly, how would I ever know the whole function calculateDeltas works if I'm never testing it as one whole unit?
4. Even if this is a good idea how does that simplify my test for calculateDeltas? I presume I would just verify somehow that populateDeltas was called.
5. In that case, how am I verifying that the output of calculateDeltas is what I actually expect in the context it was called?
6. In other words, how do I know calculateDeltas works without running integration tests on private functions?
7. Doesn't this all violate the spirit of BDD which is to guard external, as opposed, to internal behavior and populateDeltas represents internal behavior, while calculateDeltas represents external behavior?
Thanks for any insight, I'm a bit lost. Code follows:
function calculateDeltas(){
var deltas = {status: this.rowStatus};
switch(this.rowStatus){
case 'new':
this.populateDeltas(deltas, 'new');
return deltas;
case 'changed':
deltas.key = this.key();
this.populateDeltas(deltas, 'changed');
return deltas;
case 'unchanged':
deltas.key = this.key();
this.populateDeltas(deltas, 'unchanged');
return deltas;
case 'deleted':
deltas.status = 'deleted';
return deltas;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
编写多个测试来覆盖所有场景并没有什么问题。
如果您担心过度测试核心逻辑,则 case 语句的一种方法是用策略模式替换条件逻辑。在这种情况下,我将有一个类来处理每个场景的逻辑,然后将它们放入查找表中。您最终得到的代码只是根据行状态执行处理程序。您可以轻松地测试此逻辑一次,然后为每个处理程序编写特定的测试。
There's nothing wrong with writing several tests to cover all scenarios.
If your concern is over testing the core logic, one approach for case statements is to replace conditional logic with a strategy pattern. In this scenario, I would have a class to handle the logic for each scenario and then put them in a lookup table. You end up with code that just executes the handler based on row status. You can easily test this logic once and then write specific tests for each handler.
这常常令人困惑。
需要注意的是,private 并不是一个基本工具。公共职能很好。真的。 Python 开发者对没有什么是私有的感到非常满意。我们这里都是成年人了。
虽然 BDD 专注于外部行为,但您做出了创建私有共享函数的实现决定。
您仍然可以执行 BDD 并测试这个私有的共享函数。只需测试所有四种情况即可。外部接口依赖于该内部接口。
如果你想测试内部结构,那没问题。这不是 BDD,这只是一个好主意。
好的想法是有可能超出 BDD 的最低基线的。您可以添加到BDD,因为您在此处选择了实现。
这是一个相当愚蠢的问题。想太多了。只需测试外部接口即可。如果外部函数使用内部私有函数,则外部测试将在正常事件过程中测试内部函数。
This is often confusing.
It's important to note that private is not an essential tool. Public functions are fine. Really. Python folks are perfectly happy with nothing being private. We're all adults here.
While BDD is focused on external behavior, you made an implementation decision to create a private, shared function.
You can still do BDD and test this private, shared function. Just test all four cases. The external interfaces depend on this internal interface.
If you want to test the internals, that's okay. It's not BDD, it's just a good idea.
It's possible to have good ideas above and beyond the minimal baseline of good ideas that is BDD. You can add to BDD because of your implementation choice here.
This is a fairly silly question. Too much thinking. Just test the external interface. If the external function uses an internal, private function, then the external test will test the internal function in the normal course of events.