背景
在公司的项目开发中,有时候需要用到其他同学封装的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";
@Resource
public 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
@NoArgsConstructor
public 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的内部代码逻辑是这样的
@Configuration
public 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 {
@Spy
private GlobalLogWriter globalLogWriter;
@InjectMocks
GlobalLogUtil globalLogUtil;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void 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);
}
}
}
总结
其实我真实想要表达的是,如果对于整体的逻辑过程没有一个庖丁解牛般的认识,那么总会遇到各种各样的错误,遇到错误的时候,解决方案一定要灵活,黑猫白猫,抓到老鼠就是好猫。
今天只是一个静态字段没有设置的空指针可以提前设置上,后天如果对象不是内部类,是运行时生成的,那么该怎么去处理呢。再推而广之,今天碰到的是这样的问题,明天后天碰到其他的问题呢,解决问题要头脑灵活,要天马行空(但是要有效)。但是灵活,天马行空又是对日常问题的结果积累,思考总结。
虽然写完了,但感觉没有把我想表达的含义表述出来,也许后边我自己再来回顾也不能理解自己为什么要这么写。