NoSuchTableException using dbunit to test dao class - java

I'm using dbunit with an h2 in-memory database to test the methods in a DAO class that I wrote. All tests in the suite used to pass successfully, but then I reorganized the directory structure of the project and now I'm getting a NoSuchTableException when I run the test suite. I feel that it's got to be some kind of build path error, but I've been banging my head over it for two days now and can't fix it.
Here's an excerpt from the test class containing some of the set-up methods:
#RunWith(PowerMockRunner.class)
#PrepareForTest(DAOUtilities.class)
public class FeedingScheduleDaoImplDBUnitTest extends DataSourceBasedDBTestCase {
private Connection connection;
private FeedingScheduleDaoImpl fsdi = new FeedingScheduleDaoImpl();
#Override
protected DataSource getDataSource() {
JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setUrl("jdbc:h2:mem:default;DB_CLOSE_DELAY=-1;init=runscript from 'classpath:schema.sql'");
dataSource.setUser("sa");
dataSource.setPassword("sa");
return dataSource;
}
#Override
protected IDataSet getDataSet() throws Exception {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("data.xml");
return new FlatXmlDataSetBuilder().build(inputStream);
}
#Override
protected DatabaseOperation getSetUpOperation() {
return DatabaseOperation.REFRESH;
}
#Override
protected DatabaseOperation getTearDownOperation() {
return DatabaseOperation.DELETE_ALL;
}
#Before
public void setUp() throws Exception {
super.setUp();
connection = getConnection().getConnection();
}
#After
public void tearDown() throws Exception {
super.tearDown();
}
#Test
public void givenDataSetEmptySchema_whenDataSetCreated_thenTablesAreEqual() throws Exception {
IDataSet expectedDataSet = getDataSet();
ITable expectedTable = expectedDataSet.getTable("animals");
IDataSet databaseDataSet = getConnection().createDataSet();
ITable actualTable = databaseDataSet.getTable("animals");
Assertion.assertEquals(expectedTable, actualTable);
}
}
It's the return new FlatXmlDataSetBuilder().build(inputStream); that is actually generating the exception, but I suspect that's because the schema isn't being properly loaded in getDataSource() method.
Below is my new directory structure after the reorganization. The sql schema file and the xml data files needed for the tests are all in the src/test/resources directory.
eZoo directory structure
And here's the build section of my pom.xml. I haven't specified any source or resource directories for the build, and so if I'm understanding things correctly, that should land all of the resource files in the default target/test-classes directory. I've checked and they are in fact there.
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
</plugin>
</plugins>
</build>
Any tips or help would be much appreciated. I'm still learning how to use Maven, and I did this reorganization in an effort to better understand the standard Maven project structure, so I'm hoping to both fix what I broke and learn how to use Maven better.

After tinkering, I found a solution to my problem. I removed the following line from my xml files:
<!DOCTYPE xml>
Here's a link to another question and answer where I found the solution: https://stackoverflow.com/a/5915063.

Related

Start MockServer during spring-boot:run

