springboot基于junit4的单元测试实践

本ut的实践,未不需要加载过多的springbean,除了需要测试的方法,其他均是mock的。这样大大提高了启动测试的时间,但是编写mock,也同样需要大量的时间。所有我们只对于复杂业务写了单元测试,下面为代码实现

import org.apache.commons.io.FileUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;

//将bean的数据,数据结果放在文件中,json格式加载出来 @RunWith(SpringRunner.class) public class MockBaseTest { private final static Logger LOGGER = LoggerFactory.getLogger(MockBaseTest.class);/** * * @param path commond json 文件路径 * @param clazz commond 类 * @return */ @SuppressWarnings("unchecked") public static <T> T loadObject(String path,Class<T> clazz){ String basePath = MockBaseTest.class.getResource("/").getPath(); String commandStr = null; try { commandStr = FileUtils.readFileToString(new File(basePath + path), Charset.defaultCharset()); return JsonHelper.fromJson(commandStr,clazz); } catch (IOException e) { LOGGER.error("loadCommand",e); } return null; } public static <T> T loadObject(String path, Type typeOfT){ String basePath = MockBaseTest.class.getResource("/").getPath(); try { String commandStr = FileUtils.readFileToString(new File(basePath + path), Charset.defaultCharset()); return JsonHelper.fromJson(commandStr,typeOfT); } catch (IOException e) { LOGGER.error("loadCommand",e); } return null; } }
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;

//TestServiceImpl里所有的需要注入的bean都要mock,为了实际test代码地方简介一点,这里提个base类
@Import(TestServiceBaseTest.BeanConfiguration.class)
public class TestServiceBaseTest extends MockBaseTest {
    @Autowired
    public TestService testService;

    @TestConfiguration
    static class BeanConfiguration {
        @Bean
        public TestService testService() {
            return new TestServiceImpl();
        }
    }

    //------------------------------------------- bean ------------------------------------------------------------------------
    @MockBean
    public AaService aaService;
    @MockBean
    public BbService bbService;
    @MockBean
    public CcDao CcDao;


}
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.gson.reflect.TypeToken;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;


public class TestServicePayLockTest extends TestServiceBaseTest {
    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    private static Long LOCK_SECONDS = 5L;

//替代redis,可过期功能 private Cache<String, String> cache = CacheBuilder.newBuilder() .expireAfterWrite(LOCK_SECONDS, TimeUnit.SECONDS) .build();
//需要往数据库插入数据,并get时,这个可替代数据库功能,本次下面代码没有使用
private List<Test> dataList = new ArrayList(); @Test public void payLockAmountError() { expectedException.expect(RuntimeErrorException.class); expectedException.expectMessage("数据发生变化,请刷新重试!"); TypeToken<PayLock> typeToken = new TypeToken<PayLock>() { }; String number = "1"; BigDecimal amount = new BigDecimal("100.1"); PayLock payLock = loadResource("command/payment/test_data.json", typeToken.getType()); when(ccDao.find(anyString())) .thenReturn(payLock.orders); when(ccDao.find2(anyList())).thenReturn(payLock.items); setRedicLockMock(); testService.payLock(number, amount); } @Test public void payLockFailError() throws InterruptedException { TypeToken<PayLock> typeToken = new TypeToken<PayLock>() { }; String number = "1"; BigDecimal amount = new BigDecimal("6"); PayLock payLock = loadResource("command/payment/test_data.json", typeToken.getType()); when(ccDao.find(eq("1"))).thenReturn(payLock.orders); when(ccDao.find(not(eq("1")))).thenReturn(payLock.orders2); setRedicLockMock(); when(ccDao.find2(anyList())).thenReturn(payLock.items); testService.payLock(number, amount); try { testService.payLock("2", amount); } catch (Exception e) { Assert.assertEquals(e.getClass(), RuntimeException.class); Assert.assertEquals(e.getMessage(), "数据被锁定,请稍后刷新重试!"); } Assert.assertEquals(3, cache.size()); Thread.sleep(1000 * LOCK_SECONDS); testService.payLock("2", amount); } @Test public void payLockReleaseError() { expectedException.expect(RuntimeException.class); expectedException.expectMessage("数据被锁定,请稍后刷新重试!"); TypeToken<PayLock> typeToken = new TypeToken<PayLock>() { }; String number = "1"; BigDecimal amount = new BigDecimal("6"); PayLock payLock = loadResource("command/payment/test_data.json", typeToken.getType()); when(ccDao.find(anyString())).thenReturn(payLock.orders); when(redisLockUtil.getLock(not(eq(PayCode.PAY_LOCK + "3")), anyString(), eq(PayCode.LOCK_TIME), eq(PayCode.LOCK_TIME_UNIT))).thenReturn(true); when(redisLockUtil.getLock(eq(PayCode.PAY_LOCK + "3"), anyString(), eq(PayCode.LOCK_TIME), eq(PayCode.LOCK_TIME_UNIT))).thenReturn(false); doThrow(new RuntimeException("锁释放失败!")).when(redisLockUtil).releaseLock(eq("2"), anyString()); when(ccDao.find2(anyList())).thenReturn(payLock.items); testService.payLock(number, amount); } private void setRedicLockMock() {
//when invoke return method,do something when(redisLockUtil.getLock(anyString(), anyString(), anyLong(), any(TimeUnit.class))).then((a) -> { String key = a.getArgument(0); String value = cache.getIfPresent(key); if (value == null) { cache.put(key, a.getArgument(1)); return true; } return false; });
//when invoke viod method,do something doAnswer(answer -> { String ifPresent = cache.getIfPresent(answer.getArgument(0)); if (ifPresent != null && ifPresent.equals(answer.getArgument(1))) { cache.invalidate(answer.getArgument(0)); } else if(ifPresent != null && ifPresent(answer.getArgument(0))){
          throw new RuntimeExcetion("锁释放失败!");
       } return null; }).when(redisLockUtil).releaseLock(anyString(), anyString()); } // 本次ut需要所有的对象,这里构建一个内部类,有用加载json文件里的配置 class PayLock { List<Order> orders; List<Order> orders2; List<Item> items; } }

 

上一篇:ansible自动化部署之路笔记


下一篇:Oracle OCP 19c 认证1Z0-083考试题库(第1题)