在TDD过程中删除单元测试

时间:2014-08-24 16:07:36

标签: java unit-testing tdd

我目前正在阅读一本关于使用testNG和Mockito进行测试的书籍,以便提高我使用该工具的技能,扩展我对测试的总体知识并提高我创建的代码质量。

第4章之后,"测试驱动开发"我应该做很多练习来加强我刚学到的关于TDD的知识,并养成循环红色测试的习惯 - >实施代码 - >绿色测试 - >重构。其中一个名为" PasswordValidator",它应该验证密码的强度。

这种开发的推荐方法之一是从最简单的测试开始,逐步增加,编写更复杂的测试用例,实现更复杂的功能。说实话,通常我会通过编写一个正确的正则表达式来解决这个问题(比如这个问题:https://stackoverflow.com/a/5142164/2576122),编写一些测试来覆盖我可能想象的任何边界情况并完成它。

在这里,我首先创建PasswordValidatorTest类,并在其中测试如下:

@Test
public void returnsTrueWhenPasswordIsAtLeastEightCharsLong() {
    assertTrue(atLeastEightCharactersLong(EIGHT_CHARACTERS_LONG_PASSWORD));
}

我检查它是否失败,在PasswordValidator中实现逻辑:

public static boolean atLeastEightCharactersLong(String passwordToValidate) {
    if (passwordToValidate.length() >= 8) {
        return true;
    } else {
        return false;
    }
}

我检查它是否为绿色,然后重构:

public static boolean atLeastEightCharactersLong(String passwordToValidate) {
    return (passwordToValidate.length() >= 8);
} 

同样适用于包含数字,特殊标志等的测试。在这样的程序的某些时候,我最终得到了最终测试:

@Test
public void shouldReturnTrueIfPasswordIsValid() {
    assertTrue(PasswordValidator.isValid(VALID_PASSWORD_EXAMPLE));
}

实施的目的是:

public static boolean isValid(String passwordToValidate) {
    return atLeastEightCharactersLong(passwordToValidate) && containsAtLeastTwoDigits(passwordToValidate) &&
            containsAtLeastOneSpecialSign(passwordToValidate);
}

然后在测试为绿色之后我可以清楚地看到,所有早期测试的方法应该从一开始就是私有方法。

是否经常发生,并且它是否被接受,在编码过程中,TDD中的某些测试在开始时是否有用?因为有一件事是肯定的 - 这些方法根本不应该暴露给那个类用户,所以回答诸如"让它们受到保护"不要诉我。

或许我的方法从一开始就无效,我应该只编写一个isValid()方法的测试来测试API的行为?如果我这样做,这样的测试会过于笼统,并且不允许我在开发过程中检查所有边界情况,或者我可能完全错了?在测试什么和不应该测试什么之间是否存在界限?什么应该是粒度?

3 个答案:

答案 0 :(得分:2)

  

或许我的方法从一开始就无效,我应该只为isValid()方法编写测试

是的,这个。

所以你写这样的东西作为起点:

public static boolean isValid(String passwordToValidate) {
    return false;
}

然后你可以写下你的第一个测试:

@Test
public void returnsTrueWhenPasswordIsAtLeastEightCharsLong() {
    assertTrue(isValid(EIGHT_CHARACTERS_LONG_PASSWORD));
}

哪个会失败:RED。

因此,您对要测试的代码进行了最简单的更改,以使其通过:

public static boolean isValid(String passwordToValidate) {
    return true;
}

然后测试将通过:GREEN。

但是你还没有完全测试8个字符的限制:

@Test
public void returnsFalseWhenPasswordIsLessThanEightCharsLong() {
    assertFalse(isValid(SEVEN_CHARACTERS_LONG_PASSWORD));
}

这将失败 - RED - 因为测试中的方法现在总是返回true。

因此,您进行了最小的更改以使所有测试通过:

public static boolean isValid(String passwordToValidate) {
    return passwordToValidate.length() >= 8;
}

你又是绿色了。

你继续这样做,添加其他测试,每次更改你正在测试的代码,尽可能少地通过测试。

您可能会发现在某个阶段重构isValid方法,将其拆分为您调用的某些私有方法很有用。这没关系:它是 refactor 步骤的一部分,由于公共接口没有改变,你的测试仍然应该通过。

答案 1 :(得分:0)

这里的关键点是你缺少关于创建代表密码的抽象的主要概念,它为你提供了起点。由于缺少该实体,您失去了焦点,并以这种方式编写了测试用例。 您想要表示一个密码,因此您应该有一个模拟该实体的类,就域驱动设计而言,它是一个值对象。一旦创建了密码,它实际上代表了它建模的概念,所以我只有两个测试,并且会在构造函数中逐步添加检查警卫

testCreatesInvalidPassword() {
// catch exception
Password.fromString("asda");
Password.fromString("asdas1");
Password.fromString("asdas1...");
}

testCreatesValidPassword() {
Password.fromString("asdasdsad");
Password.fromString("asdas123");
Password.fromString("asdas123");
}

答案 2 :(得分:0)

一件红色 - >绿色 - >您可能忽略的重构循环是重构步骤。此步骤也适用于您的测试。因此,如果你达到了测试之间重复的程度,你应该重构那些,如果这意味着删除测试,那么你应该确信不再需要那个测试。

相关问题