We have some APIs we use in our application that are not accessible from local developer machines due to firewalls.
I want to use mockServer to mock some of these API so we can develop locally.
When running tests mockServer can be started and stopped using the maven build phases process-test-classes and verify respectively.
How can I get it to run when I start the application with mvn spring-boot:run ?
It's possible to override beans on springboot.
So you can use your beans and switch for mock values as you need
The example bellow is overriding services and using mock as you prefer but you can use interfaces as well.
Creating a service
#Service
public class ServiceReal {
#Autowired(required = false) // must be required=false. May be disabled by using mock configuration
private JdbcTemplate jdbcTemplate;
public String getInfo() {
return jdbcTemplate...// get a real value from database
}
}
Creating a mock service
#Service
#Primary
#Profile("mocklocal")
public class ServiceMock extend ServiceReal {
#Override
public String getInfo() {
return "Mocked value"
}
}
Config beans to choose one of them on properties later
#Profile("mocklocal")
#PropertySource("classpath:application-mocklocal.properties")
#Configuration
public class ConfigMock {
private static final String PROP_VALUE_TRUE = "true";
private static final boolean PROP_FALSE_DEFAULT_MISSING = false;
private static final String PROP_SERVICE_REAL = "mocklocal.service.real";
private static final String PROP_SERVICE2_REAL = "mocklocal.service2.real";
#Bean
#ConditionalOnProperty( value = PROP_SERVICE_REAL, havingValue = PROP_VALUE_TRUE, matchIfMissing = PROP_FALSE_DEFAULT_MISSING)
public ServiceReal serviceReal(){
return new ServiceMock();
}
#Bean
#ConditionalOnProperty( value = PROP_SERVICE2_REAL, havingValue = PROP_VALUE_TRUE, matchIfMissing = PROP_FALSE_DEFAULT_MISSING)
public Service2Real service2Real(){
return new Service2Mock();
}
}
Config your application-mocklocal.properties to use mock
# using ConfigMock
spring.profiles.active=mocklocal
# settig spring to override service and use mock
spring.main.allow-bean-definition-overriding=true
# disable some configuration not required in mocks. you can adjust for amqp, database or other configuration
spring.autoconfigure.exclude[0]=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
spring.autoconfigure.exclude[1]=org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
spring.autoconfigure.exclude[2]=org.springframework.boot.autoconfigure.orm.jpa.DataSourceTransactionManagerAutoConfiguration
# enable your service to use mocks not real services
mocklocal.service.real=true
mocklocal.service2.real=true
so if you start your app using --spring.profiles.active=mocklocal you will got mock values
And you can use on tests as well
#ExtendWith(SpringExtension.class)
#AutoConfigureMockMvc
#SpringBootTest
#TestPropertySource(locations = "classpath:application-mocklocal.properties")
public class RunIntegrationTests {
#Autowired
private MockMvc mockMvc;
#Test
public void run() throws Exception{
...
}
}
When running tests mockServer can be started and stopped using the maven build phases process-test-classes and verify respectively.
So there must be some (pom) configuration like:
<plugin>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-maven-plugin</artifactId>
<version>3.10.8</version>
<configuration>
<serverPort>1080</serverPort>
<proxyPort>1090</proxyPort>
<logLevel>DEBUG</logLevel>
<initializationClass>org.mockserver.maven.ExampleInitializationClass</initializationClass>
</configuration>
<executions>
<execution>
<id>process-test-classes</id>
<phase>process-test-classes</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>verify</id>
<phase>verify</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
This would start a mock server at process-test-classes (so before test phase) and stop it at validate (so after (post-)integration-test phase).
(link1, link2)
How can I get it to run when I start the application with mvn spring-boot:run ?
To run it with mvn spring-boot:run:
Just run mvn mockserver:start spring-boot:run! (pack it into a script/IDE launch..) (recommended)
Implement custom plugin, which commbines spring-boot-maven and mockserver-maven-plugin... (and then run mvn com.example:custom-plugin:run)
.
I had created a MockServer for my team once, for quite a similar purpose here (fortunately a short demo is also available). You can set up this server independently (say on a localhost) and add the request (url and payloads) with the corresponding response json you want to this server.
The one time change you need to do inside your project will be to route all your API request to this Mockserver during development/testing, which can be done by changing the base url of all the APIs you will be using and setting up the mockserver with appropriate json request and response. It can be done as simple as this:
public class BaseUrlLoader {
public static String NEWSRIVER_BASE_URL;
public static String FACEBOOK_BASE_URL;
public static String TWITTER_BASE_URL;
private static final String MOCKSERVER_BASE_URL = "mocksrvr.herokuapp.com/TEAM-SECRET-KEY";
public static void load(){
Properties properties= new Properties();
String activeProfile;
try{
properties.load(ClassLoader.getSystemResourceAsStream("application.properties"));
} catch (IOException e) {
System.out.println("Not able to load the application.properties file");
return;
}
activeProfile = properties.getProperty("spring.profiles.active");
System.out.println("Using "+activeProfile);
if(activeProfile.equals("Test")){
NEWSRIVER_BASE_URL=MOCKSERVER_BASE_URL;
FACEBOOK_BASE_URL= MOCKSERVER_BASE_URL;
TWITTER_BASE_URL= MOCKSERVER_BASE_URL;
}else{
NEWSRIVER_BASE_URL="api.newsriver.io";
FACEBOOK_BASE_URL="api.facebook.com";
TWITTER_BASE_URL="api.twitter.com";
}
System.out.println(NEWSRIVER_BASE_URL);
}
}
// Example- Use APIs as
public class NewsFetch {
...
public NewsFetch(){ BaseUrlLoader.load(); }
private URI buildURL(APIQuery apiQuery) throws URISyntaxException {
String mainURL = BaseUrlLoader.NEWSRIVER_BASE_URL+"v2/search";
URIBuilder url = new URIBuilder(mainURL);
url.addParameter("query", apiQuery.getLuceneQuery());
url.addParameter("soryBy", apiQuery.getSortBy());
url.addParameter("sortOrder", apiQuery.getSortOrder());
url.addParameter("limit", apiQuery.getLimit());
return url.build();
}
public HttpResponse <String> fetch(APIQuery apiQuery) throws URISyntaxException, IOException, InterruptedException {
URI uri = buildURL(apiQuery);
HttpRequest request = HttpRequest.newBuilder()
.GET()
.header("Authorization", KEY)
.uri(uri)
.build();
...
}
}
// and add the request like http://mocksrvr.herokuapp.com/TEAM-SECRET-KEY/v2/search/... to the Mockserver with the response you want.
The baseurl will change according to the current active profile. This mockserver is simple and can even be integrated with the Slackbot. See more in the readme file. There can be many bugs in the project and contributions will be appreciated.

