Spring Test

JDBC支持

JdbcTestUtils

  • countRowsInTable(..): counts the number of rows in the given table
  • countRowsInTableWhere(..): counts the number of rows in the given table, using the provided WHERE clause
  • deleteFromTables(..): deletes all rows from the specified tables
  • deleteFromTableWhere(..): deletes rows from the given table, using the provided WHERE clause
  • dropTables(..): drops the specified tables

创建内嵌数据库

@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .generateUniqueName(true)
            .setType(H2)
            .setScriptEncoding("UTF-8")
            .ignoreFailedDrops(true)
            .addScript("schema.sql")
            .addScripts("user_data.sql", "country_data.sql")
            .build();
    }
}

测试

public class DataAccessIntegrationTestTemplate {

        private EmbeddedDatabase db;

        @Before
        public void setUp() {
                // creates an HSQL in-memory database populated from default scripts
                // classpath:schema.sql and classpath:data.sql
                db = new EmbeddedDatabaseBuilder()
                                .generateUniqueName(true)
                                .addDefaultScripts()
                                .build();
        }

        @Test
        public void testDataAccess() {
                JdbcTemplate template = new JdbcTemplate(db);
                template.query( /* ... */ );
        }

        @After
        public void tearDown() {
                db.shutdown();
        }

}

注解

  • @BootstrapWith:一个用于配置Spring TestContext框架如何引导的类级别的注解。具体地说,@BootstrapWith用于指定一个自定义的TestContextBootstrapper
  • @ContextConfiguration:定义了类级别的元数据来决定如何为集成测试来加载和配置应用程序上下文。具体地说,@ContextConfiguration声明了用于加载上下文的应用程序上下文资源路径和注解类。
@ContextConfiguration(classes = TestConfig.class)
public class ConfigClassApplicationContextTests {
        // class body...
}

@ContextConfiguration("/test-config.xml")
public class XmlApplicationContextTests {
        // class body...
}

@ContextConfiguration(initializers = CustomContextIntializer.class)
public class ContextInitializerTests {
        // class body...
}

@ContextConfiguration(locations = "/test-context.xml", loader = CustomContextLoader.class)
public class CustomLoaderXmlApplicationContextTests {
        // class body...
}
  • @WebAppConfiguration:是一个用于声明集成测试所加载的ApplicationContext须是WebApplicationContext的类级别的注解。测试类的@WebAppConfiguration注解只是为了保证用于测试的WebApplicationContext会被加载,它使用”file:src/main/webapp”路径默认值作为web应用的根路径(即,资源基路径)。资源基路径用于幕后创建一个MockServletContext作为测试的WebApplicationContext的ServletContext。
@ContextConfiguration
@WebAppConfiguration
public class WebAppTests {
        // class body...
}
  • @ContextHierarchy:是一个用于为集成测试定义ApplicationContext层次结构的类级别的注解。@ContextHierarchy应该声明一个或多个@ContextConfiguration实例列表,其中每一个定义上下文层次结构的一个层次。下面的例子展示了在同一个测试类中@ContextHierarchy的使用方法。但是,@ContextHierarchy一样可以用于测试类的层次结构中。
@WebAppConfiguration
@ContextHierarchy({
        @ContextConfiguration(classes = AppConfig.class),
        @ContextConfiguration(classes = WebConfig.class)
})
public class WebIntegrationTests {
        // class body...
}
  • @ActiveProfiles:是一个用于当集成测试加载ApplicationContext的时候声明哪一个bean definition profiles被激活的类级别的注解。
@ContextConfiguration
@ActiveProfiles({"dev", "integration"})
public class DeveloperIntegrationTests {
        // class body...
}
  • @TestPropertySource:是一个用于为集成测试加载ApplicationContext时配置属性文件的位置和增加到Environment中的PropertySources集中的内联属性的类级别的注解。测试属性源比那些从系统环境或者Java系统属性以及通过@PropertySource或者编程方式声明方式增加的属性源具有更高的优先级。而且,内联属性比从资源路径加载的属性具有更高的优先级。
@ContextConfiguration
@TestPropertySource("/test.properties")
public class MyIntegrationTests {
        // class body...
}

@ContextConfiguration
@TestPropertySource(properties = { "timezone = GMT", "port: 4242" })
public class MyIntegrationTests {
        // class body...
}
  • @DirtiesContext:指明测试执行期间该Spring应用程序上下文已经被弄脏(也就是说通过某种方式被更改或者破坏——比如,更改单例bean的状态)。当应用程序上下文被标为”脏”,它将从测试框架缓存中被移除并关闭。因此,Spring容器将为随后需要同样配置元数据的测试而被重建。@DirtiesContext可以在同一个类或者类层次结构中的类级别和方法级别中使用。在这个场景下,应用程序上下文将在任意此注解的方法之前或之后以及当前测试类之前或之后被标为“脏”,这取决于配置的methodMode和classMode。
    这里写图片描述
  • @TestExecutionListeners:定义了一个类级别的元数据,用于配置需要用TestContextManager进行注册的TestExecutionListener实现。通常,@TestExecutionListeners与@ContextConfiguration一起使用。
