背景
在公司的项目开发中,有时候需要用到其他同学封装的lib,由于结果比较特殊需要调用这个方法才才可以,不能直接Mock掉这个lib (由于核心是调用lib,mock后就只剩下组装参数了),这样意义就不大了。
举例如下: 核心就在于 write 的过程了,如果 mock 之后,就没有测试的必要了,验证不了正确性。
public class GlobalLogUtil {public static final String AK_GLOBAL_TAG = "ak_global_tag";public static final String MODULE_CODE = "module.product";public static final String PAGE_CODE = "page.product.manager";public static final String BASIC = "basic";public static final String PC = "PC";public static final String EDIT = "action.edit";@Resourcepublic GlobalLogWriter logWriter;@Async("asyncExecutor")public void insertEditLog(LogBean logBean) {LogTemplateRequest request = new LogTemplateRequest();request.setTemplateCode("template.product.local.edit");request.setAction(EDIT);base(logBean.companyId, logBean.userId, request, logBean.relationId);request.setTemplateType(LogTemplateType.DYNAMIC.getCode());request.setUserName(logBean.userName);request.setTerminalType(PC);request.setTemplateValueList(this.buildList(logBean));logWriter.write(request);}private void base(Long companyId, Long userId, LogTemplateRequest request, String relationId) {request.setCompanyId(companyId);request.setBusiness(BASIC);request.setBusinessCode(AK_GLOBAL_TAG);request.setModuleCode(MODULE_CODE);request.setPageCode(PAGE_CODE);request.setBusinessId(relationId);request.setUserId(userId);request.setDatetime(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS"));}private List<TemplateValue> buildList(LogBean logBean) {List<TemplateValue> templateValueList = new ArrayList<>();templateValueList.add(this.buildNameValue(logBean));final DynamicLogBody first = this.buildListValue("新增标签", logBean.addList);final DynamicLogBody second = this.buildListValue("移除标签", logBean.deleteList);final List<DynamicLogBody> dynamicLogBodyList = Lists.newArrayList(first, second);//插入操作日志TemplateValue v2 = new TemplateValue();v2.setType(LogValueType.DYNAMIC.getCode());v2.setDynamicLogBodyList(dynamicLogBodyList);templateValueList.add(v2);return templateValueList;}private TemplateValue buildNameValue(LogBean logBean) {TemplateValue value = new TemplateValue();value.setType(LogValueType.ORDINARY.getCode());value.setValue(logBean.productName);return value;}private DynamicLogBody buildListValue(String key, final List<String> tagList) {StringBuilder stringBuilder = new StringBuilder();if (CollUtil.isNotEmpty(tagList)) {stringBuilder.append(tagList.get(0));if (tagList.size() != 1) {stringBuilder.append("】");}for (int i = 1; i < tagList.size() - 1; i++) {final String s = tagList.get(i);stringBuilder.append("【").append(s).append("】");}if (tagList.size() > 1) {stringBuilder.append("【").append(tagList.get(tagList.size() - 1));}}List<DynamicLogValue> dynamicLogValueList = new ArrayList<>();DynamicLogValue dynamicLogValue = new DynamicLogValue();dynamicLogValue.setValue(stringBuilder.toString());dynamicLogValue.setValueType(0);dynamicLogValueList.add(dynamicLogValue);DynamicLogBody dynamicLogBody = new DynamicLogBody();dynamicLogBody.setKey(key);dynamicLogBody.setValueList(dynamicLogValueList);return dynamicLogBody;}@Getter@Setter@ToString@Builder@AllArgsConstructor@NoArgsConstructorpublic static class LogBean {public String productName;public String relationId;public Long companyId;public Long userId;public String userName;public List<String> addList;public List<String> deleteList;}}
然后write的内部代码逻辑是这样的
@Configurationpublic class MessageUtil implements MessageSourceAware {private static MessageSource messageSource;public void setMessageSource(MessageSource messageSource) {MessageUtil.messageSource = messageSource;}public static String getMessage(String message, Object[] args) {Locale locale = LocaleContextHolder.getLocale();return messageSource.getMessage(message, args, message, locale);}}
这里其实就是利用的 Spring#MessageSource , 但是在单元测试的时候因为没有被 Spring 接管,所以必然会出现空指针。
为了解决这个问题,我提前将默认的messageSource设置进去,这样即使没有被Spring接管,他也不会出现空指针,从而达到跑通单元测试的效果。
尽管这次通过这种提前到设置可以跑通,但是如果下次我们遇到的
**ResourceBundleMessageSource**类不是public的呢,如果下次遇到的内部类怎么办。
class GlobalLogUtilTest {@Spyprivate GlobalLogWriter globalLogWriter;@InjectMocksGlobalLogUtil globalLogUtil;@BeforeEachvoid setUp() {MockitoAnnotations.openMocks(this);}@Testvoid testInsertEditLog() {//ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();messageSource.setDefaultEncoding("UTF-8");messageSource.addBasenames("i18n.messages");MessageUtil messageUtil = new MessageUtil();messageUtil.setMessageSource(messageSource);{final GlobalLogUtil.LogBean logBean = GlobalLogUtil.LogBean.builder().productName("笑嘻嘻sku").relationId("relationId").companyId(911L).userId(911L).userName("陈顺").addList(Lists.newArrayList("第三个")).deleteList(Lists.newArrayList("删除第一个", "删除第三个")).build();globalLogUtil.insertEditLog(logBean);}}}
总结
其实我真实想要表达的是,如果对于整体的逻辑过程没有一个庖丁解牛般的认识,那么总会遇到各种各样的错误,遇到错误的时候,解决方案一定要灵活,黑猫白猫,抓到老鼠就是好猫。
今天只是一个静态字段没有设置的空指针可以提前设置上,后天如果对象不是内部类,是运行时生成的,那么该怎么去处理呢。再推而广之,今天碰到的是这样的问题,明天后天碰到其他的问题呢,解决问题要头脑灵活,要天马行空(但是要有效)。但是灵活,天马行空又是对日常问题的结果积累,思考总结。
虽然写完了,但感觉没有把我想表达的含义表述出来,也许后边我自己再来回顾也不能理解自己为什么要这么写。