How to dynamically load Spring controllers

I have a Spring Boot application that hosts a REST API.
Depending on which files get deployed, I want to be able to have it load additional controllers from what is essentially a "plugin" JAR file.
For example, I'd love to be able to do something like this:
java -jar myapp.jar -Dplugins.directory=/opt/myapp/plugins
Is this possible?
Note: these would not be loaded on the fly; once deployed, the set of plugins will remain fixed. I want one application jar that remains the same in every deployment, and the behavior of the application will be determined by the plugins that are deployed alongside it.
it may not 100% Satisfy your demand.
I have two suggestion.
the easy one.
java -jar stackoverflow-1.0-SNAPSHOT.jar --spring.profiles.active=prod
and put different value "#Profile" on your controller.
#RestController
#Profile("prod")
public class URLOneController {
#PostMapping(value = "/url", consumes="application/json", produces="application/json")
public ResponseEntity<HttpStatus> insertClaim(#RequestBody String messageBody) {
return new ResponseEntity<>(HttpStatus.OK);
}
}
second suggestion ,dynamic load beanDefiniton.
#Configuration
#ConditionalOnProperty(name="external.controller.enable",havingValue = "true")
public class ExternalClassDefinitionProcessor implements
BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Class<?> aClass = null;
try {
aClass = contextClassLoader.loadClass("com.jin.learn.demo.UrlOneController");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(aClass);
beanDefinitionBuilder.addPropertyReference("personDao", "personDao");
BeanDefinition personManagerBeanDefinition = beanDefinitionBuilder
.getRawBeanDefinition();
registry.registerBeanDefinition("UrlOneController",
personManagerBeanDefinition);
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory
beanFactory) throws BeansException {
}
}
package your controller into normal jar(not use spring-boot-maven-plugin )
run your app like this command line
java -Dloader.path="lib/,config/,/home/jin/Desktop/abc/target/abc-1.0-SNAPSHOT.jar" -jar stackoverflow-1.0-SNAPSHOT.jar --external.controller.enable=true
the extra contorller in abc-1.0-SNAPSHOT.jar and your main app is stackoverflow-1.0-SNAPSHOT.jar
tips:
stackoverflow-1.0-SNAPSHOT.jar should package zip format .
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>ZIP</layout>
</configuration>
</plugin>

Serving static files with camel routes

