2

我对 Java 开发相当陌生,今天是我第一次配对程序来创建测试用例。他们很友善地大声说出他们的想法,以解释为什么要完成某些事情(例如,创建带有参数的构造函数,而不仅仅是使用默认构造函数等)。

虽然我可能会保留我今天看到/听到的内容的 40%,但我想知道是否有一组步骤/指南可以遵循,最终结果是一个测试用例,这将是一个很好的起点。我知道通常的反应

  1. 每个店铺都有自己的标准,
  2. 有个人喜好和工具,
  3. 取决于测试是针对您拥有的课程还是针对传统课程等。

无论哪种方式,我都想通了,并询问了 SO 成员。

以下是我记下的一些笔记的片段,以显示我的想法,就回应而言。

  1. 使用 Mockito,您无法模拟使用方法创建的对象,因此请尝试获取构造函数来获取这些对象。这将允许您模拟它们,然后将它们传递给方法以便于测试。
  2. Mockito 不处理静态方法,但您可以创建一个调用静态方法的包装类。现在,这个新的包装类不使用任何静态方法,您的测试类将使用它而不是静态方法。这现在允许您使用 when/thenReturn 而无需直接处理静态方法。
  3. 模拟时,在类级别声明将由所有测试用例使用的变量。然后在 setUp(); 中模拟这些类;这种方法可确保常用的模拟始终是已知状态,并且不会被先前的测试修改。
  4. 说的还不够,使用好的变量名(例如 listWithOneCar、listWithNoCars 等)
  5. 由于测试用例有时基于真值表,因此请尝试将其包含在测试方法名称中(例如,ensureCanDrive_Car_0_withValidKey_0()、ensureCanDrive_Car_1_withValidKey_1() 等)。如果您通过快速视觉扫描捕获了大量测试用例,这应该有助于您进行视觉扫描。完成后,只需添加边缘案例。
  6. 'verify' 使用不足,请尝试在您的测试用例中使用更多。
  7. 单个测试用例应该只包含测试用例之间不同的代码,并且没有重复的代码。这使得更容易理解和维护/扩展。

你明白了。这些只是来自我的笔记,但我想知道是否有人有一个进化的列表?

4

2 回答 2

1

You have very different things in your list. And even for one of the topics a 'complete' list is probably not really possible. Most would make up a book, possible multiples. So here is my list of books (and other documentation) that make together a somewhat complete list.

  • Some seem to be about general good coding practices. For example the recommendation to use dependency injection via constructor (#1); The importance of good names (#4). It is hard to make a complete list about those, but there are some important books. I recommend

    • Clean Code by Robert C. Martin

    • The Pragmatic Programmer by Andrew Hunt and David Thomas

    • Effective Java by Joshua Bloch

  • Some are about the limitations of Mockito, just go through the documentation and some blog posts about it. You might also want to look at PowerMock, which tries to fixes some (most?) technical limitations of Mockito and other mocking frameworks. Just reading what they did will improve your understanding of Mockitos limitations.

  • Some are about designing tests, making code testable and so on. You might consider the following books:

    • xUnit Test Patterns: Refactoring Test Code by Gerard Meszaros

    • Test Driven: TDD and Acceptance TDD for Java Developers by Lasse Koskela

    • Test Driven Development: By Example by Kent Beck

于 2013-04-18T04:12:39.200 回答
1

您专注于单元测试的一个方面,即模拟。当您不能(或不想)想要测试该代码的一小部分时,您可以模拟 - 例如数据库查询或日志记录实用程序。

根据我的经验,单元测试有两种形式:集成和单元测试。

最重要的是,无论哪种类型的单元测试,您都必须了解:

  • 快乐路径上的方法的行为是什么,以及
  • 边缘情况下的行为是什么。

在编写单元测试时,我将使用 Mockito 之类的模拟库,但我不仅仅将自己排除在此之外。有时使用 Mockito 不适合您想要执行的测试(100 次中有 99 次在集成测试中。不要那样做。),但当我只想模仿来自用于快乐路径或边缘案例测试的重型 bean/object/DAO。

我对测试的偏好是将它们分成三个不同的部分:

  • 我的给定,包括设置要测试的方法,包括模拟时的行为,
  • 我的“当我这样做时”子句,它本质上只是执行该方法,并且
  • 我的验证步骤,这涉及我测试结果的一个方面。

例如,假设我想测试此方法的行为。

public List<String> shortenNamesFromDatabase(final int maxLength) {
    List<String> names = dao.executeQuery("SELECT name from dbNames");
    List<String> result = new ArrayList<String>();
    for(String name : names) {
        result.add(name.substring(0, maxLength);
    }
    return result;
}

我调用了数据库和一些转换逻辑。我不想费心验证这一层的数据库信息,所以我将它模拟出来。在集成测试中,我确定对数据库进行了调用,这会从数据库中获取每个名称。

//happy path test!
@Test
public void shortenNamesFromDatabase_namesAreOnly3CharactersLong() {
    //given
    final int length = 3;
    List<String> dbResult = Arrays.asList("Alpha","Beta", "Gamma", "Del", "Zeta12345");
    Dao daoMock = mock(Dao.class);
    when(daoMock.executeQuery("SELECT name from dbNames")).thenReturn(dbResult);
    SomeClass testObj = new SomeClass();

    //when
    List<String> result = testObj.shortenNamesFromDatabase(length);

    //then
    for(String name : result) {
        assertTrue("Name was too long!", length <= name.length());
    }
}

假设现在我想测试一些极端情况的行为。如果我将最大长度设置为零,我会期待一大堆空字符串。不理想,但我最好确保边缘情况下的行为是我所期望的。

//edge-case-why-would-you-ever-do-this-for-real test
@Test
public void shortenNamesFromDatabase_zeroLengthStringsTransformed() {
    //given
    final int length = 0;
    List<String> dbResult = Arrays.asList("Alpha","Beta", "Gamma", "Del", "Zeta12345");
    Dao daoMock = mock(Dao.class);
    when(daoMock.executeQuery("SELECT name from dbNames")).thenReturn(dbResult);
    SomeClass testObj = new SomeClass();

    //when
    List<String> result = testObj.shortenNamesFromDatabase(length);

    //then
    for(String name : result) {
        assertTrue("Name was WAY too long!", length <= name.length());
    }
}

注意测试没有真正改变吗?这是一件好事。使用单元测试,您一次只应该真正断言或验证一件事。对测试一个或两个边缘案例进行重大更改可能是代码异味。

编写测试时,请注意以下几点:

  • 代码异味:如果您的代码难以进行单元测试,您应该考虑重构。 这将使测试工作更容易,并且您的代码更易于维护。

  • 不要假装高兴。只模拟你不需要验证的东西。 我不需要验证数据库中的任何内容;不是查询的长度,不是我得到了我想要的确切名称,任何东西 - 所以我嘲笑它。我确实需要验证的是转换是否适当地进行了。

  • 遵循有关测试驱动开发、缺陷驱动测试和普通单元测试的常见做法。 维基百科是一个很好的起点,但也有关于该主题的书籍和闪存卡。

于 2013-04-18T04:24:24.683 回答