Skip to content

Commit

Permalink
fixes #50
Browse files Browse the repository at this point in the history
  • Loading branch information
rmpestano committed Sep 13, 2016
1 parent 110f1bc commit 03ca742
Show file tree
Hide file tree
Showing 13 changed files with 362 additions and 25 deletions.
49 changes: 48 additions & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ Here are possible values:
|executeScriptsAfter| A list of sql script files to execute after test. Note that commands inside sql file must be separated by `;`.| {}
|===

DBUnit Configuration:: this basically setup the `DBUnit` itself. It can be configured by `@DBUnit` annotation (class or method level) and `dbunit.yml` file present in test resources folder.
DBUnit Configuration:: this basically setup `DBUnit` itself. It can be configured by *@DBUnit* annotation (class or method level) and *dbunit.yml* file present in test resources folder.
+
[source,java]
----
Expand All @@ -310,17 +310,62 @@ leakHunter: false
properties:
batchedStatements: false
qualifiedTableNames: false
caseSensitiveTableNames: false
batchSize: 100
fetchSize: 100
allowEmptyFields: false
escapePattern:
connectionConfig:
driver: ""
url: ""
user: ""
password: ""
----
+
NOTE: `@DBUnit` annotation takes precedence over `dbunit.yml` global configuration which will be used only if the annotation is not present.


TIP: Both configuration mechanisms work for all DBUnit Rules modules.

=== JDBC Connection

