【问题标题】:SonarQube issue "Add at least one assertion to this test case" for unit test with assertions?对于带有断言的单元测试,SonarQube 问题“向此测试用例添加至少一个断言”?
【发布时间】:2016-12-10 09:17:25
【问题描述】:

我在使用 SonarQube 时遇到问题,导致我的几个单元测试出现问题,提示以下问题:

向这个测试用例添加至少一个断言。

每个测试用例都类似于这种格式(其中许多断言被委托给具有通用断言的方法,以避免重复):

@Test
public void companyNameOneTooLong() throws Exception {
    AddressFormBean formBean = getValidBean();
    formBean.setCompanyNameOne("123456789012345678901234567890123456");

    assertViolation(validator.validate(formBean), "companyNameOne", "length must be between 0 and 35");
}

private void assertViolation(Set<ConstraintViolation<AddressFormBean>> violations, String fieldname, String message) {
    assertThat(violations, hasSize(1));
    assertEquals(fieldname, violations.iterator().next().getPropertyPath().iterator().next().getName());
    assertEquals(message, violations.iterator().next().getMessage());
}

现在,显然我可以将三个断言从私有方法中提取出来并将它们放入测试方法中——但我正在多次执行相同的检查(在不同的字段上)。

所以,我想我会尝试通过(重新)抛出 AssertionError 来模拟断言方法的行为:

private void assertViolation(Set<ConstraintViolation<AddressFormBean>> violations, String fieldname, String message) throws AssertionError {
    try {
        assertThat(violations, hasSize(1));
        assertEquals(fieldname, violations.iterator().next().getPropertyPath().iterator().next().getName());
        assertEquals(message, violations.iterator().next().getMessage());
    } catch (AssertionError e) {
        throw e;
    }
 }

不幸的是,这种方法也不起作用。

JUnit 断言方法有什么特别之处/SonarQube 专门寻找什么来检查是否为每个测试做出了断言?

或者 - 是否有其他方法可以实现相同的最终结果(避免一遍又一遍地复制共享断言代码)?

【问题讨论】:

  • 也许你可以重写你的方法来返回一个Matcher,这样你就会有assertThat(violations, hasViolation(field, message))
  • @DidierL 有趣的想法 - 不知道为什么。您想发表您的想法作为答案吗?
  • 我不习惯实现匹配器,但这并不能完全回答您关于 Sonar 正在寻找什么的问题......
  • @DidierL 不用担心。无论如何,我已经更新了我的问题,以便在可能的答案中提供更大的灵活性。

标签: java unit-testing junit sonarqube


【解决方案1】:

SonarQube Java Analyzer 中的规则 S2699(测试应包括断言)不执行跨过程分析,而仅探索被标识为测试方法的方法主体(通常用 @987654323 注释@)。

因此,如果在执行测试方法时将调用的唯一断言是由专用方法完成的(以避免重复),那么该规则将引发问题。这是该规则的一个已知限制,只有当我们能够有效地执行跨过程分析时,我们才会处理它。

关于 SonarQube 在此类案例中提出的问题,您可以放心地将其标记为 Won't Fix

关于检测到的断言,该规则将来自以下(单元测试)框架的常用assert/fail/verify/expect 方法视为断言:

  • JUnit
  • 节日(1.x 和 2.x)
  • 断言J
  • 哈姆克雷斯特
  • Mockito
  • 春天
  • EasyMock

【讨论】:

  • 是否应该将消息改写为“将至少一个受支持框架中的断言添加到此测试用例。”以使其更直观?
  • 使用 SonarQube 5.6;那么我是否可以期望 org.junit.Assert.assertNotNull(x) 后跟 assertEquals("ack", x.getText) 可以? SonarQube 抱怨 x.getText() 中的 x 可能为空。
  • @RichardSitze 您的问题与此问题没有直接关系。请在我们的 ML 中打开一个新线程,并指定您的 SonarJava 插件的版本:groups.google.com/forum/#!forum/sonarqube
  • @Michael-SonarSourceTeam 我们看到的结果似乎表明分析器现在正在 OP 的场景中执行跨程序分析,但是无法通过谷歌找到任何东西来确认这一点。你能提供更新吗?
【解决方案2】:

如果您不希望从测试中抛出任何异常,这可能是一种解决方法:

@Test(expected = Test.None.class /* no exception expected */)

或者,您可以抑制测试方法/测试类的警告:

@SuppressWarnings("squid:S2699")

【讨论】:

  • 我认为注释是复数“SuppressWarnings”(由于最少 6 个字符的规则,无法对其进行编辑)
【解决方案3】:

有时你不需要任何代码或断言,例如加载spring boot的上下文成功的测试。在这种情况下,当您不希望从测试中抛出任何异常时,为了防止 Sonar 问题,您可以使用这部分代码:

@Test
void contextLoads() {
    Assertions.assertDoesNotThrow(this::doNotThrowException);
}

private void doNotThrowException(){
    //This method will never throw exception
}

【讨论】:

    猜你喜欢
    • 2018-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-06
    • 2018-04-19
    • 2010-12-30
    相关资源
    最近更新 更多