DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Java
  4. Testing Databases with JUnit and Hibernate Part 3: Cleaning up and Further Ideas

Testing Databases with JUnit and Hibernate Part 3: Cleaning up and Further Ideas

Jens Schauder user avatar by
Jens Schauder
·
Sep. 08, 11 · Interview
Like (0)
Save
Tweet
Share
7.78K Views

Join the DZone community and get the full member experience.

Join For Free

This is the last part in this little series about testing of database code.
In the first part I extracted the session handling for the tests into a JUnit Rule.
In the second part I introduced ObjectMothers for easy creation of instances in the database.
In this part I’ll simplify the implementation of new ObjectMothers by extracting two superclasses and I’ll finish by sketching some further ideas for further development.

Lets have a look at the SuperHeroMother

    public class SuperHeroMother {
     private final Session session;
     
     private String secretIdentity = "Mr. Jones";
     private String name = "Name";
     private String weakness = "None";
     private SuperPower power = null;
     
     public SuperHeroMother(Session s) {
      session = s;
      power = new SuperPowerMother(session).instance();
     }
     
     public SuperHero instance() {
      SuperHero hero = loadInstance();
      if (hero == null) {
       hero = createInstance();
      }
      hero.power = power;
      hero.weakness = weakness;
      hero.secretIdentity = secretIdentity;
      session.save(hero);
      return hero;
     }
     
     private SuperHero loadInstance() {
      return (SuperHero) session.createCriteria(SuperHero.class)
        .add(Restrictions.eq("name", name)).uniqueResult();
     
     }
     
     private SuperHero createInstance() {
      SuperHero hero = new SuperHero();
      hero.name = name;
      return hero;
     }
     
     public SuperHeroMother name(String aName) {
      name = aName;
      return this;
     }
     
     public SuperHeroMother secretIdentity(String aSecretIdentity) {
      secretIdentity = aSecretIdentity;
      return this;
     }
     
     public SuperHeroMother power(SuperPower aPower) {
      power = aPower;
      return this;
     }
     
     public SuperHeroMother weaknes(String aWeakness) {
      weakness = aWeakness;
      return this;
     }
    }

There is still quite some code wich will get repeated over and over again for each ObjectMother. So lets extract some of the commonalities into a superclass ObjectMother:

    /** create instances of type <tt>T</tt> */
    public abstract class ObjectMother<T> {
     private final Session session;
     
     public ObjectMother(Session s) {
      session = s;
     }
     
     /** returns an instance based on the configuration of this object mother */
     public T instance() {
      T t = loadInstance(session);
      if (t == null)
       t = createInstance();
      configureInstance(t);
      session.save(t);
      return t;
     }
     
     /**
      * configure the instance <tt>t</tt> according to the configuration of this
      * ObjectMother
      */
     abstract protected void configureInstance(T t);
     
     /**
      * try to load an instance based on the alternate key. Returns null if no
      * such instance exists
      */
     abstract protected T loadInstance(Session session);
     
     /**
      * create a fresh instance with the alternate key set according to the
      * configuration of this ObjectMother
      */
     abstract protected T createInstance();
    }

ObjectMother uses the Template Method Pattern in order to coordinate the provisioning of intances: try to load a matching instance from the database, if this doesn’t work, create it from scratch, configure all attributes of the instance, store it in the database and return it. The loading, creating and configuring needs to get implemented in subclasses.

