java程序如何测试才是优秀的

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

一、我们写测试出于许多原因:

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

二、那么问题来了:“好的测试”到底是什么样子的呢?

1. 给测试命名

(1)测试的名字至关重要,特别是从文档角度来看的话。我们应该能够大声读出测试的名字就像一组需求一样。事实上,有一个伟大的IntelliJ插件,叫Enso,它会将你的测试名转变为恰好位于每个类旁边的语句,这样你就可以明明白白地看到你在做什么。
(2)不要以“test”开始命名测试的名称。这是来自于JUnit初期的后遗症,当需要它执行的时候。你的Test类将在Test文件夹中,在一个最后有Test这个单词的类中。会有一个@Test的注解。我们知道这是一个测试。

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

在这里插入图片描述

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

addingNumbersWillSumValuesTogether()
explodesOnNegativeID()
notifiesListenersOnUpdates()

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

测试代码---->测试将分为3个部分:

1. 设置
对你的测试设置代码应该只与在测试中被断言的值相关。如果你有多余的设置代码,那就会搞不清楚它是什么,并且与测试不相关。
这可以通过多种方式实现:

1). 将通用设置移动到使用@Before注解的具体设置方法。
2). 将重复的设置代码移动到辅助方法
3). 使用Maker来创建复杂的测试对象,并只设置测试中相关的值。
4). 我重申一下:每个测试的设置部分应该只有与最后被断言的值相关的代码。

不好的例子:
@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));
}
2. 操作
独立测试操作。专门测试的是输出,比如某些东西被多次调用,或者在某些优先操作之后调用的结果是什么,这不是一个硬性规定。当读取测试时,用户应该快速而轻松地能说“将这些值设置成这样,若我执行这个操作或者那些操作,那么这是预期的结果会什么样”。在上面的例子中,便是bookstore.findByTitle()方法。
3. 断言。
使用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));
四、要理解测试哪里失败或哪条断言重要就变得困难多了。
  1. 你也可以在Hamcrest中编写自定义的匹配器,因为Hamcrest可为复杂断言提供一个优雅的解决方案。如果你需要在一个循环中运行断言,或者你有大量的字段要断言,那么一个自定义的匹配器可能才是上上之选。

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

  3. 如果做任何类型的对象比较,那么保证对象有一个体面的toString()消息。没有什么比<MyObject @ 142131>不匹配更糟的了。

  4. 想要做的更好的话,可以对你的对象使用自定义匹配器。你可以准确地知道哪些字段未能匹配。
    确保明确为什么你要选择和这个值作比较。例如,如果你正在将一个字段值与数字3000比较,那么为什么是3000?你应该费力地明白这一点。显然,这个数字不是随便得来的,并且还要确保该变量的命名可以显示它的值是如何得来的。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皮皮攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值