Spring Boot Integration 测试

2016/2/1 posted in  Java学习  
  1. 新建一个测试配置类 TestApplication

    @Configuration
    @EnableAutoConfiguration
    @ComponentScan(
          excludeFilters = @ComponentScan.Filter(value = {
                  Application.class,
                  ApplicationChecker.class
          }, type = FilterType.ASSIGNABLE_TYPE))
    public class TestApplication {
      // 配置测试使用内存数据库
      @Bean
      public DataSource dataSource() {
          return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
      }
    }
    
  1. 新建一个测试公共类 CommonBeanIntegrationTest

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = TestApplication.class)
    // 重新加载 Spring 上下文
    // @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
    public abstract class CommonBeanIntegrationTest {
      @After
      public void emptyDatabases() {
          // 清空数据库,运行速度相对于每次重新加载 Spring 上下文快很多
      }
    }
    
  2. 正式开始写测试

    • 常规测试类结构
    public class UserTest extends CommonBeanIntegrationTest {
        @Autowired
        private UserAccessor userAccessor; // 准备测试的 Bean
    
        @Autowired
        private UserRepository userRepository; // 测试中需要用到的辅助 Bean
    
        @Before
        public void setUp() {
            // 每个测试开始前的操作
        }
    
        @Test
        public void should_save_user_with_username() {
            // given
            String username = "tangjiujun";
    
            // when
            userAccessor.save(username);
    
            // then
            assertThat(userRepository.findOne(1).getUsername, is(username));
        }
    
        @After
        public void tearDown() {
            // 每个测试执行后的操作
        }
    }
    
    • 使用 Mock 时测试类结构
    @RunWith(MockitoJUnitRunner.class)
    public class HttpClientTest {
        @Mock
        private ServerProperties serverProperties;
        @InjectMocks // 把 @Mock 注解的对象注入到 httpClient 中
        private HttpClient httpClient;
    
        @Test
        public void should_get_correct_port(){
            when(serverProperties.getPort()).thenReturn(8080);
            assertThat(httpClient.get(ApiUrl.list_node).getPort(), is(8080));
        }
    }
    
    • 混合使用前两种
    public class UserTest extends CommonBeanIntegrationTest {
        @Autowired
        private UserRepository userRepository;
        @Mock
        private HttpClient httpClient;
        @Autowired
        @InjectMocks
        private UserAccessor userAccessor; // 准备测试的 Bean
    
        @Before
        public void setUp() {
            MockitoAnnotations.initMocks(this); // 初始化并注入 @Mock 注解的对象
            // 强制将 Mock 的 httpClient 对象注入 userAccessor 中
            // ReflectionTestUtils.setField(userAccessor, "httpClient", httpClient);
        }
    
        @Test
        public void test_something(){
            // Test Code
        }
    }
    

    同时在需要测试的 Bean 上使用 @Autowired 和 @InjectMocks 注解后,可以达到只 Mock 需要 Mock 的对象,而其它对象依然是注入的真实对象的效果。
    但是有时候可能会出现我们 Mock 的对象还是注入的真实对象的情况,此时可以使用 Spring Boot 提供的 ReflectionTestUtils 在 @Before 中强制手动设置需要 Mock 的对象的值。