Java-021-单元测试

单元测试

@(Programming_Training)

What – 单元测试是什么

单元

我们在构建程序的时候,

都是把程序分成一个个小的模块和组件

这些模块在java里面按照层次可以对应的是:

  • 方法

意思就是测试的粒度, 一般在类和方法这样的粒度上做测试, 也就是说, 一个单元可以是一个类的一个方法

测试

顾名思义, 为了Double check我们实现的代码功能符合我们的预期

Why

Double check

类和方法的实现没有bug, 符合我们在设计时候的预期和要求

功能稳定的保证

类和方法的实现, 在代码的进化和发展过程, 会需要进行反复的修改和重构, 当我们更改代码的时候, 单元测试能够保证修改后功能不会受影响, 从而影响下层依赖这个类和方法的代码

良好的单元测试是重构的基础, 感受一下

对设计的帮助

当你在写单元测试的时候, 势必会把从一个个小的单元(或模块)来考虑程序, 也就是模块化思维, 因为如果不这样思考, 根本没办法写单元测试. 这样测试时候, 强迫自己去考虑代码的模块化

也就是江湖上流传的, 编写可测试的代码

扩展阅读
代码文档

好的测试也可以是一个很好的文档, 刚开始学习一个系统或者程序时候, 可以从其单元测试开始, 了解如何使用其代码, 输入是什么, 输出是什么, 预期的运行结果是什么

How – 如何做单元测试

到此为止, 我们大概知道单元测试的目标是什么 — 试着调用实现的类和方法, 然后确定其正确性

比如, 我们可以在main方法里写测试, 然后开发时候直接跑一下.

但是这样效率太低了, 而且当测试量大的时候, 需要结构化的组织测试代码.

Junit

https://github.com/junit-team/junit4/wiki/Getting-started

通过Java的标记, 组织测试代码, 组织成测试集. 然后每次编译之后, 运行所有的测试集.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Calculator {
public int evaluate(String expression) {
int sum = 0;
for (String summand: expression.split("\\+"))
sum += Integer.valueOf(summand);
return sum;
}
}

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class CalculatorTest {
@Test
public void evaluatesExpression() {
Calculator calculator = new Calculator();
int sum = calculator.evaluate("1+2+3");
assertEquals(6, sum);
}
}

EasyMock

http://easymock.org

当我们在写测试的时候, 我们会经常遇到一个问题就是, 我们会有很多依赖的模块, 或者依赖的内容, 比如一些我们使用的库, 查询的数据库, 采集的网络数据

如果测试中都去获取这些依赖的内容, 就会影响测试代码的可靠性和性能.

而且我们依赖的模块和内容, 维护其正确性的责任不在我们! 我们测试应该更关注我们自己代码的逻辑和实现的正确性!

比如在爬虫中, 我们采集的网页内容, 就是依赖, 如果我们测试时候, 我们都必须去爬一下内容, 那效率就特别低! 我们可以在本地存储一份用于测试的网页内容

比如数据库查询, 我们不可能在测试代码中真正去操作数据库(炒鸡危险的行为!), 所以我们需要模拟(Mock)一个假的数据对象

EasyMock就是用来解决这个问题的! Mock需要的依赖, 一般EasyMock是配合JUnit一起使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

public class JsonNewsTestableReaderTest extends EasyMockSupport {

private TextNewsTestableReader textNewsTestableReader;
private BufferedReader newsReader;

@Before
public void setup() {
newsReader = createStrictMock(BufferedReader.class);
textNewsTestableReader = new TextNewsTestableReader(newsReader);
}

@Test
public void shouldReadNews() throws Exception {
expect(newsReader.readLine()).andReturn("title");
expect(newsReader.readLine()).andReturn("\n");
expect(newsReader.readLine()).andReturn("content");

replayAll();
News news = textNewsTestableReader.readNews();
verifyAll();

assertEquals("title", news.getTitle());
assertEquals("content", news.getContent());
}
}

maven项目引入单元测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 <dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

<!-- mock -->
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>4.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>

代码链接