2024-08-06
One-liner
雁子南飞时,故人忆往事。 --- 《醉柳引·故人安在兮》 · 月雩
Springboot mock database
@SpringBootTest
@AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.NONE)
@AutoConfigureMockMvc
public class ReaderControllerTest {
@MockBean
private ReaderRepository readerRepository;
@Autowired
private MockMvc mockMvc;
@Test
void createReaderTest() throws Exception {
var reader = ReaderData.getReader();
when(readerRepository.findByEmail(anyString())).thenReturn(Optional.empty());
when(readerRepository.save(null)).thenReturn(reader);
var json = """
{ "name": "%s", "email": "%s" }
""".formatted(reader.getName(), reader.getEmail());
mockMvc
.perform(
post("/user").contentType("application/json").content(json))
.andExpect(status().isOk());
}
}
Spock vs JUnit
Spock 和 JUnit 都是 Java 测试框架,但它们在设计理念、特性和使用方式上有一些显著的区别。
共同点
- 测试框架:两者都是用于编写和运行自动化测试的框架。
- 集成工具:都可以与常见的构建工具(如 Maven 和 Gradle)集成。
- 单元测试支持:两者都支持编写单元测试。
不同点
1. 语法与语言特性
- Spock:使用 Groovy 语言编写测试,这使得测试代码更简洁和更具可读性。Spock 测试规范 (
Specification
) 是基于 Groovy DSL 的。class MySpec extends Specification {
def "feature method"() {
expect:
1 + 1 == 2
}
} - JUnit:使用 Java 语言编写测试。JUnit 的测试类和方法通过注解来标识。
public class MyTest {
@Test
public void testAddition() {
assertEquals(2, 1 + 1);
}
}
2. 测试生命周期
- Spock:使用
setup
,setupSpec
,cleanup
,cleanupSpec
方法来定义测试的生命周期方法。class MySpec extends Specification {
def setup() { /* 每个测试方法前执行 */ }
def cleanup() { /* 每个测试方法后执行 */ }
def setupSpec() { /* 类加载后执行一次 */ }
def cleanupSpec() { /* 类卸载前执行一次 */ }
} - JUnit:使用
@Before
,@After
,@BeforeClass
,@AfterClass
注解来定义生命周期方法。public class MyTest {
@Before
public void setUp() { /* 每个测试方法前执行 */ }
@After
public void tearDown() { /* 每个测试方法后执行 */ }
@BeforeClass
public static void beforeAll() { /* 类加载后执行一次 */ }
@AfterClass
public static void afterAll() { /* 类卸载前执行一次 */ }
}
3. 行为驱动开发(BDD)支持
- Spock:内置支持 BDD,提供了
given-when-then
的测试结构,使得测试用例更加可读和表达性更强。class MySpec extends Specification {
def "addition should work correctly"() {
given: "two numbers"
int a = 1
int b = 1
when: "they are added"
int result = a + b
then: "the result is correct"
result == 2
}
} - JUnit:通过第三方库(如 Cucumber)支持 BDD,但本身不直接支持
given-when-then
结构。public class MyTest {
@Test
public void testAddition() {
// given
int a = 1;
int b = 1;
// when
int result = a + b;
// then
assertEquals(2, result);
}
}
4. 参数化测试
- Spock:内置参数化测试支持,通过
where
块可以轻松定义多组测试数据。class MySpec extends Specification {
def "addition should work correctly"() {
expect:
a + b == result
where:
a | b || result
1 | 1 || 2
2 | 3 || 5
}
} - JUnit:需要使用
@ParameterizedTest
注解和其他注解(如@ValueSource
、@CsvSource
)来定义参数化测试。@ParameterizedTest
@CsvSource({
"1, 1, 2",
"2, 3, 5"
})
public void testAddition(int a, int b, int result) {
assertEquals(result, a + b);
}
5. Mocking
- Spock:内置强大的 mocking 支持,可 以直接在测试中使用
Mock
、Stub
、Spy
等。class MySpec extends Specification {
def "should use mock"() {
given:
def mock = Mock(MyService)
when:
mock.doSomething()
then:
1 * mock.doSomething()
}
} - JUnit:需要使用第三方 mocking 框架(如 Mockito)来实现 mocking 功能。
@ExtendWith(MockitoExtension.class)
public class MyTest {
@Mock
MyService myService;
@Test
public void testMock() {
myService.doSomething();
verify(myService, times(1)).doSomething();
}
}
总结
- Spock:以简洁、可读性强的语法和内置的 BDD 支持为特点,适合需要编写复杂测试逻辑和希望测试代码更具表达力的开发者。
- JUnit:更加传统和广泛使用的 Java 测试框架,适合希望保持 Java 语法一致性和依赖最小化的开发者。
两者各有优劣,选择哪一个取决于项目需求和团队偏好。