软件实施过程中,软件测试是很重要的一部分。
关于软件测试分类
软件实施的过程
- 单元测试
集成测试
系统测试
验收测试
代码透明度
- 白盒测试
黑盒测试
灰盒测试
是否运行程序
- 静态测试:目测看代码
动态测试:debug
所以,单元测试是一种动静态皆可的白盒测试
补充:测试实施者(内测、公测、验收)
- α测试(内部人员测试,黑盒)
β测试(客户组织测试,验收阶段、黑盒)
第三方测试(请专门的安全机构测试软件的安全性)
单元测试的定位
- 测试目的:检验软件基本组成单位的正确性(方法、接口)
- 测试对象:软件的功能模块
- 测试方式:白盒测试
- 实施者:开发工程师/白盒测试工程师
单元测试实例分析
那么,在实际项目中,哪些模块(方法)需要写单元测试呢?每个被测试的模块需要写多少个测试用例呢?
强烈推荐dao层、srvice层的相对独立功能都一接口的方式声明,并在接口注释中写明模块的功能、也可以写一下实现需要注意的地方,不必写实现的细节。因此,每一个dao层、service层的接口声明的模块都需要测试,以及每一个controller接口。 有了接口,即使没有具体实现,在spock中可以直接使用接口实例化测试桩进行测试。
单元测试中,测试用例的设计通过等价类和边界值的方法设计,基本就可以满足需求。等价类就是将测试用例花费为若干个等价类,在每个等价类中至少取一个测试用例即可。边界值法是对等价类法的补充。
例如:某个被测试模块的输入为整数,将测试用例可以划分为两个等价类(-∞, 0)和[0, +∞), 所以取了测试用例:-87,125。测试之后,我们总觉得不放心,所以我们取第三个测试用例 0再测一遍,这个0就是边界值法取的测试用例。
此外,对于多元测试用例,为了使用尽量少的测试用例,可以通过正交排列和正交表选取测试用例。
java(spring)项目中怎样进行单元测试?
- 简单工具类的测试
- 写一个main方法
- groovy脚本
- 使用单元测试框架 junit/spock
- spring环境中的测试
- 使用单元测试框架 junit/spock
junit的使用
常用方法注解:
@Before
@BeforeClass
@After
@AfterClass
@Test
@Ignore
测试方法上使用@Test
注解,并且测试方法必须是public
类型的,返回值必须为void
。测试代码写在这个方法里面即可。
spock的基本使用
0. groovy环境的安装和配置
groovy环境安装配置: http://jvm123.com/2019/10/groovy-huan-jing.html
1. 依赖
<dependencies>
<!-- groovy依赖 -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.15</version>
</dependency>
<!-- spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.2-groovy-2.4</version>
<scope>test</scope>
</dependency>
<!-- spring spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.2-groovy-2.4</version>
<scope>test</scope>
</dependency>
</dependencies>
2. 继承Specification类,四类个方法和测试方法
import spock.lang.Specification
class CalculateSpec extends Specification {
// 初始化
def setupSpec() {
calculateService = new CalculateService()
println ">>>>>> setupSpec"
}
def setup() {
println ">>>>>> setup"
}
def cleanup() {
println ">>>>>> cleanup"
}
def cleanupSpec() {
println ">>>>>> cleanupSpec"
}
def "test life cycle"() {
given:
def a = 1
def b = 2
expect:
a < b
println "test method finished!"
}
}
3. 测试方法的6中基本标签语句组合形式
- given … expect …
- given … when … then …
- when … then …
- given … expect … where …
- expect … where …
- expect
4. with() 和 verifyAll()
def "test person use with(p)"() {
given: "init a person"
Date now = new Date()
Person p = new Person(name: "yawn", age: 18, birthday: now)
expect: "测试p"
with(p) {
name == "yawn"
age < 20
birthday == now
}
}
5. 多次执行被测试方法
def "多次执行测试语句"() {
given:
def a = 0
expect: "aaa"
3 * calculateService.plusPlus(a) == 3
// 执行3次后结果为 3
}
spock数据驱动测试
1. 使用数据管道
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
a << [1, 7, 0]
b << [3, 4, 0]
c << [3, 7, 0]
}
@Shared sql = Sql.newInstance("jdbc:mysql://localhost:3306/test", "com.mysql.jdbc.Driver", "root", "root")
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where: "多变量的数据管道"
[a, b, c] << sql.rows("select a, b, c from maxdata")
}
2. 使用数据表(数据管道的语法糖)
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
a | b || c
1 | 3 || 3
7 | 4 || 7
0 | 0 || 0
}
spring环境中使用spock
使用@SpringBootTest @ContextConfiguration
启动spring容器,注入bean进行测试即可。
如下:
@SpringBootTest
@ContextConfiguration
class SpringBootSpec extends Specification {
@Share
CalculateService calculateService;
def "spring boot test"() {
expect: "asas"
z == calculateService.minus(x, y)
where:
x << [9, 8, 7]
y << [6, 5, 4]
z << [3, 3, 3]
}
def "spring boot test2"() {
expect: "asas"
z == calculateService.minus(x, y)
where:
x | y | z
9 | 8 | 1
6 | 5 | 1
3 | 3 | 0
}
}
spock测试桩mock和stub的使用
mock和stub测试桩的区别
- mock测试桩多用于检测结果。
- stub测试桩多用于提供测试的条件。
stub给模块提供一个或多个返回结果(方法的返回值)
def "Stub 测试桩"() {
given: "构造测试桩"
CalculateInterface calculateService = Stub(CalculateInterface)
calculateService.plusPlus(_) >> 1
when:
int x = calculateService.plusPlus(12)
int y = calculateService.plusPlus(3)
then:
x == 1
y == 1
}
mock给被测试的模块提供一个用于校验的动作
def subscriber = Mock(Subscriber) // 1. 创建一个mock对象
def "should send messages subscriber"() {
when:
publisher.send("hello") // 2. publisher 发送一个“hello”
then:
1 * subscriber.receive("hello") // 3. subscriber 接收到一个“hello”
1 * subscriber.messageCount == 1
}
spock中的其他注解
@Ignore
- 忽略测试方法
@IgnoreRest
- 忽略其他测试方法
@Unroll
- 展开:数据驱动测试中,展开所有的测试结果,分别显示每个测试用例的测试情况
@FailsWith(ArithmeticException.class)
- 记录已经知道的 bug
- 标记让方法执行失败的测试用例
@Timeout(value = 10, unit = TimeUnit.MILLISECONDS)
- 超时就失败