【译】JUnit测试下的Given-When-Then

个人测试方面太过短板,没有形成一套良好的测试方法,测试技巧及常用JAVA,JS技术相关的测试框架使用,也很薄若。所以需要大量学习,大量实践。

最近看到一篇,关于JUnit测试下的测试技巧介绍,很不错,这里翻一下,正好理解下。

原文链接:点击这里

我们看JUnit测试,有时会觉得很奇怪。因为我们必须Mock对象和结果,这需要大量的工作要做。虽然我们现在有很多先进的工具、框架,比如Mockito,PowerMock,但是利用它们写出的代码,有时,易读性,可理解和可维护性并没有想象的那么好。

结构化我们测试Case

这里有一个简单的策略来让我们的JUnit测试变得易读,并且不会有任何的副作用。
怎么做呢,划分下我们的测试,利用简单的注释将我们的测试分为三个部分,准备,执行,和校验。可能你会觉得,一篇博客就只是为了讲这么简单的一句话,未免有些小题大作了,但是,往往我们会在日常项目中迷失于此。

下面的代码片段就是一个例子,当然例子可能比实际的项目简单。但无论如何,你可以相信,这样的划分是可以帮助我们写好测试的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* If an item is loaded from the repository, the name of that item should
* be transformed into uppercase.
*/
@Test
public void shouldReturnItemNameInUpperCase() {

//
// Given
//
Item mockedItem = new Item("it1", "Item 1", "This is item 1", 2000, true);
when(itemRepository.findById("it1")).thenReturn(mockedItem);

//
// When
//
String result = itemService.getItemNameUpperCase("it1");

//
// Then
//
verify(itemRepository, times(1)).findById("it1");
assertThat(result, is("ITEM 1"));

不同代码块的作用显而易见。不过我们还是总结下吧。

准备数据-》Given

这个部分,创建我们Mock的函数的返回值,或者我们将要测试方法的输入参数。此外,mock的方法也会在这个部分中准备。通常单元测试Case中,这个部分应该是最长,也是最复杂的。

注意:Mockito的when声明其实是given部分的,这点需要说明下,因为的确容易困惑。但,这与测试执行的准备工作有关,因此,放在这里最为合适。

执行-》When

这里一般只Call测试方法,这里标明了测试目的,因为这个部分的代码一般是最短的了。

验证-》Then

这个部分,执行环节的所有结果在这里得以声明。除此之外,也可以确认方法是否被执行。总之,主要的点,在这里进行Check。

测试Case的命名(测试方法)

早期测试方法都是以test为前缀,现在已经不怎么这样做了,一些同事喜欢用下划线去命名,我个人喜欢用驼峰命名。

当然,一个方法命名需要包含大量的信息,但可能这些信息更好的方式是放在测试代码的注释上。表明这个方法会发生什么,像shouldReturnItemNameInUpperCase ()这样,可能是个好的办法,显而易见,在项目开始前,大家统一测试方法规范是好的。

总结

这篇文章基本就结束了。再啰嗦两句,分享下个人对于测试的一点看法。为了去正确执行一个测试,有时为此准备大量的数据,是非常头疼的。尤其是如果在不同的测试Case,我们需要去Mock相同的一些方法或者数据,那么这个时候,我们做个类函数,去共享这些,这样会比较好,当然这些Mock对象因为影响着诸多的测试,所以也会越变越复杂,而且大量的测试这些对象。因此,在创建测试,Mock数据时,怎么做,如何做,我们需要权衡利弊,再三考虑。

写在最后

翻译下来,觉得通篇的确蛮简单的,但是正如作者所说,我们在实践中,却往往容易迷失。我这里也有一些自己项目上的思考、沉淀,这里抛砖引玉下。

  1. 下划线命名法
    无论是前端JS、TS项目还是后端Java项目,我们一致使用下划线命名法,前缀一般是should_xxx_when_xxx_given_xxx,这样做的好处,一是易读,二是,可以相对很长和准确的的表达测试,方法名对应我们测试Case中的then,when,given。
  2. 工具类来做到通用Mock数据
    对于复杂的数据,我们会采用JSON文件,程序反序列化来复用,当然如果大量的测试Case依赖于一份测试数据的话,确实会出现问题,比如改了这个测试数据,就会造成大面积的测试挂。这个就是个度的问题了。
    前后端,我们都会做一些builder,对测试Case提供基本的一些方法,数据轮子。
  3. Given-When-Then方法论适用于前后端测试,文章给与我们的是拆解测试方法,不要局限于JUnit这个框架,前端也通用,可能具体的技术造成写法的些许不同,但是请理解到骨子里去。