Skip to main content

2024-08-06

One-liner

雁子南飞时,故人忆往事。 --- 《醉柳引·故人安在兮》 · 月雩


Spring Boot Mock Database

Here's an example of a Spring Boot test with a 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 and JUnit are both Java testing frameworks, but they have some significant differences in design, features, and usage.

Commonalities

  • Testing Frameworks: Both are used for writing and running automated tests.
  • Integration Tools: Both can be integrated with common build tools like Maven and Gradle.
  • Unit Test Support: Both support writing unit tests.

Differences

1. Syntax and Language Features

  • Spock: Uses Groovy, making test code more concise and readable. Spock specs (Specification) are based on Groovy DSL.
    class MySpec extends Specification {
    def "feature method"() {
    expect:
    1 + 1 == 2
    }
    }
  • JUnit: Uses Java. Test classes and methods are annotated.
    public class MyTest {
    @Test
    public void testAddition() {
    assertEquals(2, 1 + 1);
    }
    }

2. Test Lifecycle

  • Spock: Uses setup, setupSpec, cleanup, cleanupSpec for defining lifecycle methods.
    class MySpec extends Specification {
    def setup() { /* runs before every test method */ }
    def cleanup() { /* runs after every test method */ }
    def setupSpec() { /* runs once before the first test method */ }
    def cleanupSpec() { /* runs once after the last test method */ }
    }
  • JUnit: Uses @Before, @After, @BeforeClass, @AfterClass annotations.
    public class MyTest {
    @Before
    public void setUp() { /* runs before every test method */ }
    @After
    public void tearDown() { /* runs after every test method */ }
    @BeforeClass
    public static void beforeAll() { /* runs once before the first test method */ }
    @AfterClass
    public static void afterAll() { /* runs once after the last test method */ }
    }

3. Behavior Driven Development (BDD) Support

  • Spock: Built-in BDD support with given-when-then structure, making tests more readable and expressive.
    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: BDD is supported via third-party libraries like Cucumber, not directly with 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. Parameterized Tests

  • Spock: Built-in support for parameterized tests using the where block.
    class MySpec extends Specification {
    def "addition should work correctly"() {
    expect:
    a + b == result

    where:
    a | b || result
    1 | 1 || 2
    2 | 3 || 5
    }
    }
  • JUnit: Uses @ParameterizedTest annotation with other annotations like @ValueSource and @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: Powerful built-in mocking support with Mock, Stub, Spy.
    class MySpec extends Specification {
    def "should use mock"() {
    given:
    def mock = Mock(MyService)

    when:
    mock.doSomething()

    then:
    1 * mock.doSomething()
    }
    }
  • JUnit: Requires third-party mocking frameworks like Mockito.
    @ExtendWith(MockitoExtension.class)
    public class MyTest {
    @Mock
    MyService myService;

    @Test
    public void testMock() {
    myService.doSomething();
    verify(myService, times(1)).doSomething();
    }
    }

Summary

  • Spock: Known for its concise, readable syntax and built-in BDD support. Great for complex testing logic and expressive test code.
  • JUnit: Traditional and widely used Java testing framework. Good for maintaining Java syntax consistency and minimal dependencies.

Both have their strengths and weaknesses. The choice between them depends on your project needs and team preferences.