单元测试基础与java中使用spock总结

标签:

本文出自jvm123.com-java技术分享站:http://jvm123.com/2019/10/unit-testing-spock.html

软件实施过程中,软件测试是很重要的一部分。

关于软件测试分类

软件实施的过程

  • 单元测试
    集成测试
    系统测试
    验收测试

代码透明度

  • 白盒测试
    黑盒测试
    灰盒测试

是否运行程序

  • 静态测试:目测看代码
    动态测试:debug

所以,单元测试是一种动静态皆可的白盒测试

补充:测试实施者(内测、公测、验收)

  • α测试(内部人员测试,黑盒)
    β测试(客户组织测试,验收阶段、黑盒)
    第三方测试(请专门的安全机构测试软件的安全性)

单元测试的定位

  • 测试目的:检验软件基本组成单位的正确性(方法、接口)
  • 测试对象:软件的功能模块
  • 测试方式:白盒测试
  • 实施者:开发工程师/白盒测试工程师

单元测试实例分析

那么,在实际项目中,哪些模块(方法)需要写单元测试呢?每个被测试的模块需要写多少个测试用例呢?

强烈推荐dao层、srvice层的相对独立功能都一接口的方式声明,并在接口注释中写明模块的功能、也可以写一下实现需要注意的地方,不必写实现的细节。因此,每一个dao层、service层的接口声明的模块都需要测试,以及每一个controller接口。 有了接口,即使没有具体实现,在spock中可以直接使用接口实例化测试桩进行测试。

单元测试中,测试用例的设计通过等价类边界值的方法设计,基本就可以满足需求。等价类就是将测试用例花费为若干个等价类,在每个等价类中至少取一个测试用例即可。边界值法是对等价类法的补充。

例如:某个被测试模块的输入为整数,将测试用例可以划分为两个等价类(-∞, 0)和[0, +∞), 所以取了测试用例:-87,125。测试之后,我们总觉得不放心,所以我们取第三个测试用例 0再测一遍,这个0就是边界值法取的测试用例。

此外,对于多元测试用例,为了使用尽量少的测试用例,可以通过正交排列和正交表选取测试用例。

java(spring)项目中怎样进行单元测试?

  1. 简单工具类的测试 
    • 写一个main方法 
    • groovy脚本 
    • 使用单元测试框架 junit/spock
  2. 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)

  1. 记录已经知道的 bug
  2. 标记让方法执行失败的测试用例

@Timeout(value = 10, unit = TimeUnit.MILLISECONDS)

  • 超时就失败

发表评论