Ich entwickelte ein System, dessen Infrastruktur-Layer wurde auf Spring-Boot (Security, Data JPA, MVC ...) entwickelt. Zur Laufzeit verbindet sich das System mit MySQL, dessen Einstellungen sich in src/main/resources/application.properties
zusammen mit .sql befinden, das einige Benutzereinfügungen und Rollen für die Authentifizierung enthält.
Für die Integrationstests entschied ich mich, HSQLDB zu verwenden, um die Daten zu isolieren und "sichere" Tests durchzuführen. Dazu habe ich die Klasse AbstractIntegrationTest
erstellt, die Methoden zum Erstellen und Löschen von Tabellen und Methoden zum Testen vom Controller enthält. Alle Testklassen erweitern es: (I versteckte Methoden haben, die beinhalten die Datenbank nicht)In-Memory-Datenbankkonfiguration (HSQLDB) für Integrationstests in Spring-Boot-App
@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";
}
}
Die LibraryManagerContextConfiguration
und WebSecurityConfig
Klassen die Domäne und Infrastruktur Bohnen Erklärungen enthalten, so dass sie die Feder Kontext bilden. Diese Klasse befindet sich in src/test/java
und die datasource.properties
Datei zusammen mit dem Test. Die Testklassen laufen einwandfrei, die Testskripte laufen, die Tabellen werden erstellt, aber wenn ein Repository während des Tests nach Daten sucht, durchsucht es MySQL anstelle von HSQLDB. Hier ist eine Testklasse:
@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());
}
}
ich dies überprüft, wenn ich verwendet Benutzernamen und ein Passwort, das nur auf der Testdatenbank bestanden und bekam 403
Status während bei MySQL Werte 200
Status bekam. Es scheint, dass nach der Vorbereitung von HSQLDB die .properties und .sql von main gelesen werden und die Einstellungen der verwendeten Datenbank überschreiben.
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=
DB Abhängigkeiten in 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>
Was fehlt für die Ausführung, um erfolgreich zu sein? Die Verwendung einer Anmerkung? Erstellen Sie .properties für Dev und Tests getrennt mit dem BDs conf und einer Hauptdatei .properties mit spring.profiles.active = dev/test
, um zwischen Profilen zu wechseln? Ich hätte gerne Vorschläge.
Danke.
Projekt Link in Github: https://github.com/augustodossantosti/librarymanager-jwtauth
Ich denke, es ist besser, verschiedene Eigenschaften für verschiedene Umgebungen zu verwenden, um diese Verwirrung zu vermeiden. application-test.properties, application-prod.properties – CrazyMac
Sie machen es viel zu komplex ... Stellen Sie nur eine separate Eigenschaftendatei zum Testen bereit, die die Informationen für hsqldb enthält, laden Sie diese, um die anderen Eigenschaften zu überschreiben ... Arbeiten Sie mit der Rahmen nicht dagegen. Außerdem sollten Sie die Datenbank nicht für jeden Test neu erstellen, sondern Ihre Tests mit "@Transactional" durchführen. Nach Abschluss des Tests wird alles zurückgesetzt. Spart Ihnen viel Zeit. –
Danke für die Vorschläge. Die Arbeit mit dem Framework ist die beste Option. –