@ContextConfiguration
@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class})
public class CustomTestExecutionListenerTests {
        // class body...
}
  • @Commit:指定事务性的测试方法在测试方法执行完成后对事务进行提交。@Commit可以用作@Rollback(false)的直接替代,以更好的传达代码的意图。和@Rollback一样,@Commit可以在类层次或者方法层级声明。
@Commit
@Test
public void testProcessWithoutRollback() {
     // ...
}
  • @Rollback:指明当测试方法执行完毕的时候是否对事务性方法中的事务进行回滚。如果为true,则进行回滚;否则,则提交(请参加@Commit)。在Spring TestContext框架中,集成测试默认的Rollback语义为true,即使你不显示的指定它。当被声明为方法级别的注解,则@Rollback为特定的方法指定回滚语义,并覆盖类级别的@Rollback和@Commit语义。
@Rollback(false)
@Test
public void testProcessWithoutRollback() {
        // ...
}
  • @BeforeTransaction指明通过Spring的@Transactional注解配置为需要在事务中执行的测试方法在事务开始之前先执行注解的void方法。从Spring框架4.3版本起,@BeforeTransaction方法不再需要为public并可能被声明为基于Java8的接口的默认方法。
@BeforeTransaction
void beforeTransaction() {
// logic to be executed before a transaction is started
}
  • @AfterTransaction指明通过Spring的@Transactional注解配置为需要在事务中执行的测试方法在事务结束之后执行注解的void方法。从Spring框架4.3版本起,@AfterTransaction方法不再需要为public并可能被声明为基于Java8的接口的默认方法。
  • @Sql:用于注解测试类或者测试方法,以让在集成测试过程中配置的SQL脚本能够在给定的的数据库中得到执行。
@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})
public void userTest {
        // execute code that relies on the test schema and test data
}
  • @SqlConfig:定义了用于决定如何解析和执行通过@Sql注解配置的SQL脚本。
@Test
@Sql(
        scripts = "/test-user-data.sql",
        config = @SqlConfig(commentPrefix = "`", separator = "@@")
)
public void userTest {
        // execute code that relies on the test data
}
  • @SqlGroup:是一个用于聚合几个@Sql注解的容器注解。@SqlGroup可以直接使用,通过声明几个嵌套的@Sql注解,也可以与Java8的可重复注解支持协同使用,即简单地在同一个类或方法上声明几个@Sql注解,隐式地产生这个容器注解。
@Test
@SqlGroup({
        @Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
        @Sql("/test-user-data.sql")
)}
public void userTest {
        // execute code that uses the test schema and test data
}
  • @Timed用于指明被注解的测试必须在指定的时限(毫秒)内结束。如果测试超过指定时限,就当作测试失败。时限包括测试方法本身所耗费的时间,包括任何重复(请查看@Repeat)及任意初始化和销毁所用的时间。Spring的@Timed注解与JUnit 4的@Test(timeout=…​)支持相比具有不同的语义。确切地说,由于在JUnit 4中处理方法执行超时的方式(也就是,在独立线程中执行该测试方法),如果一个测试方法执行时间太长,@Test(timeout=…​)将直接判定该测试失败。而Spring的@Timed则不直接判定失败而是等待测试完成。
@Timed(millis=1000)
public void testProcessWithOneSecondTimeout() {
        // some logic that should not take longer than 1 second to execute
}
  • @Repeat指明该测试方法需被重复执行。注解指定该测试方法被重复的次数。重复的范围包括该测试方法自身也包括相应的初始化和销毁方法。
@Repeat(10)
@Test
public void testProcessRepeatedly() {
        // ...
}

JUnit Jupiter扩展

  • @SpringJUnitConfig:是将@ExtendWith(SpringExtension.class) from JUnit Jupiter 和@ContextConfiguration组合起来的一个注解
@SpringJUnitConfig(TestConfig.class)
class ConfigurationClassJUnitJupiterSpringTests {
        // class body...
}
@SpringJUnitConfig(locations = "/test-config.xml")
class XmlJUnitJupiterSpringTests {
        // class body...
}
  • @SpringJUnitWebConfig:是将@ExtendWith(SpringExtension.class) from JUnit Jupiter 和@ContextConfiguration和@WebAppConfiguration 组合起来的一个注解
@SpringJUnitWebConfig(TestConfig.class)
class ConfigurationClassJUnitJupiterSpringWebTests {
        // class body...
}
@SpringJUnitWebConfig(locations = "/test-config.xml")
class XmlJUnitJupiterSpringWebTests {
        // class body...
}
  • @EnabledIf和@DisabledIf:使用spel表达式
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@EnabledIf(
    expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
    reason = "Enabled on Mac OS"
)
public @interface EnabledOnMac {}

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@DisabledIf(
    expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
    reason = "Disabled on Mac OS"
)
public @interface DisabledOnMac {}

