单元测试框架对比
主要小试了下junit + mockito 及 testng + jmockit两种ut及mock搭配方式。做下简单对比,当做笔记。
- junit + mockito
- testng + jmockit
junit + mockito
依赖:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito-core.version}</version>
</dependency>
同spring结合,BaseTest类。继承AbstractTransactionalJUnit4SpringContextTests 加入了事务。
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration(locations = { "classpath:spring-test.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class BaseTest extends AbstractTransactionalJUnit4SpringContextTests implements ApplicationContextAware {
@Before
public void setup() {
/** Initializes objects annotated with Mockito annotations for given testClass */
MockitoAnnotations.initMocks(this);
}
}
test子类。
简单说明:leaseBiz是业务逻辑层,调用leaseService层去查询数据库。
package com.unifirst.biz;
import java.math.BigDecimal;
import java.util.Date;
import javax.annotation.Resource;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.springframework.test.util.ReflectionTestUtils;
import com.unifirst.BaseTest;
import com.unifirst.model.po.LeaseRecord;
import com.unifirst.model.ro.LeaseDetailResp;
import com.unifirst.service.LeaseService;
/**
* Created by unifirst on 2018/3/13.
*/
public class LeaseTest extends BaseTest {
@Spy
private LeaseService leaseService;
@Resource
@InjectMocks
private LeaseBiz leaseBiz;
@Before
public void init() {
LeaseRecord leaseRecord = new LeaseRecord();
leaseRecord.setTotalAmount(BigDecimal.ONE);
leaseRecord.setEndTime(new Date());
leaseRecord.setStartTime(new Date());
leaseRecord.setUid(30030633745056L);
leaseRecord.setLeaseNumber(317733346556907520L);
/** 仅仅用spy + InjectMocks,无法成功注入;需要用反射方式注入 */
ReflectionTestUtils.setField(leaseBiz, "leaseService", leaseService);
Mockito.when(leaseService.queryDetail(Mockito.anyLong())).thenReturn(leaseRecord);
}
@Test
public void detailTest() {
LeaseDetailResp detail = leaseBiz.detail(30030633745056L, 317733346556907520L);
System.out.println(detail);
}
}
testng + jmockit
依赖
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>${jmockit.version}</version>
</dependency>
test类
几处注意的地方:
- 无上面与spring结合的BaseTest类
- @Tested注解的不是interface,而是具体实现类
- 虽然被测试代码仅仅使用到了globalConfigBiz,但其他@Resource引入的service,都需要用@Injectable注入,否则报错:
java.lang.IllegalStateException: Missing @Injectable for field xxx
- 若被test的类有@Value注解,则同service一样要通过@Injectable注入
代码示例如下:
package com.unifirst.biz;
import java.util.List;
import org.testng.annotations.Test;
import org.testng.collections.Lists;
import com.unifirst.biz.impl.FeedBackBizImpl;
import com.unifirst.model.bo.FaultTypes;
import com.unifirst.service.FeedBackService;
import com.unifirst.service.OpLogService;
import com.wormpex.api.json.JsonUtil;
import mockit.Expectations;
import mockit.Injectable;
import mockit.Tested;
/**
* Created by unifirst on 2018/3/14.
*/
public class FeedBackBizTest {
@Tested
private FeedBackBizImpl feedBackBiz;
@Injectable
private FeedBackService feedBackService;
@Injectable
private OpLogService opLogService;
@Injectable
private GlobalConfigBiz globalConfigBiz;
@Test
public void faultTypeListsTest() {
new Expectations() {
{
FaultTypes faultTypes = new FaultTypes();
List<String> types = Lists.newArrayList("t1", "t2");
faultTypes.setTypes(types);
globalConfigBiz.query(anyString);
result = JsonUtil.of(faultTypes);
}
};
List<String> detail = feedBackBiz.faultTypeLists();
System.out.println(detail);
}
}
对比
junit vs testng,mockito vs jmockit 网上资料很多,不多介绍。这里只从与spring的结合性来简单对比。
- junit + mockito 与spring结合性感觉更强,可以充分兼容spring的各种注解,能较完美接近项目启动后实际运行情况。可以用来做一定的业务自动化回归测试,但可能超出了ut的范围
- testng + jmockit 可能功能更强大,但与spring不易结合,各种注解不好兼容,如上例中直接放弃了在spring中的测试。反过来说,不依赖spring,专注测好一件事情。
UT理念应该是小而简单、保持独立、mock依赖都能正确工作。这应该是jmockit所追求的,所以强调脱离spring的正常工作。
但目前项目中,需要用ut做一部分业务自动化测试,因此可能junit + mockito更适合。
个人浅见,欢迎拍砖。