天天看點

自定義注解傳回mock資料

1. 如果配置mock測試為真,傳回mock資料,為假,傳回真實資料

2. 實作方式

可以每次在方法中判斷,但是很麻煩,好多if判斷

可以定義接口,多一個mock實作,在程式啟動的時候,隻加載mock實作類,真實實作類不加載(可以自定義類加載器或者使用spring bean加載過濾)

也可以使用AOP切面,判斷是否mock看情況傳回

其他實作方式暫時沒有想法或者頭緒

最後确定使用AOP簡單快捷

3. 代碼

自定義注解

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MockData {

    @AliasFor("method")
    String value() default "";

    /**
     * 是否開啟mock資料傳回
     */
    boolean isOpen() default true;

    /**
     * 指定mock資料組裝 <strong>無參</strong>方法
     */
    @AliasFor("value")
    String method() default "";

    /**
     * mock方法類,預設目前類
     * class數組有且隻能有一個class,該類最好使用spring bean管理
     */
    Class<?>[] classFor() default {};
}
      

 AOP 切面 

@Slf4j
@Aspect
@Component
public class MockAspect {

    @Value("${mockModal}")
    private boolean mockModal;

    @Autowired
    private BeanFactory beanFactory;

    /**
     * mock資料切面方法
     *
     * @param joinPoint
     * @param mockData
     */
    @Around("within(@org.springframework.stereotype.Service *) && @annotation(mockData)")
    public Object around(ProceedingJoinPoint joinPoint, MockData mockData) {
        Object res = Res.fail("服務内部mock資料錯誤!");
        try {
            // 判斷是否開啟了 mock 資料傳回
            if (mockData.isOpen() && mockModal) {
                // @AliasFor 是spring 特有注解,這裡使用spring提供的工具類擷取注解,要不然 value和method互替不會生效
                mockData = AnnotationUtils.getAnnotation(mockData, MockData.class);
                assert mockData != null;
                Object bean;
                Method[] methods;
                if (mockData.classFor().length == 0) {
                    // 預設使用目前類
                    bean = beanFactory.getBean(joinPoint.getTarget().getClass());
                    methods = joinPoint.getTarget().getClass().getDeclaredMethods();
                } else {
                    try {
                        bean = beanFactory.getBean(mockData.classFor()[0]);
                    } catch (NoSuchBeanDefinitionException e) {
                        log.warn("mock資料通路類不在spring bean工廠中改用反射方式...");
                        bean = mockData.classFor()[0].newInstance();
                    }
                    methods = mockData.classFor()[0].getDeclaredMethods();
                }
                // mock 注解指定的 mock方法名
                String name = mockData.method();
                Optional<Method> check = Arrays.stream(methods).filter(method -> method.getName().equals(name)).findFirst();
                if (check.isPresent()) {
                    Method method = check.get();
                    method.setAccessible(true);
                    // 執行 mock 方法
                    res = method.invoke(bean);
                }
            } else {
                res = joinPoint.proceed();
            }
        } catch (Throwable throwable) {
            log.error("mock資料切面出錯!", throwable);
        }
        return res;
    }
}
      
@Override 
@MockData("mockSearch")
public Res search(AddDTO dto) {...}

private Res mockSearch() {
  return Res.ok("mock data");
}      

繼續閱讀