官方文档

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/

注意:将业务逻辑写在 try 块中。
image.png

如何不启动spring整体工程,在本地实际连db测试dao层代码?

方法思路:

  • 采用jdbc 方式直连
  • 采用反射机制 动态 实例化类 及其属性

mybatis sqlSession相关解释见 https://www.yuque.com/antone/zv3ypu/tl15md

  1. @Slf4j
  2. public class MutiDataSourceTest {
  3. private static final Logger LOGGER = LoggerFactory.getLogger(MutiDataSourceTest.class);
  4. private static Map<DataSourceType, DataSource> dataSourceContext = new HashMap<>();
  5. //@Getter
  6. private static Map<DataSourceType, SqlSessionFactory> sqlSessionFactoryContext = new HashMap<>();
  7. private static Map<DataSourceType, Properties> dataSourceTypeToConfig = new HashMap<>();
  8. private static Map<Class<? extends Annotation>, DataSourceType> classToDataSourceType = new HashMap<>();
  9. private static List<SqlSession> sqlSessions = new ArrayList<>();
  10. public void logObject(Object o) {
  11. //LOGGER.info("obj:{}", o);
  12. }
  13. static {
  14. classToDataSourceType.put(EXPLORER.class, DataSourceType.EXPLORE);
  15. // 数据库配置信息
  16. Properties explorerConfig = new Properties();
  17. explorerConfig.put("driver", "com.mysql.jdbc.Driver");
  18. explorerConfig.put("url",
  19. "jdbc:mysql://xxxx:3306/cheetah?useUnicode=true&characterEncoding=utf8"
  20. + "&rewriteBatchedStatements=true");
  21. explorerConfig.put("username", "xxx");
  22. explorerConfig.put("password", "xxx");
  23. LoggerUtil.info(LOGGER, "adb config:{0}", explorerConfig);
  24. dataSourceTypeToConfig.put(DataSourceType.EXPLORE, explorerConfig);
  25. }
  26. public static enum DataSourceType {
  27. EXPLORE
  28. }
  29. public static SqlSession openSession(DataSourceType dataSourceType) {
  30. SqlSessionFactory sqlSessionFactory = getSqlSessionFactoryByType(dataSourceType);
  31. SqlSession sqlSession = sqlSessionFactory.openSession(null, true);
  32. sqlSessions.add(sqlSession);
  33. return sqlSession;
  34. }
  35. public static <T> T getMapper(Class<T> tClass) {
  36. DataSourceType dataSourceType = parseType(tClass);
  37. SqlSessionFactory sqlSessionFactory = getSqlSessionFactoryByType(dataSourceType);
  38. MapperRegistry mapperRegistry = sqlSessionFactory.getConfiguration().getMapperRegistry();
  39. if (!mapperRegistry.hasMapper(tClass)) {
  40. sqlSessionFactory.getConfiguration().addMapper(tClass);
  41. }
  42. SqlSession sqlSession = openSession(dataSourceType);
  43. return sqlSession.getMapper(tClass);
  44. }
  45. public static SqlSessionFactory getSqlSessionFactoryByType(DataSourceType dataSourceType) {
  46. if (!sqlSessionFactoryContext.containsKey(dataSourceType)) {
  47. DataSource dataSource = getDataSourceByType(dataSourceType);
  48. Configuration configuration = new Configuration();
  49. JdbcTransactionFactory jdbcTransactionFactory = new JdbcTransactionFactory();
  50. Environment environment = new Environment("test", jdbcTransactionFactory, dataSource);
  51. configuration.setEnvironment(environment);
  52. configuration.getTypeAliasRegistry().registerAliases("com.alibaba.aliexpress.module");
  53. configuration.setMapUnderscoreToCamelCase(true);
  54. MybatisProperties mybatisProperties = new MybatisProperties();
  55. mybatisProperties.setMapperLocations(
  56. new String[] {"classpath*:com/alipay/xxxx/dal/xxx/mapper/*.xml"}); //这里是mapper配置的地址
  57. Resource[] resources = mybatisProperties.resolveMapperLocations();
  58. for (Resource mapperLocation : resources) {
  59. XMLMapperBuilder xmlMapperBuilder = null;
  60. try {
  61. xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
  62. configuration, mapperLocation.toString(), configuration.getSqlFragments());
  63. } catch (IOException e) {
  64. e.printStackTrace();
  65. }
  66. xmlMapperBuilder.parse();
  67. }
  68. DefaultSqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
  69. //LOGGER.info("{} sqlSessionFactory init success.", dataSourceType);
  70. sqlSessionFactoryContext.put(dataSourceType, sqlSessionFactory);
  71. //addIntercepter(sqlSessionFactory, dataSourceType);
  72. }
  73. return sqlSessionFactoryContext.get(dataSourceType);
  74. }
  75. public static DataSource getDataSourceByType(DataSourceType dataSourceType) {
  76. if (!dataSourceContext.containsKey(dataSourceType)) {
  77. DataSource dataSource = buildDataSouce(dataSourceType);
  78. dataSourceContext.put(dataSourceType, dataSource);
  79. //LOGGER.info("{} dataSource init success.", dataSourceType);
  80. }
  81. return dataSourceContext.get(dataSourceType);
  82. }
  83. private static DataSource buildDataSouce(DataSourceType dataSourceType) {
  84. Properties properties = dataSourceTypeToConfig.get(dataSourceType);
  85. UnpooledDataSource dataSource = new UnpooledDataSource();
  86. dataSource.setDriver(properties.getProperty("driver"));
  87. dataSource.setUrl(properties.getProperty("url"));
  88. dataSource.setUsername(properties.getProperty("username"));
  89. dataSource.setPassword(properties.getProperty("password"));
  90. return dataSource;
  91. }
  92. private static <T> DataSourceType parseType(Class<T> tClass) {
  93. Set<Class<? extends Annotation>> classes = classToDataSourceType.keySet();
  94. for (Class<? extends Annotation> anotationClass : classes) {
  95. if (anotationClass == Mapper.class) {
  96. continue;
  97. }
  98. if (tClass.isAnnotationPresent(anotationClass)) {
  99. return classToDataSourceType.get(anotationClass);
  100. }
  101. }
  102. //return DataSourceType.TDDL;
  103. return null;
  104. }
  105. @AfterClass
  106. public static void destroy() {
  107. sqlSessions.forEach(s -> {
  108. if (s != null) {
  109. s.close();
  110. }
  111. });
  112. }
  113. }
  1. public class ServiceTest extends MutiDataSourceTest {
  2. private static final Logger logger = LoggerFactory.getLogger(ServiceTest.class);
  3. private boolean added;
  4. @Before
  5. public void serviceTestInit() {
  6. beforeInit();
  7. populateField(this);
  8. //afterPopulate(this);
  9. }
  10. private void beforeInit() {
  11. addIntercepter();
  12. }
  13. public <T> T createService(Class<T> tClass) {
  14. return (T)createObject(tClass);
  15. }
  16. private Object createObject(Class tClass) {
  17. try {
  18. //log.debug("create instance for class:{}", tClass.getName());
  19. Class c = findImplClass(tClass);
  20. if (c == null) {
  21. return null;
  22. }
  23. Object o = c.newInstance();
  24. populateField(o);
  25. //afterPopulate(o);
  26. return o;
  27. } catch (Exception e) {
  28. throw new RuntimeException(e);
  29. }
  30. }
  31. private void afterPopulate(Object o) {
  32. //if (o instanceof BaseServiceImpl) {
  33. // try {
  34. // initBaseImpl(o);
  35. // } catch (InvocationTargetException e) {
  36. // e.printStackTrace();
  37. // } catch (IllegalAccessException e) {
  38. // e.printStackTrace();
  39. // }
  40. //}
  41. }
  42. private void populateField(Object o) {
  43. Field[] declaredFields = o.getClass().getDeclaredFields();
  44. Arrays.stream(declaredFields).filter(
  45. field -> field.isAnnotationPresent(Autowired.class) || field.isAnnotationPresent(
  46. Resource.class)).forEach(f -> populateField(f, o));
  47. }
  48. private <T> Class findImplClass(Class<T> tClass) {
  49. if (tClass.isInterface()) {
  50. String className = tClass.getSimpleName() + "Impl";
  51. String packageName = tClass.getName().replace(tClass.getSimpleName(), "impl.");
  52. String implClassName = packageName + className;
  53. //String implClassName2 = packageName + className2;
  54. //log.debug("find impl class for:{} is {}", tClass.getName(), implClassName);
  55. Class<?> aClass = null;
  56. try {
  57. aClass = Class.forName(implClassName);
  58. } catch (ClassNotFoundException e) {
  59. LoggerUtil.warn(logger, "not found impl class for:{0},skip create instance", tClass.getName());
  60. // e.printStackTrace();
  61. }
  62. return aClass;
  63. }
  64. return tClass;
  65. }
  66. @SneakyThrows
  67. private void populateField(Field field, Object target) {
  68. Class<?> declaringClass = field.getType();
  69. LoggerUtil.debug(logger, "field class:{0}", declaringClass);
  70. Object fieldValue = null;
  71. if (declaringClass.getName().contains("Service")) {
  72. fieldValue = createService(declaringClass);
  73. } else if ((declaringClass.isAnnotationPresent(EXPLORER.class))) {
  74. fieldValue = super.getMapper(declaringClass);
  75. } else {
  76. fieldValue = createObject(declaringClass);
  77. }
  78. field.setAccessible(true);
  79. field.set(target, fieldValue);
  80. LoggerUtil.info(logger, "inject field:{0} value:{1}", field.getName(), fieldValue);
  81. }
  82. private void addIntercepter() {
  83. if (this.added) {
  84. return;
  85. }
  86. for (DataSourceType dataSourceType : DataSourceType.values()) {
  87. getSqlSessionFactoryByType(dataSourceType);
  88. }
  89. //Map<DataSourceType, SqlSessionFactory> sqlSessionFactoryContext = getSqlSessionFactoryContext();
  90. //for (Entry<DataSourceType, SqlSessionFactory> entry : sqlSessionFactoryContext.entrySet()) {
  91. // SqlSessionFactory sqlSessionFactory = entry.getValue();
  92. // DataSourceType dataSourceType = entry.getKey();
  93. // PageInterceptor interceptor = new PageInterceptor();
  94. // Properties properties = new Properties();
  95. // properties.put("helperDialect", "mysql");
  96. // properties.put("reasonable", "true");
  97. // properties.put("supportMethodsArguments", "true");
  98. // properties.put("params", "count=countSql;pageNum=pageNumber;pageSize=pageSize;");
  99. // interceptor.setProperties(properties);
  100. // sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
  101. // if (dataSourceType == DataSourceType.ADB) {
  102. // PartitionParamInterceptor partitionParamInterceptor = new PartitionParamInterceptor();
  103. // partitionParamInterceptor.setPartitionService(createService(PartitionService.class));
  104. // sqlSessionFactory.getConfiguration().addInterceptor(partitionParamInterceptor);
  105. // }
  106. //}
  107. this.added = true;
  108. }
  109. }

测试类只需要继承ServiceTest即可