As seen in examples above `DBUnit needs a JDBC connection to be instantiated. To avoid creating connection for each test you can define it in *dbunit.yml* for all tests or define in *@DBUnit* on each test.

NOTE: `@DBUnit` annotation takes precedence over dbunit.yml global configuration.

==== Example

[source, java, linenums]
----
@RunWith(JUnit4.class)
@DBUnit(url = "jdbc:hsqldb:mem:test;DB_CLOSE_DELAY=-1", driver = "org.hsqldb.jdbcDriver", user = "sa") <1>
public class ConnectionConfigIt {
@Rule
public DBUnitRule dbUnitRule = DBUnitRule.instance(); <2>
@BeforeClass
public static void initDB(){
//trigger db creation
EntityManagerProvider.instance("rules-it");
}
@Test
@DataSet(value = "datasets/yml/user.yml")
public void shouldSeedFromDeclaredConnection() {
User user = (User) em().createQuery("select u from User u where u.id = 1").getSingleResult();
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(1);
}
}
----
<1> driver class can be ommited in new JDBC drivers since version 4.
<2> Note that the rule instantiation doesn't need a connection anymore.

IMPORTANT: As CDI module depends on a produced entitty manager, connection configuration will be ignored.

=== Rule chaining

DBUnit Rule can be https://github.com/junit-team/junit4/wiki/rules#rulechain[chained with other rules^] so you can define execution order among rules.
Expand Down Expand Up @@ -882,6 +927,8 @@ public class DBUnitJUnit5Test {
----
<1> DBUnit extension will get JDBC connection by reflection so you need to declare a *field* or *method* with `ConnectionHolder` as return type.

TIP: You can configure JDBC connection using @DBUnit annotation or dbunit.yml, https://github.com/rmpestano/dbunit-rules#jdbc-connection[see example here^].

== Leak Hunter

Leak hunter is a component based on https://vladmihalcea.com/2016/07/12/the-best-way-to-detect-database-connection-leaks/[this blog post^] which counts open jdbc connections before and after test execution.
Expand Down
52 changes: 38 additions & 14 deletions core/src/main/java/com/github/dbunit/rules/DBUnitRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.github.dbunit.rules.api.expoter.DataSetExportConfig;
import com.github.dbunit.rules.api.expoter.ExportDataSet;
import com.github.dbunit.rules.api.leak.LeakHunter;
import com.github.dbunit.rules.configuration.ConnectionConfig;
import com.github.dbunit.rules.configuration.DBUnitConfig;
import com.github.dbunit.rules.configuration.DataSetConfig;
import com.github.dbunit.rules.connection.ConnectionHolderImpl;
Expand All @@ -22,7 +23,7 @@
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.DriverManager;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand All @@ -42,6 +43,10 @@ public class DBUnitRule implements TestRule {
private DBUnitRule() {
}

public final static DBUnitRule instance() {
return instance(new ConnectionHolderImpl(null));
}

public final static DBUnitRule instance(Connection connection) {
return instance(new ConnectionHolderImpl(connection));
}
Expand All @@ -68,22 +73,25 @@ public Statement apply(final Statement statement, final Description description)

@Override
public void evaluate() throws Throwable {
DBUnitConfig dbUnitConfig = resolveDBUnitConfig(description);
currentMethod = description.getMethodName();
DataSet dataSet = resolveDataSet(description);
if (dataSet != null) {
final DataSetConfig dataSetConfig = new DataSetConfig().from(dataSet);
final String datasetExecutorId = dataSetConfig.getExecutorId();
DBUnitConfig dbUnitConfig = resolveDBUnitConfig(description);
LeakHunter leakHunter = null;
boolean executorNameIsProvided = datasetExecutorId != null && !"".equals(datasetExecutorId.trim());
if (executorNameIsProvided) {
executor = DataSetExecutorImpl.getExecutorById(datasetExecutorId);
}
try {
if (executor.getConnectionHolder() == null || executor.getConnectionHolder().getConnection() == null) {
createConnection(dbUnitConfig);
}
executor.setDBUnitConfig(dbUnitConfig);
executor.createDataSet(dataSetConfig);
} catch (final Exception e) {
throw new RuntimeException(String.format("Could not create dataset for test '%s'.",description.getMethodName()), e);
throw new RuntimeException(String.format("Could not create dataset for test '%s'.", description.getMethodName()), e);
}
boolean isTransactional = false;
try {
Expand All @@ -100,10 +108,10 @@ public void evaluate() throws Throwable {
statement.evaluate();

int openConnectionsAfter = 0;
if(leakHunterActivated){
if (leakHunterActivated) {
openConnectionsAfter = leakHunter.openConnections();
if(openConnectionsAfter > openConnectionsBefore){
throw new LeakHunterException(currentMethod,openConnectionsAfter - openConnectionsBefore);
if (openConnectionsAfter > openConnectionsBefore) {
throw new LeakHunterException(currentMethod, openConnectionsAfter - openConnectionsBefore);
}
}

Expand All @@ -117,7 +125,7 @@ public void evaluate() throws Throwable {
}
throw e;
} finally {
exportDataSet(executor,description);
exportDataSet(executor, description);
if (dataSetConfig != null && dataSetConfig.getExecuteStatementsAfter() != null && dataSetConfig.getExecuteStatementsAfter().length > 0) {
try {
executor.executeStatements(dataSetConfig.getExecuteStatementsAfter());
Expand All @@ -142,32 +150,48 @@ public void evaluate() throws Throwable {
executor.clearDatabase(dataSetConfig);
}
}
//no dataset provided, only export and evaluate expected dataset
//no dataset provided, only export and evaluate expected dataset
} else {
exportDataSet(executor,description);
exportDataSet(executor, description);
statement.evaluate();
performDataSetComparison(description);
}

}

private void createConnection(DBUnitConfig dbUnitConfig) {
ConnectionConfig connectionConfig = dbUnitConfig.getConnectionConfig();
if ("".equals(connectionConfig.getUrl()) || "".equals(connectionConfig.getUser())) {
throw new RuntimeException(String.format("Could not create JDBC connection for method %s, provide a connection at test level or via configuration, see documentation here: https://github.com/rmpestano/dbunit-rules#jdbc-connection", currentMethod));
}

try {
if (!"".equals(connectionConfig.getDriver())) {
Class.forName(connectionConfig.getDriver());
}
executor.setConnectionHolder(new ConnectionHolderImpl(DriverManager.getConnection(connectionConfig.getUrl(), connectionConfig.getUser(), connectionConfig.getPassword())));
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Could not create JDBC connection for method " + currentMethod, e);
}
}


};
}

private void exportDataSet(DataSetExecutor executor, Description description) {
ExportDataSet exportDataSet = resolveExportDataSet(description);
if(exportDataSet != null){
if (exportDataSet != null) {
DataSetExportConfig exportConfig = DataSetExportConfig.from(exportDataSet);
String outputName = exportConfig.getOutputFileName();
if(outputName == null || "".equals(outputName.trim())){
outputName = description.getMethodName().toLowerCase()+"."+exportConfig.getDataSetFormat().name().toLowerCase();
if (outputName == null || "".equals(outputName.trim())) {
outputName = description.getMethodName().toLowerCase() + "." + exportConfig.getDataSetFormat().name().toLowerCase();
}
exportConfig.outputFileName(outputName);
try {
DataSetExporterImpl.getInstance().export(executor.getDBUnitConnection(),exportConfig);
DataSetExporterImpl.getInstance().export(executor.getDBUnitConnection(), exportConfig);
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.WARNING,"Could not export dataset after method "+description.getMethodName(),e);
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Could not export dataset after method " + description.getMethodName(), e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@
* @return executor id for which the properties will be setup.
*/
String executor() default DataSetExecutorImpl.DEFAULT_EXECUTOR_ID;




/**
*
* @return if true database connection will be reused among tests
*/
boolean cacheConnection() default true;


boolean cacheTableNames() default true;

/**
*
* @return if true will activate connection leak detection.
*/
boolean leakHunter() default false;


Expand All @@ -45,6 +53,16 @@
boolean batchedStatements() default false;

/**
*
* Enable or disable case sensitive table names. If enabled, Dbunit handles all table names in a case sensitive way.
*
* @since 0.15.0
* @return value which configures DatabaseConfig.FEATURE_BATCHED_STATEMENTS
*/
boolean caseSensitiveTableNames() default false;

/**
* Allow to call INSERT/UPDATE with empty strings ('').
* @return value which configures DatabaseConfig.FEATURE_ALLOW_EMPTY_FIELDS. Defaults to false.
*
*/
Expand All @@ -62,8 +80,38 @@
int batchSize() default 100;

/**
* Allows schema, table and column names escaping. The property value is an escape pattern where the ?
* is replaced by the name. For example, the pattern "[?]" is expanded as "[MY_TABLE]" for a table named "MY_TABLE".
* The most common escape pattern is "\"?\"" which surrounds the table name with quotes (for the above example it would result in "\"MY_TABLE\"").
* As a fallback if no questionmark is in the given String and its length is one it is used to surround the table name on the left and right side.
* For example the escape pattern "\"" will have the same effect as the escape pattern "\"?\"".
* @return value which configures DatabaseConfig.PROPERTY_ESCAPE_PATTERN. Defaults to none
*/
String escapePattern() default "";

/**
* @since 0.15.0
* @return driver class name. Used for declarative connections. Don't needed for drivers that implement jdbc 4.
*/
String driver() default "";

/**
*
* @since 0.15.0
* @return Database url. Used for declarative connections.
*/
String url() default "";

/**
* @since 0.15.0
* @return Username owner of database connection. Used for declarative connection.
*/
String user() default "";

/**
* @since 0.15.0
* @return Password of owner of database connection. Used for declarative connection.
*/
String password() default "";

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ public interface ConnectionHolder extends Serializable{

Connection getConnection() throws SQLException;


}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.github.dbunit.rules.api.dataset;

import com.github.dbunit.rules.api.connection.ConnectionHolder;

import com.github.dbunit.rules.configuration.DBUnitConfig;
import com.github.dbunit.rules.configuration.DataSetConfig;
import org.dbunit.DatabaseUnitException;
Expand All @@ -27,6 +26,8 @@ public interface DataSetExecutor{

ConnectionHolder getConnectionHolder();

void setConnectionHolder(ConnectionHolder connectionHolder);

void clearDatabase(DataSetConfig dataset) throws SQLException;

void executeStatements(String[] statements);
Expand All @@ -47,6 +48,7 @@ public interface DataSetExecutor{

DBUnitConfig getDBUnitConfig();


DatabaseConnection getDBUnitConnection();


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.github.dbunit.rules.configuration;

/**
* Created by rafael-pestano on 13/09/2016.
*/
public class ConnectionConfig {

private String driver; //needed by jdbc 3 or below drivers
private String url;
private String user;
private String password;

public String getDriver() {
return driver;
}

public void setDriver(String driver) {
this.driver = driver;
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public String getUser() {
return user;
}

public void setUser(String user) {
this.user = user;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}
}
Loading

0 comments on commit 03ca742

Please sign in to comment.