【问题标题】:Same project, same branch, two machines: unit tests producing inconsistent results同一个项目,同一个分支,两台机器:单元测试产生不一致的结果
【发布时间】:2020-01-25 18:57:52
【问题描述】:

我真的不确定要在此处粘贴什么代码。我在下面包含指向我的 GitHub 的链接,指向有错误的特定文件。

突然之间,以前运行良好的单元测试开始失败。这毫无意义,失败。我正在使用 Spring 的 MockMVC 实用程序来模拟 Web API 调用,我使用这个工具进行的测试主要围绕特定的 Web 逻辑,例如我的安全规则。在这些测试中,安全规则对我来说非常重要,我对所有 API 的所有访问规则都进行了单元测试。

无论如何,这个测试应该测试一个成功验证的请求,现在返回一个 401,这会导致测试失败。查看代码,我找不到任何问题。我正在传递一个有效的 API 令牌。但是,我不相信我的任何逻辑都应该受到责备。

我之所以这么说是因为我做了一个测试。两台计算机,都在我项目的开发分支上。我从两台机器上删除了我的整个 .m2,进行了干净的编译,然后运行了测试。在一台机器上,所有的测试都通过了。在另一台机器上,这个测试失败了。

这让我想到两件事中的一件正在发生。要么其中一台机器出现严重错误,要么是测试订单问题,这意味着在我的测试之间没有正确清理某些东西。

如果我只运行这一个测试文件 (mvn clean test -Dtest=VideoFileControllerTest),它可以在两台机器上运行,这一点得到了加强。

那么……会是什么?我很茫然,因为我觉得我在测试之间正确地清理了所有东西,我通常很擅长这个。建议和反馈将不胜感激。

https://github.com/craigmiller160/VideoManagerServer/blob/develop/src/test/kotlin/io/craigmiller160/videomanagerserver/controller/VideoFileControllerTest.kt

testAddVideoFile()

【问题讨论】:

    标签: java spring spring-mvc kotlin junit


    【解决方案1】:

    我已经检查了您的项目并运行了测试。虽然我无法确定失败的确切原因,但它确实看起来与某种形式的测试(数据)污染有关。

    在我通过修改 maven surefire 配置随机化顺序后,测试开始失败。我在 pom.xml 的构建部分添加了以下 sn-p 以随机化测试:

    <build>
    ...
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
            <runOrder>random</runOrder>
        </configuration>
    </plugin>
    ...
    </build>
    

    我使用以下 (linux) bash 脚本运行了 10 次 mvn clean test 命令(如果您使用 Windows,该脚本可能使用 powershell 运行):

    #!/bin/bash
    
    for i in {1..10}
    do
      mvn clean test
      if [[ "$?" -ne 0 ]] ; then # if the exit code from mvn clean install was different than 0
        echo "Error during test ${i}" >> results.txt
      else
        echo "Test ${i} went fine" >> results.txt
      fi
    done
    

    没有插件sn-p,results.txt文件只包含十行Test x went fine,而在应用插件后,大约一半的测试失败。不幸的是,当使用mvn clean test -Dtest=VideoFileControllerTest 时,随机测试都成功了,所以我猜测污染发生在代码的其他地方。

    我希望以上内容能让您对测试失败有更多的了解。我建议通过@Ignore-ing 将活动测试类减半并运行测试来寻找罪魁祸首。如果所有测试都成功,则在下半场重试此过程,并继续将活动测试减半,直到找到失败的原因。一定要包括失败的测试。

    [编辑]

    您可以在涉及的测试类/方法上添加@DirtiesContext,以防止在测试之间重复使用ApplicationContext

    【讨论】:

      【解决方案2】:

      好的,谢谢你的建议,我想通了。

      因此,我的控制器测试的主要目的是验证我的 API 逻辑,包括身份验证。这意味着存在对 SecurityContextHolder 进行静态方法调用的逻辑。我有另一个测试类,它也在测试涉及 SecurityContextHolder 的逻辑,它正在这样做:

      @Mock
          private lateinit var securityContext: SecurityContext
      
          @Before
          fun setup() {
              SecurityContextHolder.setContext(securityContext)
          }
      

      所以它设置了一个 Mockito 模拟对象作为安全上下文。经过大量调查,我发现我的所有身份验证逻辑在我的笔记本电脑上(但不是在我的台式机上)返回 401 的测试中运行良好。我还注意到上面代码 sn-p 的测试文件在我的笔记本电脑上的控制器测试之前运行,但在我的桌面上运行之后。

      此外,我对未经身份验证的调用进行了大量测试,这就是为什么只有一个测试失败的原因:随后的未经身份验证的测试清除了上下文。

      解决方案是在上面的测试文件中添加以下逻辑:

      @After
          fun after() {
              SecurityContextHolder.clearContext()
          }
      

      这清除了模拟,让一切重新开始工作。

      【讨论】:

      • 很高兴听到您发现了问题,感谢分享!
      • 是的,我讨厌找到不完整的帖子哈哈。感谢您的帮助。
      猜你喜欢
      • 2010-09-27
      • 1970-01-01
      • 1970-01-01
      • 2016-10-05
      • 2023-01-21
      • 1970-01-01
      • 2014-09-05
      • 2014-06-03
      • 2019-10-31
      相关资源
      最近更新 更多