2017-04-11 3 views
1

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

+1

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

+0

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. –

+0

Danke für die Vorschläge. Die Arbeit mit dem Framework ist die beste Option. –

Antwort

0

Danke für die Vorschläge. Die Arbeit mit dem Framework ist die beste Option.