官方文档
javaapi:https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html
官网:https://site.mockito.org/
github:https://github.com/mockito/mockito
如何编写一个单元测试用例
https://www.vogella.com/tutorials/Mockito/article.html#mockitousage
如何编写一个集成测试用例
这个不同框架不同,可以参考spring boot的写法
如何mock static的方法?
https://asolntsev.github.io/en/2020/07/11/mockito-static-methods/
如何不启动spring整体工程,在本地实际连db测试dao层代码?
方法思路:
- 采用jdbc 方式直连
- 采用反射机制 动态 实例化类 及其属性
mybatis sqlSession相关解释见 https://www.yuque.com/antone/zv3ypu/tl15md
@Slf4jpublic class MutiDataSourceTest {private static final Logger LOGGER = LoggerFactory.getLogger(MutiDataSourceTest.class);private static Map<DataSourceType, DataSource> dataSourceContext = new HashMap<>();//@Getterprivate static Map<DataSourceType, SqlSessionFactory> sqlSessionFactoryContext = new HashMap<>();private static Map<DataSourceType, Properties> dataSourceTypeToConfig = new HashMap<>();private static Map<Class<? extends Annotation>, DataSourceType> classToDataSourceType = new HashMap<>();private static List<SqlSession> sqlSessions = new ArrayList<>();public void logObject(Object o) {//LOGGER.info("obj:{}", o);}static {classToDataSourceType.put(EXPLORER.class, DataSourceType.EXPLORE);// 数据库配置信息Properties explorerConfig = new Properties();explorerConfig.put("driver", "com.mysql.jdbc.Driver");explorerConfig.put("url","jdbc:mysql://xxxx:3306/cheetah?useUnicode=true&characterEncoding=utf8"+ "&rewriteBatchedStatements=true");explorerConfig.put("username", "xxx");explorerConfig.put("password", "xxx");LoggerUtil.info(LOGGER, "adb config:{0}", explorerConfig);dataSourceTypeToConfig.put(DataSourceType.EXPLORE, explorerConfig);}public static enum DataSourceType {EXPLORE}public static SqlSession openSession(DataSourceType dataSourceType) {SqlSessionFactory sqlSessionFactory = getSqlSessionFactoryByType(dataSourceType);SqlSession sqlSession = sqlSessionFactory.openSession(null, true);sqlSessions.add(sqlSession);return sqlSession;}public static <T> T getMapper(Class<T> tClass) {DataSourceType dataSourceType = parseType(tClass);SqlSessionFactory sqlSessionFactory = getSqlSessionFactoryByType(dataSourceType);MapperRegistry mapperRegistry = sqlSessionFactory.getConfiguration().getMapperRegistry();if (!mapperRegistry.hasMapper(tClass)) {sqlSessionFactory.getConfiguration().addMapper(tClass);}SqlSession sqlSession = openSession(dataSourceType);return sqlSession.getMapper(tClass);}public static SqlSessionFactory getSqlSessionFactoryByType(DataSourceType dataSourceType) {if (!sqlSessionFactoryContext.containsKey(dataSourceType)) {DataSource dataSource = getDataSourceByType(dataSourceType);Configuration configuration = new Configuration();JdbcTransactionFactory jdbcTransactionFactory = new JdbcTransactionFactory();Environment environment = new Environment("test", jdbcTransactionFactory, dataSource);configuration.setEnvironment(environment);configuration.getTypeAliasRegistry().registerAliases("com.alibaba.aliexpress.module");configuration.setMapUnderscoreToCamelCase(true);MybatisProperties mybatisProperties = new MybatisProperties();mybatisProperties.setMapperLocations(new String[] {"classpath*:com/alipay/xxxx/dal/xxx/mapper/*.xml"}); //这里是mapper配置的地址Resource[] resources = mybatisProperties.resolveMapperLocations();for (Resource mapperLocation : resources) {XMLMapperBuilder xmlMapperBuilder = null;try {xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),configuration, mapperLocation.toString(), configuration.getSqlFragments());} catch (IOException e) {e.printStackTrace();}xmlMapperBuilder.parse();}DefaultSqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration);//LOGGER.info("{} sqlSessionFactory init success.", dataSourceType);sqlSessionFactoryContext.put(dataSourceType, sqlSessionFactory);//addIntercepter(sqlSessionFactory, dataSourceType);}return sqlSessionFactoryContext.get(dataSourceType);}public static DataSource getDataSourceByType(DataSourceType dataSourceType) {if (!dataSourceContext.containsKey(dataSourceType)) {DataSource dataSource = buildDataSouce(dataSourceType);dataSourceContext.put(dataSourceType, dataSource);//LOGGER.info("{} dataSource init success.", dataSourceType);}return dataSourceContext.get(dataSourceType);}private static DataSource buildDataSouce(DataSourceType dataSourceType) {Properties properties = dataSourceTypeToConfig.get(dataSourceType);UnpooledDataSource dataSource = new UnpooledDataSource();dataSource.setDriver(properties.getProperty("driver"));dataSource.setUrl(properties.getProperty("url"));dataSource.setUsername(properties.getProperty("username"));dataSource.setPassword(properties.getProperty("password"));return dataSource;}private static <T> DataSourceType parseType(Class<T> tClass) {Set<Class<? extends Annotation>> classes = classToDataSourceType.keySet();for (Class<? extends Annotation> anotationClass : classes) {if (anotationClass == Mapper.class) {continue;}if (tClass.isAnnotationPresent(anotationClass)) {return classToDataSourceType.get(anotationClass);}}//return DataSourceType.TDDL;return null;}@AfterClasspublic static void destroy() {sqlSessions.forEach(s -> {if (s != null) {s.close();}});}}
public class ServiceTest extends MutiDataSourceTest {private static final Logger logger = LoggerFactory.getLogger(ServiceTest.class);private boolean added;@Beforepublic void serviceTestInit() {beforeInit();populateField(this);//afterPopulate(this);}private void beforeInit() {addIntercepter();}public <T> T createService(Class<T> tClass) {return (T)createObject(tClass);}private Object createObject(Class tClass) {try {//log.debug("create instance for class:{}", tClass.getName());Class c = findImplClass(tClass);if (c == null) {return null;}Object o = c.newInstance();populateField(o);//afterPopulate(o);return o;} catch (Exception e) {throw new RuntimeException(e);}}private void afterPopulate(Object o) {//if (o instanceof BaseServiceImpl) {// try {// initBaseImpl(o);// } catch (InvocationTargetException e) {// e.printStackTrace();// } catch (IllegalAccessException e) {// e.printStackTrace();// }//}}private void populateField(Object o) {Field[] declaredFields = o.getClass().getDeclaredFields();Arrays.stream(declaredFields).filter(field -> field.isAnnotationPresent(Autowired.class) || field.isAnnotationPresent(Resource.class)).forEach(f -> populateField(f, o));}private <T> Class findImplClass(Class<T> tClass) {if (tClass.isInterface()) {String className = tClass.getSimpleName() + "Impl";String packageName = tClass.getName().replace(tClass.getSimpleName(), "impl.");String implClassName = packageName + className;//String implClassName2 = packageName + className2;//log.debug("find impl class for:{} is {}", tClass.getName(), implClassName);Class<?> aClass = null;try {aClass = Class.forName(implClassName);} catch (ClassNotFoundException e) {LoggerUtil.warn(logger, "not found impl class for:{0},skip create instance", tClass.getName());// e.printStackTrace();}return aClass;}return tClass;}@SneakyThrowsprivate void populateField(Field field, Object target) {Class<?> declaringClass = field.getType();LoggerUtil.debug(logger, "field class:{0}", declaringClass);Object fieldValue = null;if (declaringClass.getName().contains("Service")) {fieldValue = createService(declaringClass);} else if ((declaringClass.isAnnotationPresent(EXPLORER.class))) {fieldValue = super.getMapper(declaringClass);} else {fieldValue = createObject(declaringClass);}field.setAccessible(true);field.set(target, fieldValue);LoggerUtil.info(logger, "inject field:{0} value:{1}", field.getName(), fieldValue);}private void addIntercepter() {if (this.added) {return;}for (DataSourceType dataSourceType : DataSourceType.values()) {getSqlSessionFactoryByType(dataSourceType);}//Map<DataSourceType, SqlSessionFactory> sqlSessionFactoryContext = getSqlSessionFactoryContext();//for (Entry<DataSourceType, SqlSessionFactory> entry : sqlSessionFactoryContext.entrySet()) {// SqlSessionFactory sqlSessionFactory = entry.getValue();// DataSourceType dataSourceType = entry.getKey();// PageInterceptor interceptor = new PageInterceptor();// Properties properties = new Properties();// properties.put("helperDialect", "mysql");// properties.put("reasonable", "true");// properties.put("supportMethodsArguments", "true");// properties.put("params", "count=countSql;pageNum=pageNumber;pageSize=pageSize;");// interceptor.setProperties(properties);// sqlSessionFactory.getConfiguration().addInterceptor(interceptor);// if (dataSourceType == DataSourceType.ADB) {// PartitionParamInterceptor partitionParamInterceptor = new PartitionParamInterceptor();// partitionParamInterceptor.setPartitionService(createService(PartitionService.class));// sqlSessionFactory.getConfiguration().addInterceptor(partitionParamInterceptor);// }//}this.added = true;}}
测试类只需要继承ServiceTest即可
