2 Star 16 Fork 9

yawn / demo-spock

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
单元测试.md 10.64 KB
一键复制 编辑 原始数据 按行查看 历史
yawn 提交于 2019-10-16 15:53 . bb

单元测试(Unit Testing)

关于软件测试分类(通识)

软件实施的过程

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

代码透明度

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

是否运行程序

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

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

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

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

单元测试的定位

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

单元测试实例分析

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

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

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

等价类划分必须满足简单路径覆盖。

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

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

例如:某个被测试模块,输入为三个整数、每个输入都可以划分为两个等价类(-∞, 0)和[0, +∞),该如何选取测试用例?

如何进行单元测试?

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

  1. 简单工具类的测试
  2. spring环境中的测试
    • 使用单元测试框架 junit/spock

如何写自动化执行的单元测试

junit的使用(略)

常用方法注解:

    @Before
    @BeforeClass
    @After
    @AfterClass
    
    @Test
    @Ignore 

测试方法上使用@Test注解,并且测试方法必须是public 类型的,返回值必须为void

spock的基本使用

0. groovy环境安装配置

groovy环境安装配置和入门使用示例

1. 依赖

    <dependencies>
        <!-- Spock需要的groovy依赖 -->
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.4.15</version>
        </dependency>
        <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
    }

*6. spock对异常的测试

thrown()方法

测试的结果需要抛出某个异常时使用,实例如下:

    /**
     * 测试一个方法抛出异常,可以使用try-catch在when语句块中捕获验证,但是写起来比较繁琐
     * 所以,Spock测试框架中可以使用thrown()来表示这种现象,并且可以返回异常的实例
     */
    def "test Thrown"() {
        when:
        int a = 1
        int b = 0
        int c = 2
        c = (a / b)
        then:
        def ex = thrown(Exception)
        ex instanceof ArithmeticException
        // ArithmeticException 异常时我们预料之中的
    }

notThrown()方法

测试的结果不能抛出某个异常时使用,实例如下:

    /**
     * notThrown() 表示被测试的方法不会抛出某种异常
     */
    def "HashMap accepts null key"() {
        given:
        def map = new HashMap()
        when:
        map.put(null, "elem")
        then:
        notThrown(NullPointerException)
    }

我们通过以上方法来测试HashMap是否允许null作为键,如果测试通过(不会抛出异常),则表示可以用null。

*7. HamcrestMatcher

import static spock.util.matcher.HamcrestSupport.expect
import static spock.util.matcher.HamcrestMatchers.closeTo

class HamcrestSupportSpec extends Specification {

    def "test for closeTo()"() {
        given:
        int a = 101256
        when:
        int b = a / 10
        then:
//        HamcrestSupport.expect(b, HamcrestMatchers.closeTo(10000, 200))
//        expect(b, closeTo(10000, 200))
        expect b, closeTo(10000, 200)
    }
}

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") 
      // [[a, b, c], [a, c]] 这种写法类似于Matlab和Lisp语言中二维表、多维表的写法(Lisp中使用小括号)
    }

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 {

    @Shared
    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. 标记让方法执行失败的测试用例

*补充:groovy中使用groovy.sql.Sql访问数据库

groovy-sql模块是比java中jdbc更高一级的对数据库操作的抽象,可以理解为groovy中的jdbc。

使用实例:

    def url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=UTF-8&useUnicode=true"
    def sql = Sql.newInstance(url, "root", "root", "com.mysql.cj.jdbc.Driver")
    List<GroovyRowResult> testList = sql.rows("select * from test")
    for (GroovyRowResult row : testList) {
        println row
        println row.get("id")
        println row.get("name")
        println row.get("remark")
    }
    println testList
    sql.close()

参考 http://jvm123.com/2019/10/groovy-sql.html

源代码下载

本堂课演示代码下载地址

git clone https://gitee.com/yawensilence/demo-spock.git
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Java
1
https://gitee.com/yawensilence/demo-spock.git
git@gitee.com:yawensilence/demo-spock.git
yawensilence
demo-spock
demo-spock
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891