Spock+Idea入门(一)

目录
一、定义
二、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的对比图和代码结构映射图
image-20210808184257480.png

二、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”命名


image-20210808190106171.png

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 + '\'' +
                '}';
    }
}

5.运行结果

image-20210808191112761.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容