I am trying to serve a static file in camel routes.
The routes in my main class contains this piece of code:
public final void configure() throws Exception {
// declaring camel routes
// match on uri prefix must be true when parameters are passed as part of the uri
// for example, "http://localhost/hello/rick"
// http.port is in local.properties file user-api
from("jetty:http://0.0.0.0:{{http.port}}/user/dist/?matchOnUriPrefix=true")
.process( new StaticProcessor( "help", "index.html", "dist"))
.routeId( "static");
from("jetty:http://0.0.0.0:{{http.port}}/user?matchOnUriPrefix=true")
.to("cxfbean:userService");
}
This works good. When I hit the url: http://xxxx:8086/user/dist/index.html, my index page is rendered and the url shows to behttp://xxxx:8086/user/dist/ in url bar.
But when I reload the page (press F5), the url becomes: http://xxxx:8086/user/dist// and I get error like:
This page should have been replaced by Swagger. Do you have the
following in your application's pom.xml as the only reference to the
swagger-maven-plugin?
<build>
<plugins>
<plugin>
<groupId>com.github.kongchen</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<executions>
<execution>
<id>swagger</id>
<phase>compile</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
I have this dependency in my effective POM. So what am I missing?
I wish to achieve that any url with http://clv035sl-8947d6:8888/user/dist should route the call to index.html. Why I need to explictly write index.html at end of the url?
Any help/ suggestion will be appreciated.
I made a simple JUnit Test case to test your scenario based on this blog post.
Where are the implementation of the StaticProcessor class? I've implemented something for this scenario that is quite similar (IMHO):
public void configure() throws Exception {
from("jetty:http://0.0.0.0:8080/user/dist?matchOnUriPrefix=true").process(new Processor() {
public void process(Exchange exchange) throws Exception {
Message in = exchange.getIn();
String relativepath = in.getHeader(Exchange.HTTP_PATH, String.class);
String requestPath = in.getHeader("CamelServletContextPath", String.class); //CamelServletContextPath
if (relativepath.isEmpty() || relativepath.equals("/")) {
relativepath = "index.html";
}
final String formattedPath = String.format("%s/%s", requestPath, relativepath);
InputStream pathStream = this.getClass().getResourceAsStream(formattedPath);
Path path = FileSystems.getDefault().getPath(this.getClass().getResource(formattedPath).getPath());
Message out = exchange.getOut();
try {
out.setBody(IOUtils.toByteArray(pathStream));
out.setHeader(Exchange.CONTENT_TYPE, Files.probeContentType(path));
} catch (IOException e) {
out.setBody(relativepath + " not found.");
out.setHeader(Exchange.HTTP_RESPONSE_CODE, "404");
}
}
}).routeId("static");
}
It takes from the classpath the resources that needed to be exposed and set the out message to the response. Please, take a look at the entire test case.
I've tested the following URLs:
http://localhost:8080/user/dist/
http://localhost:8080/user/dist
http://localhost:8080/user/dist/index.html
Please note that I added the swagger plugin dependency just like you did.
Let me know if that helps or point where the StaticProcessor implementation is that I may test with it and edit my answer.
Cheers

Where to generate ressources into during Annotation processing in a maven workflow?

