1.常用註解
@Before:初始化方法,在任何一個測試方法執行之前,必須執行的代碼。
@After: 釋放資源,在任何一個測試方法執行之後,需要進行的收尾工作。
@Ignore:忽略的測試方法,標註的含義就是“某些方法尚未完成,咱不參與此次測試”;這樣的話測試結果就會提示你有幾個測試被忽略,而不是失敗
@BeforeClass:針對所有測試,也就是整個測試類中,在所有測試方法執行前,都會先執行由它註解的方法,而且只執行一次。當然,需要注意的是,修飾符必須是public static void xxxx ;此 Annotation 是 JUnit 4 新增的功能。
@AfterClass:針對所有測試,也就是整個測試類中,在所有測試方法都執行完之後,纔會執行由它註解的方法,而且只執行一次。需要注意的是,修飾符也必須是 public static void xxxx ; JUnit 4 新增的功能,與 @BeforeClass 是一對。
執行順序:
2.示例
2.1 Controller層
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ArticleControllerTest extends AbstractTransactionalJUnit4SpringContextTests {
private MockMvc mockMvc;
@Value("${api.secret-token}")
private String secretToken;
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).alwaysDo(print()).build();
}
// 1.模擬上傳Excel
@Test
@Sql("/db/test/test_import_loan_data.sql")
public void testImportLoanData() throws Exception {
URL resource = this.getClass().getResource("/file/upload_excel.xls");
Path path = Paths.get(resource.toURI());
byte[] data = Files.readAllBytes(path);
MockMultipartFile file = new MockMultipartFile("excel", "upload_excel.xls",
MediaType.IMAGE_JPEG_VALUE, data);
ResultActions perform = mockMvc.perform(
MockMvcRequestBuilders.fileUpload("/admin/import_loan").file(file)
);
perform.andExpect(status().isOk());
perform.andExpect(jsonPath("status").value("200"));
}
/**
* 2. 測試翻頁
* @throws Exception 異常
*/
@Test
@Sql(scripts = {"/db/test/admin_init.sql", "/db/test/exchange_rest_controller_test.sql", "/db/180411_dictionary_init.sql"})
public void testGetExchangeList1() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/admin/exchange/get_exchange_list") // 構建請求
.param("page_size", "10") // 構建傳參
.param("page_num", "1")
.param("status", "2")
.accept(MediaType.APPLICATION_JSON_UTF8)) // 設置請求類型
.andExpect(status().isOk()) // 期望藉口調用狀態斷言
.andExpect(jsonPath("status").value(200)) // 期望藉口返回值斷言
.andExpect(jsonPath("data.total_record").value(261))
.andExpect(jsonPath("data.data").isNotEmpty())
.andReturn();
}
}
2.2 Service層
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.test.annotation.Commit;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertThat;
@RunWith(MockitoJUnitRunner.class)
public class IfcertAccessCoreServiceImplTest {
@Mock
private AccountCheckConfig config;
@Mock
private IfcertAccessDataService accessDataService;
@Mock
private IfcertAccessClientService accessClientService;
@Mock
private QueryTaskService queryTaskService;
@Mock
private PushTaskService pushTaskService;
@Mock
private IfcertDailyCheckHistoryMapper dailyCheckHistoryMapper;
@Mock
private IfcertDailyFailRepushHistoryMapper failRepushHistoryMapper;
@Mock
private MailService mailService;
private IfcertAccessCoreServiceImpl service;
// 此處必須要求service實現類要有構造方法
@Before
public void setup() {
service = new IfcertAccessCoreServiceImpl(config, accessDataService, accessClientService, queryTaskService,
pushTaskService, dailyCheckHistoryMapper, failRepushHistoryMapper, mailService);
}
/**
* 對賬接口1:對賬入庫流程測試
*/
@Test
public void getBatchMessage() {
String[] params = new String[]{"123", "456"};
AccountCheckToDbRequest request = new AccountCheckToDbRequest();
request.setApiKey("apikey");
request.setEndPoint("endpoint");
request.setSourceCode("sourcecode");
request.setVersion("version");
request.setDataType("0");
request.setInfType("81");
request.setBatchNum("323223");
when(accessDataService.fillCheckRequestData(AccountCheckTypeEnum.DATA_TO_DB.getCode(), params))
.thenReturn(request);
BatchMessageResponseWrapper response = new BatchMessageResponseWrapper();
List<BatchMessageResponse> result = Lists.newArrayList();
BatchMessageResponse batchResponse = new BatchMessageResponse();
batchResponse.setBatchNum("23232332");
batchResponse.setDataType("0");
batchResponse.setErrorMsg("errormsg");
result.add(batchResponse);
response.setResult(result);
response.setCode("0000");
response.setMessage("查詢成功");
when(accessClientService.getBatchMessage(request)).thenReturn(response);
service.getBatchMessage("123", "456");
verify(accessDataService, times(1))
.fillCheckRequestData(AccountCheckTypeEnum.DATA_TO_DB.getCode(), params);
verify(accessClientService, times(1)).getBatchMessage(request);
}
// 解決方法反覆時void的處理
@Test
public void updateReading() throws Exception {
doNothing().when(taskModificationService).changeReadingStatus(anyList(),anyByte());
}
}
/**
* @author lei.liu
* @version 1.0.0
* @date 2017年12月29日 下午3:45:23
*/
@Service
public class IfcertAccessCoreServiceImpl implements IfcertAccessCoreService {
private static final Logger LOGGER = LoggerFactory.getLogger(IfcertAccessCoreServiceImpl.class);
/**
* 對賬接口2接口返回的數據分頁顯示數
*/
private static final int PAGE_SIZE = 3000;
private AccountCheckConfig config;
private IfcertAccessDataService accessDataService;
private IfcertAccessClientService ifcertAccessClientService;
private QueryTaskService queryTaskService;
private PushTaskService pushTaskService;
private IfcertDailyCheckHistoryMapper dailyCheckHistoryMapper;
private IfcertDailyFailRepushHistoryMapper failRepushHistoryMapper;
private MailService mailService;
/**
* 構造注入,避免測試案例mock調用關聯bean爲null
*
* @param config
* @param accessDataService
* @param ifcertAccessClientService
* @param queryTaskService
* @param pushTaskService
* @param dailyCheckHistoryMapper
* @param failRepushHistoryMapper
* @param mailService
*/
@Autowired
public IfcertAccessCoreServiceImpl(AccountCheckConfig config, IfcertAccessDataService accessDataService,
IfcertAccessClientService ifcertAccessClientService, QueryTaskService queryTaskService,
PushTaskService pushTaskService, IfcertDailyCheckHistoryMapper dailyCheckHistoryMapper,
IfcertDailyFailRepushHistoryMapper failRepushHistoryMapper, MailService mailService) {
this.config = config;
this.accessDataService = accessDataService;
this.ifcertAccessClientService = ifcertAccessClientService;
this.queryTaskService = queryTaskService;
this.pushTaskService = pushTaskService;
this.dailyCheckHistoryMapper = dailyCheckHistoryMapper;
this.failRepushHistoryMapper = failRepushHistoryMapper;
this.mailService = mailService;
}
...
...
}
2.3 Mapper層
import com.htouhui.bonus.coupon.entity.Account;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringRunner;
import java.math.BigDecimal;
@RunWith(SpringRunner.class)
@SpringBootTest
public class AccountMapperTest extends AbstractTransactionalJUnit4SpringContextTests {
@Autowired
private AccountMapper accountMapper;
@Test
@Sql("/db/test/account_mapper_test.sql") // 調用方法前執行sql腳本
public void testGetByUserId() {
Account account = accountMapper.getByUserId("h5602154779");
Assert.assertNotNull(account);
Assert.assertEquals(new BigDecimal("888.00"), account.getGrantMoney());
}
@Test
@Sql("/db/test/account_mapper_test.sql")
public void testUpdate() {
Account account = Account.builder()
.id(370941)
.grantMoney(new BigDecimal("444.00"))
.build();
int num = accountMapper.update(account);
Assert.assertEquals(1, num);
Account byId = accountMapper.findById(370941L);
Assert.assertNotNull(byId);
Assert.assertEquals(new BigDecimal("444.00"), byId.getGrantMoney());
}
}
3.對特殊框架的測試mock
3.1 針對shiro模擬用戶登錄進行mock
在某些場景下,對於shiro用戶登錄後產生的用戶信息,需要用到
shiro官方文檔提供了一個測試工具類AbstractShiroTest,需要用到
public abstract class AbstractShiroTest {
private static ThreadState subjectThreadState;
public AbstractShiroTest() {
}
/**
* Allows subclasses to set the currently executing {@link Subject} instance.
*
* @param subject the Subject instance
*/
protected void setSubject(Subject subject) {
clearSubject();
subjectThreadState = createThreadState(subject);
subjectThreadState.bind();
}
protected Subject getSubject() {
return SecurityUtils.getSubject();
}
protected ThreadState createThreadState(Subject subject) {
return new SubjectThreadState(subject);
}
/**
* Clears Shiro's thread state, ensuring the thread remains clean for future test execution.
*/
protected void clearSubject() {
doClearSubject();
}
private static void doClearSubject() {
if (subjectThreadState != null) {
subjectThreadState.clear();
subjectThreadState = null;
}
}
protected static void setSecurityManager(SecurityManager securityManager) {
SecurityUtils.setSecurityManager(securityManager);
}
protected static SecurityManager getSecurityManager() {
return SecurityUtils.getSecurityManager();
}
@AfterClass
public static void tearDownShiro() {
doClearSubject();
try {
SecurityManager securityManager = getSecurityManager();
LifecycleUtils.destroy(securityManager);
} catch (UnavailableSecurityManagerException e) {
//we don't care about this when cleaning up the test environment
//(for example, maybe the subclass is a unit test and it didn't
// need a SecurityManager instance because it was using only
// mock Subject instances)
}
setSecurityManager(null);
}
}
模擬shiro測試
package com.micecs.erp.group.service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import com.micecs.erp.module.admin.Admin;
import com.micecs.erp.module.approval.entity.GroupAudit;
import com.micecs.erp.module.approval.exception.GroupAuditException;
import com.micecs.erp.module.approval.mapper.GroupAuditMapper;
import com.micecs.erp.module.common.CommonException;
import com.micecs.erp.module.enums.*;
import com.micecs.erp.module.meeting.dto.ReserveFundDto;
import com.micecs.erp.module.meeting.entity.AuditRequire;
import com.micecs.erp.module.meeting.entity.GroupBaseInfo;
import com.micecs.erp.module.meeting.entity.ReserveFund;
import com.micecs.erp.module.meeting.exception.DataInitException;
import com.micecs.erp.module.meeting.exception.RfpException;
import com.micecs.erp.module.meeting.mapper.AuditRequireMapper;
import com.micecs.erp.module.meeting.mapper.GroupBaseInfoMapper;
import com.micecs.erp.module.meeting.mapper.ReserveFundMapper;
import com.micecs.erp.module.meeting.service.ReserveManagerService;
import com.micecs.erp.module.meeting.service.impl.ReserveManagerServiceImpl;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.apache.shiro.subject.Subject;
import org.junit.*;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static com.micecs.erp.admin.ShiroTestUtils.setSubject;
import static org.mockito.Mockito.*;
/**
* @author liulei, [email protected]
* @version 1.0
*/
@RunWith(MockitoJUnitRunner.class)
public class ReserveManagerServiceTest {
@Mock
private ReserveFundMapper reserveFundMapper;
@Mock
private GroupBaseInfoMapper groupBaseInfoMapper;
@Mock
private GroupAuditMapper groupAuditMapper;
@Mock
private AuditRequireMapper auditRequireMapper;
private ReserveManagerService service;
@Before
public void init() {
// 模擬shiro登錄
Subject subjectUnderTest = mock(Subject.class);
setSubject(subjectUnderTest);
Admin admin = new Admin();
admin.setId(1L);
admin.setAdministrator(true);
when(subjectUnderTest.getPrincipal()).thenReturn(admin);
service = new ReserveManagerServiceImpl(reserveFundMapper, groupBaseInfoMapper, groupAuditMapper, auditRequireMapper);
}
//該測試案例,有用到shiro登錄後的一些用戶信息取值靜態方法
@Test
public void testGetReserveFundListByGroupId() {
Long groupId = 1L;
List<ReserveFund> list = Lists.newArrayList();
ReserveFund fund = new ReserveFund();
fund.setId(1L);
fund.setGroupId(groupId);
fund.setMoney(new BigDecimal(1000));
fund.setFundType(CostDetailEnum.TRAVEL);
fund.setDataType(DataTypeEnum.BUDGET.getValue());
fund.setCreateTime(LocalDateTime.now());
ReserveFund fund2 = new ReserveFund();
fund2.setId(1L);
fund2.setGroupId(groupId);
fund2.setMoney(new BigDecimal(1000));
fund2.setFundType(CostDetailEnum.TRAVEL);
fund2.setDataType(DataTypeEnum.BUDGET.getValue());
fund2.setCreateTime(LocalDateTime.now());
list.add(fund);
list.add(fund2);
when(reserveFundMapper.selectListByGroupId(isA(LambdaQueryWrapper.class))).thenReturn(list);
try {
service.getReserveFundListByGroupId(groupId, DataTypeEnum.BUDGET);
} catch (UnavailableSecurityManagerException e) {
System.out.println("用戶未登錄");
}
verify(reserveFundMapper, times(1)).selectListByGroupId(isA(LambdaQueryWrapper.class));
}
}
3.2 針對redisTemplate進行mock
package com.micecs.erp.group.service;
import com.google.common.collect.Lists;
import com.micecs.erp.common.Settings;
import com.micecs.erp.module.approval.entity.AuditArchive;
import com.micecs.erp.module.enums.*;
import com.micecs.erp.module.meeting.entity.GroupBaseInfo;
import com.micecs.erp.module.meeting.entity.InvoiceInfo;
import com.micecs.erp.module.meeting.entity.RequireDetail;
import com.micecs.erp.module.meeting.exception.CustomerBaseException;
import com.micecs.erp.module.meeting.mapper.GroupBaseInfoMapper;
import com.micecs.erp.module.meeting.mapper.InvoiceInfoMapper;
import com.micecs.erp.module.meeting.service.InvoiceInfoService;
import com.micecs.erp.module.meeting.service.impl.InvoiceInfoServiceImpl;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.mockito.Mockito.*;
/**
* @author liulei, [email protected]
* @version 1.0
*/
@RunWith(MockitoJUnitRunner.class)
public class InvoiceInfoServiceTest {
@Mock
private InvoiceInfoMapper invoiceInfoMapper;
@Mock
private GroupBaseInfoMapper groupBaseInfoMapper;
@Mock
private RedisTemplate<String, Object> collectionRedisTemplate;
@Mock
private ValueOperations valueOperations;
private InvoiceInfoService service;
@Before
public void init() {
when(collectionRedisTemplate.opsForValue()).thenReturn(valueOperations);
service = new InvoiceInfoServiceImpl(invoiceInfoMapper, groupBaseInfoMapper, collectionRedisTemplate);
}
@Test
public void testGetInvoiceOfDefined() {
when(invoiceInfoMapper.getInvoiceByTypes(anyList())).thenReturn(Lists.newArrayList());
service.getInvoiceOfDefined(InvoiceInfoEnum.MEETING);
verify(invoiceInfoMapper, times(1)).getInvoiceByTypes(anyList());
}
@Test
public void testGetInvoiceInfoByType() {
InvoiceInfoEnum type = InvoiceInfoEnum.TRAVEL;
List<InvoiceInfo> itemLists = Lists.newArrayList();
InvoiceInfo item1 = new InvoiceInfo();
item1.setName("款項1");
item1.setValue(1);
InvoiceInfo item2 = new InvoiceInfo();
item2.setName("款項2");
item2.setValue(1);
itemLists.add(item1);
itemLists.add(item2);
List<InvoiceInfo> taxLists = Lists.newArrayList();
InvoiceInfo iv1 = new InvoiceInfo();
iv1.setName("發票1");
iv1.setValue(1);
InvoiceInfo iv2 = new InvoiceInfo();
iv2.setName("發票2");
iv2.setValue(2);
InvoiceInfo iv3 = new InvoiceInfo();
iv3.setName("發票3");
iv3.setValue(3);
InvoiceInfo iv4 = new InvoiceInfo();
iv4.setName("發票4");
iv4.setValue(4);
taxLists.add(iv1);
taxLists.add(iv2);
taxLists.add(iv3);
taxLists.add(iv4);
TeamNatureEnum teamType = TeamNatureEnum.TRAVEL;
String redisKey = Settings.ENV + ":INVOICE:DEFINE:" + type.getValue() + "-" + teamType.getValue() + "-";
when(valueOperations.get(redisKey)).thenReturn(null);
when(invoiceInfoMapper.getInvoiceByTypes(anyList())).thenReturn(itemLists).thenReturn(taxLists);
doNothing().when(valueOperations).set(isA(String.class), anyList(), anyLong(), isA(TimeUnit.class));
List<InvoiceInfo> info = service.getInvoiceInfoByType(type, teamType, null);
verify(valueOperations, times(1)).get(argThat(argument -> {
Assert.assertEquals(redisKey, argument);
return true;
}));
verify(invoiceInfoMapper, times(2)).getInvoiceByTypes(anyList());
info.forEach(vo -> {
// 斷言發票稅率
Assert.assertTrue(new BigDecimal("0").compareTo(vo.getChildren().get(0).getTaxRate()) == 0);
Assert.assertEquals(4, vo.getChildren().size());
});
}
}