目录
一、定义
二、Stub(桩)和Mock名词解释
三、实操环境搭建
四、Demo实操
五、SpringBoot实操
一、定义
(1)spock是一款基于Groovy语言的单元测试框架,其基础也是Java的Junit。
(2)Mockito是一个模拟测试框架,可以用简洁的接口写出漂亮的单元测试。Mockito可以让单元测试易于可读,产生简洁的校验错误。
(3)TDD测试驱动开发要求我们先写单元测试,再写实现代码。在写单元测试用到Mockito类似的框架来完成资源、对象的模拟,避免由于各种依赖的关系导致的阻碍。
(4)如果你没有用过groovy,它的代码结构很清晰,即使你不懂代码的也可以看懂的。通过given、and、when、then, 可以看到它遵循了BDD(Behavior Driven Development)模式。
(5)以下是Spock和Junit的对比图和代码结构映射图
二、Stub(桩)和Mock名词解释
(1)Stub也叫桩,最早出现在集成测试中,从上到下集成时,作为下方程序的代替。就像需要你的时候你就像“桩”一样出现举手就行。
(2)Mock有模拟的意思,可以理解为像一个傀儡的虚拟程序,可以按照测试者的逻辑返回被测对象想要获取的信息,要返回什么对象就返回什么对象。
(3)区别
--:Stub关注是状态,为了验证SUT(被测系统)调用目标系统后的状态;而Mock关注交互行为,为了验证SUT(被测系统)调用目标系统接口的交互行为。
--:粗粒度的测试,例如集成测试可以使用Stub,Mock适合单元测试,运行速度快,测试覆盖率高
三、实操环境搭建
1.maven配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>spock-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<springboot.version>1.5.10.RELEASE</springboot.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--springboot + web-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- aop + 日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--若是Junit4.x以下,则jmockit依赖需要在junit4之前-->
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.30</version>
<scope>test</scope>
</dependency>
<!-- Junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--spock-->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.2-groovy-2.4</version>
</dependency>
<!-- Spock需要的groovy依赖 -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.15</version>
</dependency>
<!-- spring集成spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.2-groovy-2.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>1.5.10.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.5.10.RELEASE</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>xmlbeans</artifactId>
<groupId>org.apache.xmlbeans</groupId>
</exclusion>
<exclusion>
<artifactId>json-path</artifactId>
<groupId>com.jayway.jsonpath</groupId>
</exclusion>
</exclusions>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.使用工具
Idea + maven3.6.3 + JDK1.8
四、Demo实操
1.创建第一个Spock单元测试类
将Spock的单测放于src/test/groovy文件夹下,且以“*Spec”命名
2.DemoSpec.groovy代码
package com.fong
import spock.lang.Specification
import spock.lang.Unroll
/**
* @author yzfang*
* @date 2021/8/6 - 10:54
*/
class DemoSpec extends Specification{
//增强方法: setupSpec()方法在setup()方法前运行,cleanupSpec()在cleanup()方法后运行
//before setup
def setupSpec() {
println ("setupSpec");
}
//setup
def setup() {
println ("setup");
}
//cleanup
def cleanup() {
println ("cleanup");
}
//after cleanup
def cleanupSpec() {
println ("cleanupSpec");
}
//测试方法
def "testMethod"() {
println "hello world"
expect:"期望结果"
1<2
}
def "method1"() {
expect:"期望结果"
Math.max(1, 3) == 3
Math.max(7, 3) == 7
Math.max(0, 0) == 0
}
def "method2"() {
given:"准备变量"
def a = 1
def b = 2
expect:"期望结果"
a < b
}
def "method3"() {
given:"准备变量"
def num1 = 10
def num2 = 20
when:"调用"
def res1 = Math.abs(num1);
def res2 = -Math.abs(num2);
then:"期望结果"
res1 == 10
res2 == -20
}
def "method4"() {
given:"准备变量"
String str = "aaabbbcd"
when:"调用方法"
def res1 = str.contains(str1)
def res2 = str.contains(str2)
then:"期望结果"
res1 == true
res2 == true
where:"批量测试数据"
str1 | str2
"aaa" | "bbb"
"ab" | "cd"
}
//控制台可以看到更加具体的报错信息,显示第几组数据测试失败
@Unroll
def "method5"(){
given:"准备变量"
String str="aaabbbcd"
when:"调用方法"
def res1=str.contains(str1)
def res2=str.contains(str2)
then:"期望结果"
res1==true
res2==true
where:
str1 |str2
"aaa" |"bbb"
"ab" |"bc"
"ccdf" |"g"
}
}
五、SpringBoot实操
1.SpringBootSpec.groovy
package com.fong
import com.fong.model.pojo.User
import com.fong.service.UserService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.annotation.Rollback
import org.springframework.transaction.annotation.Transactional
import spock.lang.Specification
import spock.lang.Unroll
/**
* @author yzfang
* @date 2021/8/6 - 15:55
*/
@SpringBootTest
class SpringBootSpec extends Specification{
@Autowired
UserService userService
@Unroll
@Transactional
@Rollback
def "method6"() {
given:
User user = new User()
user.setId(id);
user.setUsername(username)
user.setPassword(password)
userService.insertUser(user)
when:
User res = userService.getUserById(id);
then:
res.getId() == id
res.getUsername() == username
res.getPassword() == password
where:
id << [1, 2, 3]
username << ["张三", "李四", "赵五"]
password << ["123", "123", "234"]
}
//类似于这种写法
//id |username |password
//1 |"张三" |"123"
//2 |"李四" |"123"
//3 |"赵五" |"234"
}
2.UserController.java
package com.fong.controller;
import com.fong.common.ApiRestResponse;
import com.fong.exception.SpockException;
import com.fong.exception.SpockExceptionEnum;
import com.fong.model.pojo.User;
import com.fong.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author yzfang
* @date 2021/8/6 - 14:49
*/
@Controller
public class UserController {
@Autowired
UserService userService;
@GetMapping("/user/get")
@ResponseBody
public User getUserByid(@RequestParam("id") Integer id) {
return userService.getUserById(id);
}
@PostMapping("/user/insert")
@ResponseBody
public ApiRestResponse register(@RequestParam("username") String username,
@RequestParam("password") String password) throws SpockException {
if (StringUtils.isEmpty(username)) {
return ApiRestResponse.error(SpockExceptionEnum.NEED_USER_NAME);
}
if (StringUtils.isEmpty(password)) {
return ApiRestResponse.error(SpockExceptionEnum.NEED_PASSWORD);
}
User user = new User();
user.setUsername(username);
user.setPassword(password);
userService.insertUser(user);
return ApiRestResponse.success();
}
}
3.UserMapper.java
@Repository
public interface UserMapper {
User getUserById(Integer id);
void insertUser(User user);
}
4.User.java
package com.fong.model.pojo;
public class User {
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}