写单元测试懒人神器spock

架构小魔方 2024-09-21 20:26:34

有人说:“不做单元测试的代码就好像上厕所不洗手”,可见单元测试重要性不言而喻,但实际在国内的开发中,很多开发都没有写单元测试的习惯,为什么单元测试如此重要,但实际在执行落地过程中为什么这么难?

无外乎存在以下方面

1、开发人员的质量意识不够,普遍认为开发质量是由QA负责的,开发只管写代码。

2、代码逻辑过于复杂,写单元测试的耗时较长,代码量多,一般用Junit写单元测试的代码基本相当于代码的3倍

3、业务代码变化频繁,单元测试维护的成本较高,可能刚开始写的很好,写到后面慢慢维护少了,慢慢的原有的单元测试也就没有什么用了。

4、任务重,工期紧,正常的代码都写不完,更何况单元测试了。

.......

以上原因,如果进行归纳总结,无外乎就是两个原因

一、不想写呗

二、写单元测试任务重。

如果是不想写,那只能从其他方面去想办法改变这种状态,别无他法。

但写单元测试任务重?有没有一些可以偷懒的方式,能够提高写单元测试的效率,减少写单元测试的时间,那么本文带来一款写单元测试的利器spock

什么是spock?

spock 是什么?官网给出的原文是

Spock is a testing and specification framework for Java and Groovy applications. What makes it stand out from the crowd is its beautiful and highly expressive specification language. Thanks to its JUnit runner, Spock is compatible with most IDEs, build tools, and continuous integration servers. Spock is inspired from JUnit, RSpec, jMock, Mockito, Groovy, Scala, Vulcans, and other fascinating life forms.

简单来说就是

1、spock 是一个基于Java和Grovvy语言的测试框架

2、它的灵感来源于JUnit、RSpec、jMock、Mockito、Groovy、Scala、Vulcans,得益于JUnit,使得Spock能于多大数的IDE、构建工具进行集成

很明显Spock一出生就站在巨人的肩膀上,更详细的有关spock的说明可以参考其官网https://spockframework.org/

为什么使用Spock?

对于一个测试框架,我们将它与传统的测试框架Junit进行对比,看它有什么神奇之处。

一、测试数据

在写单元测试的时候,最烦的就是测试数据,如果在一个特别复杂,if/else分支特别多的场景下,写单元测试简直让人崩溃,为了测试一个正常的业务代码,可能要花费几倍甚至于几十倍的单元测试代码。

举个简单的例子:假设有这么一个方法,获取文件名作为输入并验证其扩展名。要求只有.jpeg、.jpg和.bmp扩展名可以通过,而其他扩展名是不允许的。这个方法会根据文件扩展名返回 "true "或 "false”。

public ImageValidate { public static boolean validate(String filename){ if(filename==null){ return false; } if(filename.endsWith(".jpeg")||filename.endsWith(".jpg")||filename.endsWith(".bmp")){ return true; }else { return false; } }}

spock写法:

@Title("测试文件扩展名验证方法")class ImageValidator extends Specification { @Unroll def "validate extension of #filename"() { when: "validator checks filename" def isValid = ImageValidate. validate(filename) then: "return appropriate result" isValid == expectedResult where: "input files are" filename || expectedResult 'some.jpeg' || true 'some.jpg' || true 'some.tiff' || false 'some.bmp' || true 'some.png' || false }}

Junit写法

@RunWith(Parameterized.class)@RunWith(Parameterized.class)public ImageValidatorTest { @Parameters public static Collection<Object[]> data() { return asList(new Object[][]{ {"some.jpeg", true}, {"some.jpg", true}, {"some.tiff", false}, {"some.bmp", true}, {"some.png", false} }); } private String filename; private boolean isValid; public ImageValidatorTest(String filename, boolean expected) { this.filename = filename; this. isValid = expected; } @Test public void validateFileExtension() { assertEquals(isValid, ImageValidate.validate(filename)); }}

从代码片段中可以看出,JUnit代码很明显要比Spock 要多,且可读性方面也没有Spock清晰。

二、Mock能力

在写JUnit单测的时候,要想使用Mock功能或多或少都需要依赖一些三方库,例如EasyMock或Mockito,但在Spock中无需再引用任何第三方依赖,直接使用Mock()方法即可

举个例子

@Servicepublic UserService { @Autowired private UserDao userDao; public UserVO getUserById(int id) { List<UserDTO> users = userDao.getUserList(); UserDTO userDTO = users.stream().filter(u -> u.getId() == id).findFirst().orElse(null); UserVO userVO = new UserVO(); if (userDTO == null) { return userVO; } userVO.setId(userDTO.getId()); userVO.setName(userDTO.getName()); userVO.setSex(userDTO.getSex()); userVO.setAge(userDTO.getAge()); return userVO; }}

可以使用spock自带的mock能力对userDao进行mock,非常简单

class UserServiceSpec extends Specification { def userDao = Mock(UserDao) def tester = new UserService(userDao: UserDao) def "test getUserById"() { given: "初始化参数" def user1 = new UserDTO(id: 1, name: "张三", age: 16,sex:'男') def user2 = new UserDTO(id: 2, name: "李四", age: 18,sex:'女') and: "mock userDao" userDao.getUserList() >> [user1, user2] when: "获取用户信息(getUserById)" def response = tester.getUserById(1) then: "结果验证" with(response) { id == 1 name == "张三" age == 16 sex == '男' } }}写在最后

当然Spock功能不仅仅只是这样,还有更多的功能强大的功能等着我们去发掘,通过上面几个例子,基本大家可以发现Spock在写单元测试的时候,无论从测试数据、mock能力还是代码的可读性方面均比junit要更好,当然spock也有一些学习的成本,对于不知道grovvy脚本的开发人员来说,还需要额外了解grovvy脚本的语法。

更多关于spock的资料可以参考

0 阅读:1

架构小魔方

简介:感谢大家的关注