I have a maven project with several modules, i.e.
<module>backend</module> <!-- provides annotations -->
<module>annotationProcessor</module> <!-- processes ann., generates files -->
<module>mainprog</module> <!-- uses annotations/files -->
backend provides an annotation class MyAnnotation for annotating classes.
mainprog contains Mainprog.java which defines a class with a #MyAnnotation annotation. At runtime this class tries to load a file via getResourceAsStream("Mainprog.properties") (which does not exist yet).
The annotationProcessor has a class MyAnnotationProcessor which maven executes and finds my annotations.
The processor should create the file Mainprog.properties from information gathered by the annotation processor.
I can not manage to put the properties file in a place where it is found when executing/testing Mainprog.
Where should I generate the file to into, being in a maven workflow?
How do I tell maven this file is used in tests or at runtime? Eventually
is has to be packaged in the jar.
Mainprog
package demo;
#MyAnnotation
public class Mainprog {
}
Use the properties file
Currently I do it in the testing class, but later this will be in the class itself.
package demo;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.junit.Test;
public class MainprogTest {
Class testclass = Mainprog.class;
#Test
public void testPropertiesFile() throws IOException {
String fn = testclass.getCanonicalName().replace('.', '/') + ".properties";
System.err.println("loading: '"+fn+"'");
InputStream in = getClass().getResourceAsStream(fn);
Properties prop = new Properties();
prop.load(in);
in.close();
}
}
This currently runs as such:
loading: 'demo/Mainprog.properties'
Tests in error:
testPropertiesFile(demo.MainprogTest)
with a NullPointerException, because the stream returns null, i.e. does not exist.
Despite the file is there (but is it in the right place?):
towi#havaloc:~/git/project/mainprog$ find . -name Mainprog.properties
./src/java/demo/Mainprog.properties
./target/classes/demo/Mainprog.properties
Processor
package demo;
import com.github.javaparser.*;
import com.github.javaparser.ast.*;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
#SupportedAnnotationTypes({"demo.MyAnnotation"})
public class MyAnnotationProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
for (TypeElement te : elements) {
for (Element e : env.getElementsAnnotatedWith(te))
{
processAnnotation(e);
}
}
return true;
}
private void processAnnotation(Element elem) {
final TypeElement classElem = (TypeElement) elem;
...
final String prefix = System.getProperty("user.dir").endsWith("/"+"mainprog") ? "." : "mainprog";
final String className = classElem.getQualifiedName().toString();
String fileName = prefix + "/src/java/" + className.replace('.', '/') + ".java";
FileInputStream in = new FileInputStream(fileName);
final CompilationUnit cu = JavaParser.parse(in);
final CallGraph graph = ...
generateInfoProperties(classElem, fileName, graph);
}
private void generateInfoProperties(TypeElement classElem, String inFilename, CallGraph graph) throws IOException {
final File outFile = new File(inFilename
.replace("/src/java/", "/src/java/") // <<< WHERE TO ???
.replace(".java", ".properties"));
outFile.getParentFile().mkdirs();
try (PrintWriter writer = new PrintWriter(outFile, "UTF-8")) {
final Properties ps = new Properties();
graph.storeAsProperties(ps);
ps.store(writer, inFilename);
writer.close();
}
}
}
As you can see, there is a lot of guesswork and "heuristics" going on when handling directory names. All that System.getProperty("user.dir") and replace("/src/java/", "/src/java/") is probably wrong, but what is better?
maven
In Maven I have 4 poms, of course
pom.xml
backend/pom.xml
annotationProcessor/pom.xml
mainprog/pom.xml
Only one of seems to me contains anything of note, i.e., the execution of the annotation processor in mainprog/pom.xml:
<project>
....
<dependencies>
<dependency>
<groupId>project</groupId>
<artifactId>backend</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>project</groupId>
<artifactId>annotationProcessor</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>mainprog</finalName>
<sourceDirectory>src/java</sourceDirectory>
<resources>
<resource>
<directory>${basedir}/src/conf</directory>
<targetPath>META-INF</targetPath>
</resource>
<resource>
<directory>${basedir}/web</directory>
</resource>
<resource>
<directory>${basedir}/src/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
<include>**/*.wsdl</include>
<include>**/*.xsd</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessors>
<annotationProcessor>demo.MyAnnotationProcessor
</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
...
</plugins>
</build>
</project>
I thought by generating the file into /src/java/ and then having <resource><directory>${basedir}/src/java and <include>**/*.properties is enough, but it does not seem so. Why is that?
Use the provided Filer, which can be obtained using processingEnv.getFiler(). If you create a source file using it, the compiler will compile it on the next round and you won't need to worry about configuring Maven to compile generated source files.

Run migrations programmatically in Dropwizard

I have dropwizard-application (0.7.0) for which I want to run integration tests.
I've set up an integration test using DropwizardAppRule, like this:
#ClassRule
public static final DropwizardAppRule<MyAppConfiguration> RULE =
new DropwizardAppRule<MyAppConfiguration>(
MyApplication.class, Resources.getResource("testconfiguration.yml").getPath());
When I try to run the below tests using it, it doesn't work because I haven't run my migrations. What is the best way to run the migrations?
Test:
#Test
public void fooTest() {
Client client = new Client();
String root = String.format("http://localhost:%d/", RULE.getLocalPort());
URI uri = UriBuilder.fromUri(root).path("/users").build();
client.resource(uri).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).post(User.class, new LoginUserDTO("email#email.com", "password"));
}
Configuration:
public class MyAppConfiguration extends Configuration {
#Valid
#NotNull
private DataSourceFactory database = new DataSourceFactory();
#JsonProperty("database")
public DataSourceFactory getDataSourceFactory() {
return database;
}
#JsonProperty("database")
public void setDataSourceFactory(DataSourceFactory dataSourceFactory) {
this.database = dataSourceFactory;
}
}
Thanks to Kimble and andersem for putting me on the right track. Here's what I came up with in my #BeforeClass method:
// Create the test database with the LiquiBase migrations.
#BeforeClass
public static void up() throws Exception
{
ManagedDataSource ds = RULE.getConfiguration().getMainDataSource().build(
RULE.getEnvironment().metrics(), "migrations");
try (Connection connection = ds.getConnection())
{
Liquibase migrator = new Liquibase("migrations.xml", new ClassLoaderResourceAccessor(), new JdbcConnection(connection));
migrator.update("");
}
}
I ran into some concurrency issues when trying to do the database migration as part of the test case and ended up baking it into the application itself (protected by a configuration option).
private void migrate(MyAppConfiguration configuration, Environment environment) {
if (configuration.isMigrateSchemaOnStartup()) {
log.info("Running schema migration");
ManagedDataSource dataSource = createMigrationDataSource(configuration, environment);
try (Connection connection = dataSource.getConnection()) {
JdbcConnection conn = new JdbcConnection(connection);
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(conn);
Liquibase liquibase = new Liquibase("migrations.xml", new ClassLoaderResourceAccessor(), database);
liquibase.update("");
log.info("Migration completed!");
}
catch (Exception ex) {
throw new IllegalStateException("Unable to migrate database", ex);
}
finally {
try {
dataSource.stop();
}
catch (Exception ex) {
log.error("Unable to stop data source used to execute schema migration", ex);
}
}
}
else {
log.info("Skipping schema migration");
}
}
private ManagedDataSource createMigrationDataSource(MyAppConfiguration configuration, Environment environment) {
DataSourceFactory dataSourceFactory = configuration.getDataSourceFactory();
try {
return dataSourceFactory.build(environment.metrics(), "migration-ds");
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException("Unable to initialize data source for schema migration", ex);
}
}
Another approach that doesn't rely on importing Liquibase's classes directly is to run the db migrate command in the same way that you might from the command line, using the RULE:
#Before
public void migrateDatabase() throws Exception {
RULE.getApplication().run("db", "migrate", ResourceHelpers.resourceFilePath("testconfiguration.yml"));
}
This approach also works for any other commands from any other bundles that you might want to run before starting the tests.
A small winkle: Doing this with any commands that extend Dropwizards ConfiguredCommand (which all of the dropwizard-migrations do) will unnecessarily disable logback when the command finishes.
To restore it, you can call:
RULE.getConfiguration().getLoggingFactory().configure(RULE.getEnvironment().metrics(),
RULE.getApplication().getName());
I did it this way using Liquibase's API:
private void migrate(){
DataSourceFactory dataSourceFactory = RULE.getConfiguration().dataSourceFactory;
Properties info = new Properties();
info.setProperty("user", dataSourceFactory.getUser());
info.setProperty("password", dataSourceFactory.getPassword());
org.h2.jdbc.JdbcConnection h2Conn = new org.h2.jdbc.JdbcConnection(dataSourceFactory.getUrl(), info);
JdbcConnection conn = new JdbcConnection(h2Conn);
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(conn);
Liquibase liquibase = new Liquibase("migrations.xml", new ClassLoaderResourceAccessor(), database);
String ctx = null;
liquibase.update(ctx);
}
And then I put this in a beforeclass:
#BeforeClass
public void setup(){
migrate();
}
It's probably not the ultimate solution, and it depends a lot on the database you're using, but it works.
What I do to achieve the same goal is to run the migration from within maven.
Add this to the section in the sction of your pom.xml:
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.0.5</version>
<executions>
<execution>
<phase>process-test-resources</phase>
<configuration>
<changeLogFile>PATH TO YOUR MIGRATIONS FILE</changeLogFile>
<driver>org.h2.Driver</driver>
<url>JDBC URL LIKE IN YOUR APP.YML</url>
<username>USERNAME</username>
<password>PASSWORD</password>
<dropFirst>false</dropFirst>
<promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
<logging>info</logging>
</configuration>
<goals>
<goal>dropAll</goal>
<goal>update</goal>
</goals>
</execution>
</executions>
</plugin>
This will work with maven from command line. With this setting, maven will use liquibase dropAll to drop all database objects, and then run a migration, so with every test you have a clean new database.
When using that, I ran intoissues with eclipse, it complained about the lifecycle mapping not working upon the execution tag of the plugin. In this case, you need to add the following to the build section as well, so eclipse can properly map the life cycles:
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<versionRange>[1.0,)</versionRange>
<goals>
<goal>dropAll</goal>
<goal>update</goal>
</goals>
</pluginExecutionFilter>
<action>
<execute />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>

Categories

Resources