For the three entities used in this series we can extract even more code from the various ObjectMothers because loading the instance from the database works always in exactly the same way. But this is only the case because we have an alternate key based on a single attribute. This will be often the case but not always, so I don’t want to tie this into the ObjectMother. Instead I’ll create another super class for this special kind of ObjectMother: SingleAlternateKeyObjectMother

    public abstract class SingleAlternateKeyObjectMother<T, A, S extends SingleAlternateKeyObjectMother<T, A, S>>
      extends ObjectMother<T> {
     
     private final Class<T> objectType;
     private final String alternateKeyName;
     private A alternateKey;
     
     public SingleAlternateKeyObjectMother(Session s, Class<T> theObjectType,
       A defaultAlternateKey, String theAlternateKeyName) {
      super(s);
      objectType = theObjectType;
      alternateKeyName = theAlternateKeyName;
      alternateKey(defaultAlternateKey);
     }
     
     @SuppressWarnings("unchecked")
     @Override
     final protected T loadInstance(Session session) {
      return (T) session.createCriteria(objectType)
        .add(Restrictions.eq(alternateKeyName, getAlternateKey()))
        .uniqueResult();
     
     }
     
     @SuppressWarnings("unchecked")
     public S alternateKey(A theAlternateKey) {
      alternateKey = theAlternateKey;
      return (S) this;
     }
     
     public A getAlternateKey() {
      return alternateKey;
     }
    }

With this a SuperHeroMother contains only a couple of extremly simple methods with hardly any code duplication left:

    public class SuperHeroMother extends
      SingleAlternateKeyObjectMother<SuperHero, String, SuperHeroMother> {
     
     private String secretIdentity = "Mr. Jones";
     private String weakness = "None";
     private SuperPower power;
     
     public SuperHeroMother(Session s) {
      super(s, SuperHero.class, "Name", "name");
      power = new SuperPowerMother(s).instance();
     }
     
     @Override
     protected void configureInstance(SuperHero hero) {
      hero.power = power;
      hero.weakness = weakness;
      hero.secretIdentity = secretIdentity;
     }
     
     @Override
     protected SuperHero createInstance() {
      SuperHero hero = new SuperHero();
      hero.name = getAlternateKey();
      return hero;
     }
     
     public SuperHeroMother secretIdentity(String aSecretIdentity) {
      secretIdentity = aSecretIdentity;
      return this;
     }
     
     public SuperHeroMother power(SuperPower aPower) {
      power = aPower;
      return this;
     }
     
     public SuperHeroMother weaknes(String aWeakness) {
      weakness = aWeakness;
      return this;
     }
    }

Note that the three last methods are only necessary when you want to control these three attributes in your tests. Also these methods actually do suffer from code duplication, but I don’t see a way to abstract over this duplication without using lots of reflection which I don’t consider worthwhile in this case.

With this infrastructure setup there shouldn’t be a reason to leave any SQL statement untested. There are some special cases though.

As mentioned in the first part of this series you should use an in memory database for these tests. It is by orders of magnitudes faster then a normal persistent database. With an in memory database (HSQLDB) we execute about 400 tests in about 5 minutes in a current project. I consider this slow as mud for unit test standards, but compare it to 90 minutes for the same tests against an Oracle database. But sometimes you might have SQL statements that are specific for a database. For example we use analytic functions which are extremely powerfull, but not supported by HSQLDB and as far as I know by no other free in memory database. In order to test these statements and still have a fast running test suite, we further extended our session factory rule. It checks if a the test method or test class is annotated with a special annotation (@OracleTest) and also checks the SqlDialect of the HibernateConfiguration. If the annotation is present, but the SqlDialect is not an Oracle dialect it does not execute the test. Our continuos integration system (Jenkins) has two jobs for the test, one configured with an oracle database and one with a HSQLDB in memory database. The later gives a fast feedback for each check in into version control and the second runs the slow, but more complete tests.

While the stuff presented here is in my opinion extremely helpfull for unit tests of database access there are other things that need testing in the context of databases. You should especially consider tests for your ddl statements and performance tests. For tests of ddl statements I provided ideas in a former article. For performance tests the approach of ObjectMothers is at least in its current form mostly useless, because creating the large amounts of data would be way to slow. So special techniques are needed here. Maybe somebody can provide helpfull links in the comments?

 

From http://blog.schauderhaft.de/2011/03/27/testing-databases-with-junit-and-hibernate-part-3-cleaning-up-and-further-ideas/

Database unit test JUnit Hibernate

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • A Beginner’s Guide To Styling CSS Forms
  • Integrating AWS Secrets Manager With Spring Boot
  • How To Perform Local Website Testing Using Selenium And Java
  • How To Set Up and Run Cypress Test Cases in CI/CD TeamCity

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: