Spock(Spock官網(wǎng):http://spockframework.org/ )作為java和Groovy測試一種表達的規(guī)范語言,其參考了Junit、Groovy榛做、jMock忘晤、Scala等眾多語言的優(yōu)點,并采用Groovy作為其語法跃闹,目前能夠在絕大多數(shù)的集成開發(fā)環(huán)境(如eclipse,Intellij Ieda),構(gòu)建工具(如Maven颓鲜,gradle)等場景運行。Spock單元測試相對于傳統(tǒng)的junit典予、JMockito甜滨、EsayMock、Mockito瘤袖、PowerMock衣摩,由于使用了Groovy作為語法規(guī)則,代碼量少捂敌,容易上手艾扮,提高了單元測試開發(fā)的效率,因此號稱是下一代單元測試框架占婉。
本文以實戰(zhàn)的方式詳解怎樣使用Spock進行單元測試泡嘴,以便更好地理解Spock單元測試,至少能夠讓讀者能夠在選擇java單元測試面前多了一種選擇逆济。
1. 實戰(zhàn)
1.1 Spock的Maven依賴:
<!-- Mandatory dependencies for using Spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.1-groovy-2.4-rc-3</version>
<scope>test</scope>
</dependency>
<!-- Optional dependencies for using Spock -->
<dependency> <!-- use a specific Groovy version rather than the one specified by spock-core -->
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.7</version>
</dependency>
<dependency> <!-- enables mocking of classes (in addition to interfaces) -->
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.4</version>
<scope>test</scope>
</dependency>
<dependency> <!-- enables mocking of classes without default constructor (together with CGLIB) -->
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>2.5.1</version>
<scope>test</scope>
</dependency>
1.2 構(gòu)造項目基本代碼
BizService.java(接口)酌予、BizServiceImpl.java(接口實現(xiàn)類)磺箕、Dao.java(dao層代碼)、PersonEntity.java(bean對象)
1.2.1 接口類 BizService.java
/*** Created by lance on 2017/2/25.
*/
package com.lance.spock.demo.api;
public interface BizService {
String insert(String id, String name, int age);
String remove(String id);
String update(String name, int age);
String finds(String name);
boolean isAdult(int age) throws Exception;
}
1.2.2 接口實現(xiàn)類 BizServiceImpl.java
package com.lance.spock.demo.service.impl;
import com.lance.spock.demo.api.BizService;
import com.lance.spock.demo.dao.Dao;
import com.lance.spock.demo.entity.PersonEntity;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/*** Created by lance on 2016/9/6.
*/
@Service
public class BizServiceImpl implements BizService {
@Autowired
private Dao dao;
public String insert(String id, String name, int age) {
if (StringUtils.isAnyBlank(id, name)) {
return "";
}
PersonEntity bean = new PersonEntity();
bean.setAge(age);
bean.setPersonId(id);
bean.setPersonName(name);
dao.insert(bean);
return name;
}
public String remove(String id) {
if (StringUtils.isBlank(id)) {
return "";
}
dao.remove(id);
return id;
}
public String update(String name, int age) {
if (StringUtils.isAnyBlank(name)) {
return "";
}
dao.update(name, age);
return name;
}
public String finds(String name) {
if (StringUtils.isBlank(name)) {
return null;
}
List beans = dao.finds(name);
StringBuilder sb = new StringBuilder();
sb.append("#");
for (PersonEntity bean : beans) {
sb.append(bean.getAge()).append("#");
}
return sb.toString();
}
public boolean isAdult(int age) throws Exception {
if(age < 0) {
throw new Exception("age is less than zero.");
}
return age >= 18;
}
public Dao getDao() {
return dao;
}
public void setDao(Dao dao) {
this.dao = dao;
}
}
1.2.3 Dao層類 Dao.java
package com.lance.spock.demo.dao;
import com.lance.spock.demo.entity.PersonEntity;
import org.springframework.stereotype.Repository;
import java.util.Arrays;
import java.util.List;
/**
* Created by lance on 2016/9/6.
*/
@Repository
public class Dao {
public void insert(PersonEntity bean) {
System.out.println("Dao insert person");
}
public void remove(String id) {
System.out.println("Dao remove");
}
public void update(String name, int age) {
System.out.println("Dao update");
}
public List<PersonEntity> finds(String name) {
System.out.println("Dao finds");
PersonEntity bean = new PersonEntity();
bean.setPersonId("24336461423");
bean.setPersonName("張三");
bean.setAge(28);
return Arrays.asList(bean);
}
}
1.2.4 Bean數(shù)據(jù)類 PersonEntity.java
/**
* Created by lance on 2016/9/6.
*/
package com.lance.spock.demo.entity;
public class PersonEntity {
private String personId;
private String personName;
private int age;
public String getPersonId() {
return personId;
}
public void setPersonId(String personId) {
this.personId = personId;
}
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "PersonEntity{" +
"personId='" + personId + '\'' +
", personName='" + personName + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
return false;
PersonEntity that = (PersonEntity) o;
if (age != that.age) return false;
if (personId != null ? !personId.equals(that.personId) : that.personId != null) return false;
return personName != null ? personName.equals(that.personName) : that.personName == null;
}
@Override
public int hashCode() {
int result = personId != null ? personId.hashCode() : 0;
result = 31 * result + (personName != null ? personName.hashCode() : 0);
result = 31 * result + age;
return result;
}
}
1.2.5 上下文配置 applicationContext.xml
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.lance.spock.demo"/>
</beans>
1.3. Test類 BizServiceTest.groovy
package com.lance.spock.demo.service.impl.groovy
import com.lance.spock.demo.api.BizService
import com.lance.spock.demo.dao.Dao
import com.lance.spock.demo.entity.PersonEntity
import com.lance.spock.demo.service.impl.BizServiceImpl
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.context.support.FileSystemXmlApplicationContext
import spock.lang.Specification
import spock.lang.Unroll
/**
* Created by lance on 2017/2/25.
*/
public class BizServiceTest extends Specification {
@Autowired
private BizServiceImpl bizService;
Dao dao = Mock(Dao) // 生成dao的Mock對象
/**
* Spock和Junit類似也將單元測試劃分成了多個階段
* 如 setup() 類似于Junit的@Before,在這個方法中的代碼塊會在測試用例執(zhí)行之前執(zhí)行,一般用于初始化程序以及Mock定義
* when:和then: 表示當...的時候,結(jié)果怎樣.
* @return
*/
def setup() {
println(" ============= test start =============")
// 關(guān)聯(lián)Mock對象抛虫,由于本測試是基于接口的測試松靡,沒有相應(yīng)的setDao()方法,故采用此方法設(shè)置dao
//
ApplicationContext ac = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
bizService = ac.getBean(BizService.class)
// bizService.h.advised.targetSource.target.dao = dao;
bizService.setDao(dao)
}
def "test isAdult"() {
setup: //setup: 代碼塊主要針對自己所在方法的初始化參數(shù)操作
int age = 20
when:
bizService.isAdult(age) // 當執(zhí)行isAdult方法時
then:
true // 判斷when執(zhí)行bizService.isAdult(age)結(jié)果為true
notThrown() // 表示沒有異常拋出
println("age = " + age)
}
def "test isAdult except"() {
expect: // expect簡化了when...then的操作
bizService.isAdult(20) == true
}
def "age less than zero"() {
setup:
int age = -100
when:
bizService.isAdult(age)
then:
def e = thrown(Exception) //thrown() 捕獲異常,通常在then:中判斷
e.message == "age is less than zero."
println(e.message)
cleanup:
println("test clean up") // 單元測試執(zhí)行結(jié)束后的清理工作,如清理內(nèi)存莱褒,銷毀對象等
}
@Unroll
// 表示根君where的參數(shù)生成多個test方法,如本例生成了2個方法,方法名稱分別為:
// 1."where blocks test 20 isadult() is true"
// 2."where blocks test 17 isadult() is false"
def "where blocks test #age isadult() is #result"() {
expect:
bizService.isAdult(age) == result
where: // 其實實質(zhì)是執(zhí)行了兩次"where blocks test"方法击困,但是參數(shù)不一樣
age || result
20 || true
17 || false
}
def "insert test"() {
setup:
PersonEntity person = new PersonEntity();
person.setAge(28)
person.setPersonId("id_1")
person.setPersonName("zhangsan")
when:
bizService.insert("id_1", "zhangsan", 28)
then:
PersonEntity
1 * dao.insert(person) //判斷dao執(zhí)行了一次insert,且插入的對象是否equals
}
}
參考資料:
使用Spock框架進行單元測試(http://www.open-open.com/lib/view/open1439793373083.html)广凸;
Spock官網(wǎng)(http://spockframework.org/).