我的单元测试是否违反"test should do one thing principle"?



我在我的类周围写了一些单元测试,最后我很困惑是否合并这些测试,因为它们失败的原因是一样的。

模式生成器类

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)));
}    
}

如果我将第一个测试与第二个测试结合起来,以至于它断言无空值和正确的模式字符串,我会违反"测试应该只做一件事"吗?

实际上,一个单一的测试将具有以下两个断言-

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

原因- 我正在考虑将它们结合起来,因为如果创建了空模式字符串,那么它将由于一个原因而使两个测试失败

如果我

先结合,我会违反"测试应该只做一件事"吗 用第二个测试,使其断言无空值和正确 模式字符串 ?

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

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

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

此方法旨在根据Pattern作为参数接收的内容返回它。
要检查的一件事就是这一点。
通过编写测试方法来执行非 null 断言,您不会涵盖该方法的规范。您只需编写第一步来验证它。

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

所以我会替换:

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

只需 :

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

给定你的代码:你不需要第一次测试。第二个测试确保生成器返回的对象等于预期模式。

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

从这个意义上说:无论如何,进行两个不同的测试都不会增加太多价值!

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

请记住:任何一行源代码对你来说类似于成本。只要它存在,它就可能被阅读并需要理解(以修复错误或添加功能(。因此,即使是单元测试也应该为您提供"投资回报"。含义:不会为测试存储桶增加重要价值的测试可能会被删除。

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

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

仍然是多余的。"更糟" - 请记住,@Parameterized测试的工作方式与">普通 JUnit"测试有所不同。您会看到,在@Parameterized运行器就位的情况下,将针对每个数据值调用每个测试。含义:上述测试被调用三次。这个想法是所有测试都使用数据元素。从这个意义上说:在那里有一个不使用"数据值"的测试是没有意义的。特别是考虑到这里的测试测试了其他测试已经检查的内容。

换句话说:当模式为 null 时,assertThat(pattern, is(equalTo(expectedPattern)))失败(除非您碰巧将null放入预期的模式中(。

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

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

同样对于多个断言,如果第一个断言失败,则不会执行其他断言。
因此,为了"全面了解"可能的原因,您需要在修复第一个断言后重新运行测试

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

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

尽管值得一提的是,测试可以具有单个概念(而不是事物(,这可能意味着单个测试用例中的多个断言,如果所有逻辑相关的话。

如前所述,他们期待的关键是测试的清晰度,以快速识别失败的内容和原因,您拥有它的方式为您提供了这一点,这对于测试的附加值很重要。

相关内容

最新更新