我的单元测试是否违反“测试应该做一件事原则”?

时间:2017-07-31 13:38:36

标签: java unit-testing tdd

我在课堂上写了一些单元测试,最后我很困惑是否要将这些测试结合起来,因为失败的原因是相同的。

PatternBuilder类

public class PatternBuilder {    
    private static final String PATTERN_INITIALS = "#0.%s";

    String buildPatternFromScale(int scale) {
        return String.format(Locale.ENGLISH, PATTERN_INITIALS, StringUtils.repeat("0", scale));
    }
}

上述实施的单元测试

@RunWith(Parameterized.class)
public class PatternBuilderTest {

    private PatternBuilder patternBuilder;

    @Parameterized.Parameter
    public int scale;

    @Parameterized.Parameter(1)
    public String expectedPattern;

    @Before
    public void setUp() {
        patternBuilder = new PatternBuilder();
    }

    @Parameterized.Parameters(name = "buildPatternFromScale({0}) = {1}")
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][]{
                {1, "#0.0"},
                {2, "#0.00"},
                {5, "#0.00000"},
        });
    }

    @Test
    public void testShouldVerifyThatNonNullPatternIsBuilt() {
        //given

        //when
        String pattern = patternBuilder.buildPatternFromScale(scale);

        //then
        assertThat(pattern, is(notNullValue()));
    }

    @Test
    public void testShouldVerifyThatCorrectPatternOfSpecifiedScaleShouldBeCreated() {

        //when
        String pattern = patternBuilder.buildPatternFromScale(scale);

        //then
        assertThat(pattern, is(equalTo(expectedPattern)));
    }    
}

如果我将第一个测试与第二个测试结合使用,它会断言no-null-value和正确的模式字符串,那么我是否会违反'测试应该只做一件事'?

有效地,单个测试将有两个断言 -

assertThat(pattern, is(notNullValue()));
assertThat(pattern, is(equalTo(expectedPattern)));

原因 - 我正在考虑将两者结合起来,因为如果创建了null模式字符串,那么两个测试都会因单一原因而失败

4 个答案:

答案 0 :(得分:2)

  

如果我先合并,我是否会违反'测试应该只做一件事'   用第二个测试,它声明为无空值并且正确   模式字符串?

在这种情况下我不这么认为 为什么? 因为第一种测试方法:

assertThat(pattern, is(notNullValue()));

无效buildPatternFromScale()方法的任何预期行为。

此方法旨在根据收到的参数返回Pattern 唯一要检查的是。
通过编写测试方法来执行非空断言,您不会涵盖该方法的规范。您只需编写第一步即可验证它。

如果您的方法在某些情况下可能会返回null而在其他情况下却不会返回null,这可能是有意义的。
但事实并非如此。

所以我会替换:

assertThat(pattern, is(notNullValue()));
assertThat(pattern, is(equalTo(expectedPattern)));

只是:

assertThat(pattern, is(equalTo(expectedPattern)));

答案 1 :(得分:1)

鉴于您的代码:您不需要第一次测试。第二个测试确保构建器将等于的对象返回到预期的模式。

如果构建器返回null对象,那么此测试显然会失败

从这个意义上说:无论如何,进行两次不同的测试并没有增加多少价值!

换句话说:你可以开始同时进行测试,或者更准确地说:当你遵循TDD时;你可能会从第一个测试用例开始。稍后,当您实现了真正的构建器,并且“真正的”测试用例到位时 - 那么您不再需要首先检查为null的测试用例。

请注意:任何源代码 都属于费用。只要它存在,它可能被阅读并且需要被理解(修复错误或添加功能)。因此,即使单元测试也应该为您提供“投资回报”。含义:不会对测试桶添加重要值的测试可能会被删除。

根据问题的更改进行编辑。附加测试用例

@Test
public void testShouldVerifyThatNonNullPatternIsBuilt() {
    //given
    int scale = 1;

仍然是多余的。而且“更糟糕” - 请记住@Parameterized测试以某种方式工作不同而不是“普通JUnit”测试。您可以看到,使用@Parameterized运行程序,为每个数据值调用每个测试。含义:上述测试称为三次次。我们的想法是所有测试都使用数据元素。从这个意义上讲:在使用“数据值”的情况下进行测试是没有意义的。特别考虑到这个测试在这里测试其他测试已经检查的事实。

换句话说:assertThat(pattern, is(equalTo(expectedPattern))) 在模式为空时失败(除非您碰巧将 null 放入 expectedPattern

答案 2 :(得分:0)

“每个测试方法一个断言”规则的一个目的是只有测试方法的名称才能为您提供足够的信息来理解测试失败的原因。

如果您在测试中有多个断言,则需要读取失败的消息以了解测试失败的原因。

同样有多个断言,如果第一个断言失败,则不会执行其他断言 因此,为了获得可能原因的“全貌”,您需要在修复第一个断言后重新运行测试

在您的特定情况下,非null的断言与对预期结果的相等性的断言相同。如果预期非null,则空结果将始终失败。

答案 3 :(得分:0)

我想我会坚持两个单独的测试。如果这是使用TDD完成的,那么你很可能最终会进行两次测试。

虽然值得一提的是测试可以有一个单一的概念(而不是一个东西),这可能意味着在单个测试用例中有多个断言,如果所有逻辑相关的话。

如前所述,他们期待的关键是测试中的清晰度,以便快速确定失败的原因和原因,您可以通过它获得这一点,这对于测试所带来的附加值非常重要。