Integration Testing DAO standalone with DBUnit in a Dropwizard environment.

668 views
Skip to first unread message

Dev

unread,
Apr 18, 2015, 12:09:49 AM4/18/15
to dropwiz...@googlegroups.com
I'm trying to write some DBunit test cases for DAO's that will eventually be wired into Resource classes in a Dropwizard application. I'm using H2 as the in memory database for the integration tests.

The roadblock I'm hitting is that the Hibernate Bundle is configured in the application class. This forces me to use the Dropwizard AppRule and start up the whole application in order to get things to configure exactly like they would be in production.

Even If I do that, it's quite difficult to test drive just the DAO class. Especially since the transaction boundary @UnitOfWork is on the Resource class. 

I've read the thread https://groups.google.com/forum/#!topic/dropwizard-user/MAqh9OLJkyc and I'm leaning towards settling for the solution https://gist.github.com/carlo-rtr/4081457 (the first part of it i.e managing the Hibernate Session in the @Before and @After).

This might work but I don't want to be controlling the transaction myself as this is not the production setup. For example, if I forget to put the @UnitOfWork annotation, the integration test might pass but production would still be broken.

Is there a way around this? At a more fundamental level, would you test the transactional boundary using a test for the DAO or some other test maybe further up?


Yun Zhi Lin

unread,
Apr 20, 2015, 6:48:29 AM4/20/15
to dropwiz...@googlegroups.com
Some comments inline:

On Saturday, April 18, 2015 at 2:09:49 PM UTC+10, Dev wrote:
I'm trying to write some DBunit test cases for DAO's that will eventually be wired into Resource classes in a Dropwizard application. I'm using H2 as the in memory database for the integration tests.

The roadblock I'm hitting is that the Hibernate Bundle is configured in the application class. This forces me to use the Dropwizard AppRule and start up the whole application in order to get things to configure exactly like they would be in production.

Even If I do that, it's quite difficult to test drive just the DAO class. Especially since the transaction boundary @UnitOfWork is on the Resource class. 

I've read the thread https://groups.google.com/forum/#!topic/dropwizard-user/MAqh9OLJkyc and I'm leaning towards settling for the solution https://gist.github.com/carlo-rtr/4081457 (the first part of it i.e managing the Hibernate Session in the @Before and @After).

If you are concerned with Unit Test at the DAO layer, then you should stop worrying about the @UnitOfWork which is at the Resource layer. Standalone DAO testing is quite painful at the moment. We ended up hacking a util helper class that reads the DataSourceFactory config, creates a SessionFactory and manage the transaction manually.
 

This might work but I don't want to be controlling the transaction myself as this is not the production setup. For example, if I forget to put the @UnitOfWork annotation, the integration test might pass but production would still be broken.

If you forget to put the @UnitOfWork annotation, your integration test will not pass. Your DAO will through an error like "a Session has not been initialised". Your integration test is as close to production as you can get, except for the H2. So just let your integration test cover your @UnitOfWork and manually manage the Session for your DAO test.

Dev

unread,
Apr 28, 2015, 6:27:48 AM4/28/15
to dropwiz...@googlegroups.com
Yes. Without arguing about the nomenclature, I have classified the DBUnit tests as integration tests (testing integration to a database) and hence my comment about integration tests passing while Production is failing.

