zoukankan      html  css  js  c++  java
  • 优秀的Java程序测试是什么样的?

    作为测试驱动设计和开发的忠实粉丝,我相信创造良好的测试是我们作为Java开发人员可以做的最重要的事情之一。我们写测试出于许多原因:

    • 塑造系统的设计。我们知道输入和输出应该是什么样的,但是我们需要创建什么对象来做到这一点呢?代码应该塑造成什么样的“形状”?编写测试可以让我们知道应该创建什么样的代码。
    • 为了确保初始和持续的正确性。让我们的应用程序如期望地那样运作并且始终如一地精确很重要。测试应该竭力确保做到这一点。
    • 文档。测试是系统的文档,因为它会说明它应该做什么以及应该怎么做。

    那么“好的测试”到底是什么样子的呢?

    给测试命名

    测试的名字至关重要,特别是从文档角度来看的话。我们应该能够大声读出测试的名字就像一组需求一样。事实上,有一个伟大的IntelliJ插件,叫Enso,它会将你的测试名转变为恰好位于每个类旁边的语句,这样你就可以明明白白地看到你在做什么。

    不要以“test”开始命名测试的名称。这是来自于JUnit初期的后遗症,当需要它执行的时候。你的Test类将在Test文件夹中,在一个最后有Test这个单词的类中。会有一个@Test的注解。我们知道这是一个测试。

    你也应该避免以“should”或“will”开头。这些都是干扰词。既然你已经为这个功能写了一个测试,那我们就知道它“should或will”工作(如果不能工作的话,那我们知道我们需要修复它)。

    将测试名称当作一个要求。 下面是一些例子

    addingNumbersWillSumValuesTogether()
    explodesOnNegativeID()
    notifiesListenersOnUpdates()

    不要害怕表达出来。如果你的测试名称确实需要很长的一串单词,那就这么做,只要它能清楚说明将发生什么事情。

    测试代码

    测试将分为3个部分:设置,操作,断言。

    设置

    对你的测试设置代码应该只与在测试中被断言的值相关。如果你有多余的设置代码,那就会搞不清楚它是什么,并且与测试不相关。

    这可以通过多种方式实现:

    • 将通用设置移动到使用@Before注解的具体设置方法。
    • 将重复的设置代码移动到辅助方法
    • 使用Maker来创建复杂的测试对象,并只设置测试中相关的值。

    我重申一下:每个测试的设置部分应该只有与最后被断言的值相关的代码。

    不好的例子:

    @Test
        public void returnsBooksWherePartialTitleMatchesInAnyCast(){
            Bookstore bookstore = new Bookstore();
            Book harryPotterOne = new Book("Harry Potter and The Philosopher Stone");
            bookstore.add(harryPotterOne);
            bookstore.add(new Book("Guardians of the Galaxy"));
            Book harryPotterTwo = new Book("The Truth about HARRY POTTER");
            bookstore.add(harryPotterTwo);
            List<Book> results = bookstore.findByTitle("RY pot");
            assertThat(results.size(), is(2));
            assertThat(results, containsInAnyOrder(harryPotterOne, harryPotterTwo));
        }

    书店的初始化发生在测试中,书本的创建也是。这让测试显得混乱不堪,让人搞不清楚发生了什么事情。

    好的例子:

    private Bookstore bookstore = new Bookstore();
    private Book aHarryPotterBook = new Book("Harry Potter and The Philosopher Stone");
    private Book anotherHarryPotterBook = new Book("The Truth about HARRY POTTER");
    private Book aBook = new Book("Guardians of the Galaxy");
    @Test
    public void returnsBooksWherePartialTitleMatchesInAnyCast(){
    	bookstore.add(aHarryPotterBook);
    	bookstore.add(aBook);
    	bookstore.add(anotherHarryPotterBook);
    	List<Book> results = bookstore.findByTitle("RY pot");
    	assertThat(results.size(), is(2));
    	assertThat(results, containsInAnyOrder(aHarryPotterBook, anotherHarryPotterBook));
    }

    初始化发生在字段中,这样在测试中发生了什么一清二楚。

    操作

    小菜一碟!最好保持到一行,你要进行测试的独立操作。有时候,你专门测试的是输出是什么,如果某些东西被多次调用,或者在某些优先操作之后调用的结果是什么,所以这不是一个硬性规定。当读取测试时,用户应该快速而轻松地能说“将这些值设置成这样,如果我执行这个操作/这些操作,那么这是预期的结果”。在上面的例子中,便是bookstore.findByTitle()方法。

    断言

    使用Hamcrest。 Hamcrest是一个很棒的库,给我们一个流畅的API用来写入测试。不会像这样的代码:

    assertEquals(results.size(), 2);
    assertTrue(results.contains(aHarryPotterBook))
    assertTrue(results.contains(anotherHarryPotterBook))

    我们可以一目了然、轻松地阅读像这样的代码:

    assertThat(results.size(), is(2));
    assertThat(results, containsInAnyOrder(aHarryPotterBook, anotherHarryPotterBook));

    这些相当简单的例子:Hamcrest有很多伟大的方法,使编写复杂测试变得很容易,并允许你创建自己的匹配器。

    当然,理想情况下,我们希望有一个独立的断言。这可以让我们知道我们正在测试什么,并说明我们的代码没有意外情况。就像这篇文章中所说的那样,这不是一个硬性的规则,因为在某些情况下,这是必要的,但如果你有这样一个的测试:

    assertThat(orderBook.bids.size(), is(4));
    assertThat(orderBook.asks.size(), is(3));
    assertThat(orderBook.bids.get(0).price, is(5200));
    assertThat(orderBook.asks.get(2).price, is(10000000));
    assertThat(orderBook.asks.get(2).isBuy, is(false));

    那么要理解测试哪里失败或哪条断言重要就变得困难多了。

    你也可以在Hamcrest中编写自定义的匹配器,因为Hamcrest可为复杂断言提供一个优雅的解决方案。如果你需要在一个循环中运行断言,或者你有大量的字段要断言,那么一个自定义的匹配器可能才是上上之选。

    一个测试的最重要的部分之一是,当它失败时,哪怕是一个5岁孩子也应该看得出什么地方出了错以及哪里错了。失败的消息一定不能含糊。关于这方面的解决方法是:

    • 如果做任何类型的对象比较,那么保证对象有一个体面的toString()消息。没有什么比<MyObject @ 142131>不匹配更糟的了。
    • 想要做的更好的话,可以对你的对象使用自定义匹配器。你可以准确地知道哪些字段未能匹配。
    • 确保明确为什么你要选择和这个值作比较。例如,如果你正在将一个字段值与数字3000比较,那么为什么是3000?你应该费力地明白这一点。显然,这个数字不是随便得来的,并且还要确保该变量的命名可以显示它的值是如何得来的。

    所有这些都应该是在一个适度的常识范围内。没有严格规定!

    欢迎加入学习交流群569772982,大家一起学习交流。

  • 相关阅读:
    JS站点
    1011 World Cup Betting (20分)
    1007 Maximum Subsequence Sum (25分)(动态规划DP)
    1006 Sign In and Sign Out (25分)
    1005 Spell It Right (20分)
    1004 Counting Leaves (30分)(DFS)
    1003 Emergency (25分)(Dijkstra算法)
    1002 A+B for Polynomials (25分)
    1001 A+B Format (20分)
    canvas
  • 原文地址:https://www.cnblogs.com/z335/p/8298477.html
Copyright © 2011-2022 走看看