spring 启动应用程序中用于集成测试的内存数据库配置 (HSQLDB)

In-memory database configuration (HSQLDB) for integration testing in spring boot app

我开发了一个系统,其基础设施层是在 Spring 启动时开发的(安全、数据 JPA、MVC ...)。在 运行 时,系统连接到 MySQL,其设置在 src/main/resources/application.properties 以及包含一些用户插入和身份验证角色的 .sql。
对于集成测试,我决定使用 HSQLDB 来隔离数据并执行 'safe' 测试。为此,我创建了 class AbstractIntegrationTest,其中包含创建和清理表的方法以及从控制器进行测试的方法。所有测试 classes 扩展它:(我隐藏了不涉及数据库的方法)

@WebAppConfiguration
@ContextConfiguration(classes={LibraryManagerContextConfiguration.class, WebSecurityConfig.class})
public class AbstractIntegrationTest {

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Autowired
    private LoginFilter loginFilter;

    private MockMvc mockMvc;

    private static IDatabaseConnection databaseConnection;
    private static Connection connection;
    private static boolean isAfterFirstRun;
    private static Logger logger = LogManager.getLogger(AbstractIntegrationTest.class);

    @BeforeClass
    public static void createDatabase() throws Exception {
        try {
            final Properties properties = loadProperties();

            final String driver = properties.getProperty("datasource.driver");
            final String url = properties.getProperty("datasource.url");
            final String userName = properties.getProperty("datasource.username");
            final String password = properties.getProperty("datasource.password");
            final String schema = properties.getProperty("datasource.schema");

            Class.forName(driver);
            connection = DriverManager.getConnection(url, userName, password);
            databaseConnection = new HsqldbConnection(connection, schema);

        } catch (final SQLException exception) {
            throw new RuntimeException(exception.getMessage(), exception);
        } catch (final ClassNotFoundException exception) {
            throw new RuntimeException(exception.getMessage(), exception);
        }
    }

    @Before
    public void setDatabaseUp() throws Exception {
        if (!isAfterFirstRun) {
            runSQLCommands(getDataSetupFile());
        }
        runSQLCommands(getClearDatabaseFile());
        runSQLCommands(getResetSequencesFile());
        runSQLCommands(getDataFile());
        isAfterFirstRun = true;
    }

    @AfterClass
    public static void closeConnection() throws Exception {
        connection.close();
        databaseConnection.close();
    }

    protected void runSQLCommands(final String file) throws Exception {
        if (file != null) {
            final InputStream stream = getSQLInputStream(file);
            final BufferedReader databaseReader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));

            int i = 1;
            String sqlStatement = null;
            while ((sqlStatement = databaseReader.readLine()) != null) {
                if (sqlStatement.startsWith("--")) {
                    i++;
                    continue;
                }
                final int index = sqlStatement.lastIndexOf(";");
                if (index > -1) {
                    sqlStatement = sqlStatement.substring(0, index + 1);
                }
                if (sqlStatement.trim().length() != 0) {
                    try {
                        connection.createStatement().execute(sqlStatement);
                        logger.info(sqlStatement);
                    } catch (final Exception exception) {
                        logger.error("Error running command on line " + i + " of file " + file + ": " + exception.getMessage());
                        throw new RuntimeException(exception);
                    }
                }
                i++;
            }
        }
    }

    protected IDatabaseConnection getConnection() {
        return databaseConnection;
    }

    protected static IDataSet getDataSet(final String dataset) {
        try {
            final InputSource source = new InputSource(AbstractIntegrationTest.class.getResourceAsStream(dataset));
            return new FlatXmlDataSetBuilder().build(source);
        } catch (final Exception exception) {
            throw new RuntimeException("Cannot read the dataset file " + dataset + "!", exception);
        }
    }

    private static Properties loadProperties() throws Exception {
        final InputStream stream = ClassLoader.getSystemResourceAsStream("datasource.properties");
        if (stream == null) {
            throw new FileNotFoundException("File erm.properties not found. A file named erm.properties must be present "
                    + "in the src/test/resources folder of the project whose class is being tested.");
        }
        final Properties properties = new Properties();
        properties.load(stream);
        return properties;
    }

    private static InputStream getSQLInputStream(final String fileName) {
        return AbstractIntegrationTest.class.getResourceAsStream(fileName);
    }

    protected String getClearDatabaseFile() {
        return "/database/clear-database.sql";
    }

    protected String getDataSetupFile() {
        return "/database/database-setup.sql";
    }

    protected String getDataFile() {
        return "/database/data.sql";
    }

    protected String getResetSequencesFile() {
        return "/database/reset-sequences.sql";
    }

}

LibraryManagerContextConfigurationWebSecurityConfig classes 包含域和基础结构 bean 声明,因此它们构成了 spring 上下文。

此 class 在 src/test/java 中,datasource.properties 文件连同测试 .sql 在 src/test/resources 中。测试 classes 运行 完美,测试脚本 运行,表已创建,但是当存储库在测试期间查找某些数据时,它搜索 MySQL 而不是 HSQLDB .这是一个测试 class:

@RunWith(SpringJUnit4ClassRunner.class)
public class AuthenticationIntegrationTest extends AbstractIntegrationTest {

    @Test
    public void shouldGetAuthorizationJwt() throws Exception {

        final String jsonCredentials = "{"
                    + "\"username\" : \"augusto\","
                    + "\"password\" : \"spring\""
                + "}";

        final MvcResult result =  performRESTLogin(jsonCredentials);
        final MockHttpServletResponse response = result.getResponse();
        final int status = response.getStatus();
        final String jwt = response.getHeader("Authorization");

        assertThat(status, is(200));
        assertThat(jwt, notNullValue());
    }

}

当我使用仅存在于测试数据库中的用户名和密码并获得 403 状态时,我验证了这一点,而使用 MySQL 值获得了 200 状态。 似乎在准备好 HSQLDB 后,.properties 和 .sql 的 main 被读取并覆盖正在使用的数据库的设置。

application.properties:

server.contextPath=/librarymanager
server.port: 8081

spring.datasource.url = jdbc:mysql://localhost:3306/librarymanager
spring.datasource.username = root
spring.datasource.password = root
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = create-drop
spring.jpa.hibernate.naming.strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

datasource.properties:

datasource.class=org.hsqldb.jdbc.JDBCDataSource
datasource.driver=org.hsqldb.jdbc.JDBCDriver
datasource.url=jdbc:hsqldb:mem:librarymanager;sql.syntax_ora=true
datasource.schema=sa
datasource.username=sa
datasource.password=

pom.xml 中的数据库依赖项:

<!-- Banco de dados -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <scope>test</scope>
        </dependency>

执行成功还缺少什么?注释的使用?为开发和测试创建 .properties 与 BDs conf 和一个主 .properties 分开 spring.profiles.active = dev/test 以在配置文件之间切换?我想要一些建议。

谢谢。

github 中的项目 link:https://github.com/augustodossantosti/librarymanager-jwtauth

感谢您的建议。实际上使用框架是最好的选择。