我的junit测试看起来像一个长篇故事:
我创建了4个用户
我删除了1个用户
我尝试使用已删除的用户登录并确保它失败
我使用剩下的3个用户中的一个登录并验证我可以登录
我从一个用户向另一个用户发送消息,并验证它是否显示在发件人的发件箱中以及接收者的收件箱中.
我删除了这条消息
...
...
优点:测试非常有效(非常擅长检测错误)并且非常稳定,因为它们只使用API,如果我重构代码,那么测试也会被重构.由于我不使用"脏技巧",例如在给定状态下保存和重新加载数据库,我的测试无视模式更改和实现更改.
缺点:测试变得难以维护,测试中的任何更改都会影响其他测试.测试运行8-9分钟,这对于持续集成非常有用,但对开发人员来说有点令人沮丧.测试不能孤立运行,你可以做的最好的事情是在你感兴趣的测试运行后停止 - 但是你绝对必须运行之前的所有测试.
你会如何改进我的考试?
首先,了解您所拥有的测试是集成测试(可能访问外部系统并访问各种类).单元测试应该更加具体,这对已经构建的系统来说是一个挑战.实现这一目标的主要问题通常是代码的结构方式:
即类紧密耦合到外部系统(或其他类).为了能够这样做,您需要以这样的方式构建类,以便您可以在单元测试期间实际避免命中外部系统.
更新1:阅读以下内容,并考虑最终设计将允许您实际测试加密逻辑,而无需访问文件/数据库 - http://www.lostechies.com/blogs/gabrielschenker/archive/2009/01/30/ the-dependency-inversion-principle.aspx(不是在java中,但是很好地说明了这个问题)...还要注意你可以为读者/编写者做一个非常集中的集成测试,而不是一起测试它们.
我建议:
逐步在您的系统中包含真实的单元测试.您可以在进行更改和开发新功能时进行相应的重构.
在执行上述操作时,请在适当的位置包含重点集成测试 确保您能够运行与集成测试分开的单元测试.
考虑到您的测试接近于测试整个系统,因此与自动验收测试的不同之处仅在于它们在API的边界上运行.考虑到这一点,考虑与产品API的重要性相关的因素(如果它将在外部使用),以及您是否具有良好的自动验收测试覆盖率.这可以帮助您了解在您的系统上使用这些内容的价值,以及它们自然需要这么长时间的原因.决定是否要在接口级别或接口+ api级别上测试整个系统.
更新2:根据其他答案,我想澄清一些关于做TDD的事情.让我们说你必须检查一些给定的逻辑是否发送电子邮件,将信息记录在一个文件上,将数据保存在数据库中,并调用一个Web服务(不是我所知道的,但你开始为每个服务添加测试) .在每次测试中,您不想访问外部系统,您真正想要测试的是逻辑是否会调用您期望它执行的那些系统.因此,当您编写检查在创建用户时发送电子邮件的测试时,您测试的是逻辑是否调用依赖性来执行此操作.请注意,您可以编写这些测试和相关逻辑,而无需实际发送电子邮件的代码(然后必须访问外部系统以了解发送的内容...).这将帮助您专注于手头的任务,并帮助您获得分离的系统.它还可以简化测试发送到这些系统的内容.
现在,您正在一种方法中测试很多东西(违反每次测试一次断言).这是一件坏事,因为当这些事情发生变化时,整个测试都会失败.这导致它不能立即明显为什么测试失败以及需要修复的内容.此外,当您有意更改系统的行为时,您需要更改更多测试以对应更改的行为(即测试是脆弱的).
要知道什么样的测试是好的,有助于阅读有关BDD的更多信息:http ://dannorth.net/introducing-bdd http://techblog.daveastels.com/2005/07/05/a-new-look- at-test-driven-development/ http://jonkruger.com/blog/2008/07/25/why-behavior-driven-development-is-good/
为了改进您提到的测试,我将使用这些上下文和测试方法名称将其拆分为以下三个测试类:
创建用户帐户
在创建用户之前
用户不存在
创建用户时
用户存在
删除用户时
用户不再存在
在登录
当用户存在时
用户可以使用正确的密码登录
用户无法使用错误的密码登录
当用户不存在时
用户无法登录
发送消息
当用户发送消息时
邮件显示在发件人的发件箱中
消息显示在收件人的收件箱中
该消息不会出现在任何其他消息框中
删除邮件时
消息不再存在
您还需要提高测试速度.你应该有一个覆盖良好的单元测试套件,它可以在几秒钟内运行.如果运行测试花费的时间超过10-20秒,那么每次更改后您都会犹豫不决,并且会丢失一些运行测试的快速反馈.(如果它与数据库对话,它不是单元测试,而是系统或集成测试,它们有其用途,但速度不够快,不能连续执行.)你需要通过模拟来打破被测试类的依赖关系.或stub他们.同样根据您的描述,您的测试似乎不是孤立的,而是测试取决于先前测试引起的副作用 - 这是禁忌.好的测试是第一次.
单元测试应该 - 理想情况下 - 是独立的,并且能够以任何顺序运行.所以,我建议你:
将你的测试分解为独立的
考虑使用内存数据库作为测试的后端
考虑将每个测试或套件包装在最后回滚的事务中
对单元测试进行分析,看看时间在哪里,并专注于此
如果创建一些用户并发送一些消息需要8分钟,性能问题可能不在测试中,而这可能是系统本身性能问题的症状 - 只有您的探查器确切知道!
[告诫:我不认为这些类型的测试是'整合测试',尽管我可能属于少数; 我认为这些类型的测试是功能的单元测试,一个TDD]