Spring TestContext Framework

@RunWith(SpringRunner.class)

Spring MVC Test Framework

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringJUnitWebConfig(locations = "test-servlet-context.xml")
class ExampleTests {

   private MockMvc mockMvc;

   @BeforeEach
   void setup(WebApplicationContext wac) {
      this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
   }

   @Test
   void getAccount() throws Exception {
      this.mockMvc.perform(get("/accounts/1")
            .accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
         .andExpect(status().isOk())
         .andExpect(content().contentType("application/json"))
         .andExpect(jsonPath("$.name").value("Lee"));
   }

}


MockMVc mockMvc = standaloneSetup(new MusicController())
                .defaultRequest(get("/").accept(MediaType.APPLICATION_JSON))
                .alwaysExpect(status().isOk())
                .alwaysExpect(content().contentType("application/json;charset=UTF-8"))
                .build();

mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));

mockMvc.perform(multipart("/doc").file("a1", "ABC".getBytes("UTF-8")));

mockMvc.perform(get("/hotels").param("foo", "bar"));

mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build();
Spring 框架中,使用 Spring Test 编写单元测试可以极大地简化测试流程,并提高测试效率。Spring Test 提供了对 JUnit 和 Mockito 的集成支持,使得开发者可以在不启动整个 Spring 容器的情况下进行依赖注入和上下文管理。 ### 基本步骤 #### 1. 引入必要的依赖 为了使用 Spring Test 进行单元测试,首先需要在项目中引入相关的依赖。通常包括: - `spring-test`:提供 Spring 测试支持。 - `junit`:用于编写和运行测试用例。 - `mockito-core` 或 `mockito-inline`:用于创建和管理 Mock 对象。 以 Maven 为例,可以在 `pom.xml` 中添加如下依赖: ```xml <dependencies> <!-- Spring Test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.20</version> <scope>test</scope> </dependency> <!-- JUnit 5 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.9.3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.9.3</version> <scope>test</scope> </dependency> <!-- Mockito --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency> </dependencies> ``` #### 2. 配置测试类 在编写测试类时,可以通过注解来配置 Spring 上下文并注入 Bean。常用的注解包括: - `@ExtendWith(SpringExtension.class)`:启用 Spring Test 支持(适用于 JUnit 5)。 - `@ContextConfiguration`:指定 Spring 配置文件的位置。 - `@Autowired`:自动注入需要测试的 Bean。 以下是一个简单的示例,展示如何测试一个服务类: ```java import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = { "classpath:applicationContext.xml" }) public class MyServiceTest { @Autowired private MyService myService; @Test public void testCalculate() { int result = myService.calculate(2, 3); assertEquals(5, result); // 断言结果是否为预期值 } } ``` #### 3. 使用 Mockito 进行 Mock 测试 当测试某个组件时,如果它依赖于其他外部组件(如数据库、第三方 API 等),可以使用 Mockito 创建 Mock 对象来模拟这些依赖的行为。这样可以隔离外部因素,专注于测试目标逻辑。 例如,假设有一个 `UserService` 依赖于 `UserRepository`,可以通过 Mockito 来模拟 `UserRepository` 的行为: ```java import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(MockitoExtension.class) public class UserServiceTest { @Mock private UserRepository userRepository; @InjectMocks private UserService userService; @Test public void testGetUserById() { // 模拟 userRepository.findById 返回特定用户 User mockUser = new User(1L, "John Doe"); Mockito.when(userRepository.findById(1L)).thenReturn(mockUser); // 调用被测方法 User result = userService.getUserById(1L); // 验证结果 assertEquals("John Doe", result.getName()); } } ``` #### 4. 测试异常情况 在某些情况下,需要验证代码是否正确处理了异常。JUnit 提供了 `assertThrows` 方法来捕获预期的异常: ```java @Test public void testInvalidInput() { Exception exception = assertThrows(IllegalArgumentException.class, () -> { myService.calculate(-1, 3); }); assertEquals("Input must be positive", exception.getMessage()); } ``` #### 5. 使用 `@DataJpaTest` 或 `@WebMvcTest`(可选) 如果你使用的是 Spring Boot,还可以利用其提供的测试切片功能,例如: - `@DataJpaTest`:仅加载 JPA 相关的配置,适合测试数据访问层。 - `@WebMvcTest`:仅加载 Web 层相关的 Bean,适合测试控制器。 ```java import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(MyController.class) public class MyControllerTest { @Autowired private MockMvc mockMvc; @Test public void testHomePage() throws Exception { mockMvc.perform(get("/")) .andExpect(status().isOk()); } } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值