I see your point of having a different integration test, a test that test drives from the Resource class (given that I'm using dropwizard) which will cover the test for the existince of the @UnitOfWork annotation.

That said, you're right unit (or integration) testing just the DAO class is quite painful. I've resorted to just setting autocommit = true on the in memory H2 while the tests run.


I now have a different problem. I have a situation where there are threads that run independently and not viz a resource. These threads do some work and update the database and therefore need a transaction!

Dropwizard has a way to do this relatively easily if one uses JDBI. See here. I want to figure out a way to do this using Hibernate. 

Basically, I want the goodness of hibernate with a way to have Dropwizard control the transactions----but---without using a Resource class. Like having a @UnitOfWork on the DAO method instead.

Rupert Smith

unread,
Apr 29, 2015, 5:35:24 AM4/29/15
to dropwiz...@googlegroups.com
On Tuesday, April 28, 2015 at 11:27:48 AM UTC+1, Dev wrote:
Basically, I want the goodness of hibernate with a way to have Dropwizard control the transactions----but---without using a Resource class. Like having a @UnitOfWork on the DAO method instead.

Hi,

This is fairly simple to achieve with a dynamic proxy. Here is the 'invoke' method from one that I wrote. When you wrap this proxy around anything it has the effect of running its methods in a single transaction:

/** {@inheritDoc} */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            sessionFactory.getCurrentSession().getTransaction().begin();

            Object result = method.invoke(obj, args);

            sessionFactory.getCurrentSession().getTransaction().commit();

            return result;
        } catch (InvocationTargetException e) {
            if (e.getCause() instanceof RuntimeException) {
                sessionFactory.getCurrentSession().getTransaction().rollback();
            } else {
                sessionFactory.getCurrentSession().getTransaction().commit();
            }

            throw e.getCause();
        } catch (Throwable e) {
            sessionFactory.getCurrentSession().getTransaction().rollback();

            throw e;
        }
    } 

Variations on this theme are extremely useful for testing.

I also faced the same problem of using the AppRule, but found it did not meet my requirements. I wrote my own test controller, which I pass the MyAppConfiguration object to, and which has methods to create a hibernate session factory, load reference data, setup a bean validator, clear the database and so on, and invoke those at appropriate points in my test.

Rupert

Rupert Smith

unread,
Apr 29, 2015, 5:50:58 AM4/29/15
to dropwiz...@googlegroups.com
In addition to the test controller for my application specifics, I wrote another one for controlling dropwizard. Its a while since I did this, but I think I probably just took the code from the AppRule and adapted it to my needs. I found this better than the AppRule because I can decide when to call it, not just use it as a test rule, and of course, add my own features to it:

public class DropwizardTestController<C extends Configuration> {
    private final Class<? extends Application<C>> applicationClass;
    private final String configPath;
    private C configuration;
    private Application<C> application;
    private Environment environment;
    private Server jettyServer;

    public DropwizardTestController(Class<? extends Application<C>> applicationClass, String configPath) {
        this.applicationClass = applicationClass;
        this.configPath = configPath;

    }

    public void start() {
        startIfRequired();
    }

    public void stop() throws Exception {
        jettyServer.stop();
    }

    public C getConfiguration() {
        return configuration;
    }

    public int getLocalPort() {
        return ((ServerConnector) jettyServer.getConnectors()[0]).getLocalPort();
    }

    public int getAdminPort() {
        return ((ServerConnector) jettyServer.getConnectors()[1]).getLocalPort();
    }

    public <A extends Application<C>> A getApplication() {
        return (A) application;
    }

    public Environment getEnvironment() {
        return environment;
    }

    protected Application<C> newApplication() {
        try {
            return applicationClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void startIfRequired() {
        if (jettyServer != null) {
            return;
        }

        try {
            application = newApplication();

            final Bootstrap<C> bootstrap =
                new Bootstrap<C>(application) {
                    /** {@inheritDoc} */
                    public void run(C configuration, Environment environment) throws Exception {
                        environment.lifecycle().addServerLifecycleListener(new ServerLifecycleListener() {
                                /** {@inheritDoc} */
                                public void serverStarted(Server server) {
                                    jettyServer = server;
                                }
                            });
                        DropwizardTestController.this.configuration = configuration;
                        DropwizardTestController.this.environment = environment;
                        super.run(configuration, environment);
                    }
                };

            application.initialize(bootstrap);

            final ServerCommand<C> command = new ServerCommand<>(application);

            ImmutableMap.Builder<String, Object> file = ImmutableMap.builder();

            if (!Strings.isNullOrEmpty(configPath)) {
                file.put("file", configPath);
            }

            final Namespace namespace = new Namespace(file.build());

            command.run(bootstrap, namespace);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

Reply all
Reply to author
Forward
0 new messages