我不能使用伪数据,因为我的主要功能是测试是与黑匣子远程服务器的连接,该服务器仅从第一个子模块获取数据。
在这种情况下,可以强制执行测试的执行顺序还是不好的做法?我感觉此设置中有异味,但找不到更好的方法。
edit:问题出在如何构建测试,其中一个测试是另一个测试的设置?因为“上一个”测试不是安装程序,而是测试执行安装程序的代码。
#1 楼
我无法使用伪数据,因为我正在测试的主要功能是与黑匣子远程服务器的连接,该服务器仅从第一个子模块获取数据。
这是我的关键部分。您可以谈论“单元测试”,它们“彼此独立运行”,但是听起来它们都依赖于此远程服务器,并且依赖于“第一个子模块”。因此,一切听起来都紧密耦合,并取决于外部状态。因此,您实际上是在编写集成测试。按特定顺序运行这些测试是很正常的,因为它们高度依赖于外部因素。有序的测试运行,如果出现问题,可以选择提前退出测试,这对于集成测试是完全可以接受的。
但是,也有必要重新审视结构您的应用程序。能够模拟出第一个子模块和外部服务器,可能会允许您为所有其他子模块编写真正的单元测试。
评论
更不用说某些测试必须专门检查当远程服务器不可用时是否发生了预期的行为。
–亚历山大
18年4月4日在8:56
或者,也许您实际上是打算编写集成测试,因此模拟数据并不能达到您要通过这些测试要实现的目标。
– Guy Schalnat
18年4月4日在11:51
问题很可能是Junit名称中包含“ unit”的意思。
–索比昂·拉文·安德森(ThorbjørnRavn Andersen)
18年4月5日在8:48
@ThorbjørnRavnAndersen确实如此。人们自然而然地编写集成测试,而不是编写单元测试,因为集成测试比“真实的”单元测试更有用,编写起来也更容易。但是,由于流行的测试框架是以单元测试的概念命名的,因此该术语已被采用,并且在现代术语中意味着“任何自动化测试”。
–梅森·惠勒
18年4月5日在21:24
@MasonWheeler甚至由非技术经理选拔来表示验收测试。
– StackOverthrow
18年4月6日,0:02
#2 楼
是的,这是一个不好的做法。通常,单元测试旨在测试单个代码单元(例如,基于已知状态的单个函数)。
当您要测试可能在野外发生的一系列事件时,您需要一种不同的测试样式,例如集成测试。如果您依赖第三方服务,则更是如此。
要对此类内容进行单元测试,您需要找出一种注入伪数据的方法,例如,实现一个数据服务接口,该接口镜像Web请求,但从本地伪数据文件返回已知数据。
评论
同意我认为这种混淆源于以下事实:许多人都认为集成测试必须是端到端的,并使用“单元测试”来指代仅测试一层的任何测试。
–噬菌体
18年4月3日在13:56
@autophage:绝对同意。事实上,我非常同意它,尽管我同意这是一个陷阱,但我经常发现自己陷入了同一陷阱trap
–轨道轻赛
18年4月3日在16:16
#3 楼
您建议的强制执行顺序只有在第一次失败后也中止测试运行时才有意义。在第一次失败时中止测试运行意味着每个测试运行只能发现一个问题,并且在解决所有先前的问题之前,您无法找到新问题。如果第一个运行的测试发现需要花费一个月的时间才能解决的问题,则在该月内实际上将不会执行任何测试。
如果您不中止第一次运行的测试,则强制执行命令不会给您带来任何好处,因为无论如何,每个失败的测试都需要进行调查。即使只是为了确认查询子模块上的测试失败,也因为数据生成子模块上也已确定失败。
我能提供的最佳建议是编写测试,以便易于识别依赖项中的故障何时导致测试失败。
#4 楼
您所指的气味是对测试应用了错误的约束和规则集。单元测试经常与“自动测试”或“程序员的自动测试”混淆。
单元测试必须小,独立且快速。
有些人错误地将其理解为“程序员编写的自动化测试必须是小独立且快速的”。但这只是意味着,如果您的测试不是小规模,独立且快速的,则它们不是单元测试,因此,某些单元测试规则不应,不能或不能应用于您的测试。一个简单的例子:您应该在每次构建后运行单元测试,对于速度不快的自动化测试,则不应这样做。
虽然您的测试不是单元测试,这意味着您可以跳过一条规则,并且允许测试之间有一些相互依存关系,但是您还发现,您可能还缺少其他规则,因此需要重新引入-另一个问题的范围。
#5 楼
如上所述,您正在运行的似乎是一个集成测试,但是您声明:例如,一个子模块正在生成数据,而另一个正在运行
查询数据。如果生成数据的子模块包含错误,则即使子模块本身可以正常工作,查询子模块的测试也会失败。
可能是开始重构的好地方。对数据运行查询的模块不应依赖于第一个(数据生成)模块的具体实现。相反,最好插入一个包含获取该数据的方法的接口,然后可以将其模拟出来以测试查询。
例如
如果您有:
class Queries {
int GetTheNumber() {
var dataModule = new Submodule1();
var data = dataModule.GetData();
return ... run some query on data
}
}
而不是首选:
interface DataModule {
Data GetData();
}
class Queries {
IDataModule _dataModule;
ctor(IDataModule dataModule) {
_dataModule = dataModule;
}
int GetTheNumber() {
var data = _dataModule.GetData();
return ... run some query on data
}
}
这消除了查询对数据源的依赖性,并允许您为特定情况设置易于重复的单元测试。
#6 楼
其他答案提到排序测试是不好的(在大多数情况下是正确的),但是在测试执行时强制执行排序有一个很好的理由:确保慢速测试(即集成测试)在更快的测试(测试)之后运行不依赖其他外部资源)。这样可以确保您更快地执行更多测试,从而可以加快反馈循环。评论
我更倾向于研究为什么某个单元测试运行缓慢而不是强制执行命令。单元测试应该是快速的。
–罗比·迪(Robbie Dee)
18年4月4日在14:27
OP说他无法在其中一些测试中使用虚拟数据。这意味着某种数据库命中率会降低所有测试的速度(甚至包括某些自然而应该快速运行的真实单元测试)。如果他有其他不需要数据库命中的测试,则它们的运行速度将比需要磁盘或网络命中的任何东西快一个数量级。
–迈克·霍勒(Mike Holler)
18-4-4在15:46
我认为你们俩都是对的。 Robbie是对的,因为单元测试应该小而又快速并且与依赖关系隔离开,因此顺序无关紧要,并且随机排序通常通过加强这种独立性来鼓励更好的设计。 Mike是对的,首先运行更快的测试对集成测试非常非常好。就像上面的答案一样,部分问题是单元测试与集成测试的术语。
– WillC
18年4月4日在23:39
@MikeHoller然后,它们不是单元测试。关于什么是单元测试,确实应该没有混淆。
–罗比·迪(Robbie Dee)
18年4月5日在7:35
@RobbieDee我只是在使用OP使用的术语。我了解这些不是真正的单元测试。如果您想争用术语,请使用OP进行介绍。 (因此,为什么我在先前的评论中用“真实的单元测试”进行了澄清”)
–迈克·霍勒(Mike Holler)
18-4-5在15:31
评论
可能重复如何在一个测试是另一测试的设置的情况下构造测试?如果要测试到远程服务器的连接,那么按照定义,它们不是单元测试。
第一个答案在这里使我感到困惑,因为您在标题中说“这是不好的做法吗?”在您的问题摘要中,您写道“可以吗?”任何回答是或否的人都会混淆其中之一!
听起来您正在创建一组集成测试。即使是这样,一个测试也不应该依赖其他测试。
如果顺序很重要,那么您可能做错了。