Spring Boot中如何确保测试完成后数据库清理?

问题描述 投票:0回答:1

我正在利用以下技术堆栈

spring-boot : 3.0.0
JUnit5
H2(In-Memory)

问题在于,即使测试完成后,数据仍然保留在数据库中。

我尝试在测试完成后使用 JUnit 5 的扩展来清理数据库内容。

但是,在执行测试时,我观察到

registMemberTestIfEveryThingsIsOk
方法中保存的数据仍然存在,导致
registMemberTestIfOauthPlatformHasInvalidValue
方法中的第二个断言抛出异常不会抛出任何内容。

测试代码

@SpringBootTest
@ExtendWith(DataClearExtension.class)
class AuthServiceTest {

    @Autowired
    private AuthService authService;
    @BeforeAll
    public static void initDataBase(@Autowired DataSource dataSource) {
        try (Connection conn = dataSource.getConnection()) {
            ScriptUtils.executeSqlScript(conn, new ClassPathResource("/sql/member_test.sql"));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
       ...


    @Nested
    class registMemberTest {
        @ParameterizedTest
        @CsvSource(value = {"123, google, yongjun, 213, kevinH, 20000928, male, student, red, img",
                "2, kakao, hannah, 314, alex, 19980512, female, engineer, blue, img",
                "3, apple, david, 415, emma, 20010320, male, designer, green, img",
                "4, kakao, sarah, 516, michael, 19991225, female, developer, yellow, img",
                "5, google, emily, 617, chris, 19921005, female, manager, orange, img"})
        void registMemberTestIfDuplicatedIdExist(@AggregateWith(MemberAggregator.class) MemberRegistRequestDto memberRegistRequestDto) {
            assertThrows(DataIntegrityViolationException.class, () -> authService.registMember(memberRegistRequestDto));
            
            Member member = authService.findMemberByOAuthId(memberRegistRequestDto.oauthId());
            assertAll(
                    () -> assertNotEquals(memberRegistRequestDto.name(), member.getName()),
                    () -> assertNotEquals(memberRegistRequestDto.nickname(), member.getNickname()),
                    () -> assertNotEquals(memberRegistRequestDto.birth(), member.getBirth())
            );
        }

        @ParameterizedTest
        @CsvSource(value = {"101, naver, yongjun, 213, kevin, 20000928, male, student, red, img",
                "102, git, hannah, 314, alex, 19980512, female, engineer, blue, img",
                "103, microsoft, david, 415, emma, 20010320, male, designer, green, img",
                "104, samsung, sarah, 516, michael, 19991225, female, developer, yellow, img",
                "105, wooabros, emily, 617, chris, 19921005, female, manager, orange, img"})
        void registMemberTestIfoauthPlatformHasInvalidValue(@AggregateWith(MemberAggregator.class) MemberRegistRequestDto memberRegistRequestDto) {
            assertAll(
                    () -> assertThrows(UnknownOAuthPlatformException.class, () -> authService.registMember(memberRegistRequestDto)),
                    
                    //the error ocurred here!
                    () -> assertThrows(MemberSigninException.class, () -> authService.findMemberByOAuthId(memberRegistRequestDto.oauthId()))
            );
        }

    }

      ...


    static class MemberAggregator implements ArgumentsAggregator {
        @Override
        public Object aggregateArguments(ArgumentsAccessor argumentsAccessor, ParameterContext parameterContext) throws ArgumentsAggregationException {
            return new MemberRegistRequestDto(argumentsAccessor.getString(0), argumentsAccessor.getString(1), argumentsAccessor.getString(2), argumentsAccessor.getString(3),
                    argumentsAccessor.getString(4), argumentsAccessor.getInteger(5), argumentsAccessor.getString(6), argumentsAccessor.getString(7), argumentsAccessor.getString(8),
                    argumentsAccessor.getString(9));
        }
    }
}

member_test.sql

CREATE TABLE member
(
    id                 BIGINT       NOT NULL AUTO_INCREMENT,
    oauth_id           VARCHAR(255) NOT NULL,
    oauth_platform     ENUM ('APPLE', 'GOOGLE', 'KAKAO'),
    name               VARCHAR(255) NOT NULL,
    profile_img        VARCHAR(255),
    nickname           VARCHAR(255) NOT NULL,
    birth              INT,
    gender             VARCHAR(255),
    profession         VARCHAR(255),
    signature_color    VARCHAR(255) NOT NULL,
    create_date        DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    last_modified_date DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY oauth_id (oauth_id)
);

INSERT INTO member (oauth_id, oauth_platform, name, profile_img, nickname, birth, gender, profession, signature_color)
VALUES ('123', 'GOOGLE', 'hong', '123123', 'kevin', '0928', 'male', 'student', 'red'),
       ('2', 'GOOGLE', 'muny', '123123', 'anna', '0929', 'female', 'developer', 'blue'),
       ('3', 'GOOGLE', 'kim', '123123', 'jenny', '0930', 'female', 'professor', 'black'),
       ('4', 'GOOGLE', 'karina', '123123', 'katarina', '1000', 'female', 'developer', 'pink'),
       ('5', 'GOOGLE', 'down', '123123', 'mark', '1001', 'female', 'teacher', 'skyblue');

public class DataClearExtension implements BeforeEachCallback {
    @Override
    public void beforeEach(ExtensionContext extensionContext) throws Exception {
        DataCleaner dataCleaner = getDataCleaner(extensionContext);
        dataCleaner.clear();
    }

    private DataCleaner getDataCleaner(ExtensionContext extensionContext) {
        return SpringExtension.getApplicationContext(extensionContext)
                .getBean(DataCleaner.class);
    }
}
@Component
public class DataCleaner {

    private static final String FOREIGN_KEY_CHECK_FORMAT = "SET FOREIGN_KEY_CHECKS %d";
    private static final String TRUNCATE_FORMAT = "TRUNCATE TABLE %s";

    private final List<String> tableNames = new ArrayList<>();

    @PersistenceContext
    private EntityManager entityManager;

    @PostConstruct
    public void findDatabaseTableNames() {
        List<Object[]> tableInfos = entityManager.createNativeQuery("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='PUBLIC'").getResultList();
        for (Object[] tableInfo : tableInfos) {
            String tableName = (String) tableInfo[0];
            tableNames.add(tableName);
        }
    }

    @Transactional
    public void clear() {
        entityManager.clear();
        truncate();
    }

    private void truncate() {
        entityManager.createNativeQuery(String.format(FOREIGN_KEY_CHECK_FORMAT, 0)).executeUpdate();
        for (String tableName : tableNames) {
            entityManager.createNativeQuery(String.format(TRUNCATE_FORMAT, tableName)).executeUpdate();
        }
        entityManager.createNativeQuery(String.format(FOREIGN_KEY_CHECK_FORMAT, 1)).executeUpdate();
    }

yaml

spring:
  datasource :
    url: jdbc:h2:mem:test;MODE=MySQL
    driverClassName: org.h2.Driver
    username: sa
    password:
  jpa:
    hibernate:
      ddl-auto: none

调试后发现

findDatabaseTableNames
类中的
DataCleaner
方法无法获取表名,因为tableInfo的大小为0。

因此,我认为问题可能在于无法从 H2 检索表。但是,如果我在其他地方犯了任何其他错误,请告诉我。

我读过的文档

  1. JUnit、JPA 和 Spring:如何确保单元测试完成后数据库保持干净
  2. 内存数据库中的H2不显示在Spring Boot中创建的表

希望您度过愉快的一天。感谢您阅读长文。

spring-boot h2 junit5
1个回答
0
投票

尝试添加注解@TestInstance(TestInstance.Lifecycle.PER_CLASS)并将@BerforeAll方法转换为非静态并添加@AfterAll方法,如下所示:

@SpringBootTest
@ExtendWith(DataClearExtension.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class AuthServiceTest {

    @Autowired
    private AuthService authService;
    @BeforeAll
    public void initDataBase(@Autowired DataSource dataSource) {
        try (Connection conn = dataSource.getConnection()) {
            ScriptUtils.executeSqlScript(conn, new ClassPathResource("/sql/member_test.sql"));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @AfterAll
    public void cleanUpDataBase(@Autowired DataSource dataSource) {
        // clean up code goes here
    }    

@Nested
    class registMemberTest {
        @ParameterizedTest
        @CsvSource(value = {"123, google, yongjun, 213, kevinH, 20000928, male, student, red, img",
                "2, kakao, hannah, 314, alex, 19980512, female, engineer, blue, img",
                "3, apple, david, 415, emma, 20010320, male, designer, green, img",
                "4, kakao, sarah, 516, michael, 19991225, female, developer, yellow, img",
                "5, google, emily, 617, chris, 19921005, female, manager, orange, img"})
        void registMemberTestIfDuplicatedIdExist(@AggregateWith(MemberAggregator.class) MemberRegistRequestDto memberRegistRequestDto) {
            assertThrows(DataIntegrityViolationException.class, () -> authService.registMember(memberRegistRequestDto));
            
            Member member = authService.findMemberByOAuthId(memberRegistRequestDto.oauthId());
            assertAll(
                    () -> assertNotEquals(memberRegistRequestDto.name(), member.getName()),
                    () -> assertNotEquals(memberRegistRequestDto.nickname(), member.getNickname()),
                    () -> assertNotEquals(memberRegistRequestDto.birth(), member.getBirth())
            );
        }

        @ParameterizedTest
        @CsvSource(value = {"101, naver, yongjun, 213, kevin, 20000928, male, student, red, img",
                "102, git, hannah, 314, alex, 19980512, female, engineer, blue, img",
                "103, microsoft, david, 415, emma, 20010320, male, designer, green, img",
                "104, samsung, sarah, 516, michael, 19991225, female, developer, yellow, img",
                "105, wooabros, emily, 617, chris, 19921005, female, manager, orange, img"})
        void registMemberTestIfoauthPlatformHasInvalidValue(@AggregateWith(MemberAggregator.class) MemberRegistRequestDto memberRegistRequestDto) {
            assertAll(
                    () -> assertThrows(UnknownOAuthPlatformException.class, () -> authService.registMember(memberRegistRequestDto)),
                    
                    //the error ocurred here!
                    () -> assertThrows(MemberSigninException.class, () -> authService.findMemberByOAuthId(memberRegistRequestDto.oauthId()))
            );
        }

    }

      ...


    static class MemberAggregator implements ArgumentsAggregator {
        @Override
        public Object aggregateArguments(ArgumentsAccessor argumentsAccessor, ParameterContext parameterContext) throws ArgumentsAggregationException {
            return new MemberRegistRequestDto(argumentsAccessor.getString(0), argumentsAccessor.getString(1), argumentsAccessor.getString(2), argumentsAccessor.getString(3),
                    argumentsAccessor.getString(4), argumentsAccessor.getInteger(5), argumentsAccessor.getString(6), argumentsAccessor.getString(7), argumentsAccessor.getString(8),
                    argumentsAccessor.getString(9));
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.