RSS

Category Archives: FEST-Assert

Chasing Heisenbugs from an AKKA actor integration test with awaitility

Abstract : Ever had an impression you’re changing what you’re observing by simply observing it? If you so, you may have hit a Heisenbug ([1], [2]). Well, OK, I agree that a more precise definition of “observing” is needed and that we can differentiate between active and passive observing. The thing is one can hardly be a fully passive observer, especially when it comes to testing multi-threaded programs. Assuming you’re really working on the program and not simply looking a video tutorial in which case you will be a fully passive observer (and I would be wondering how the heck you’ve tumbled on my article). In short, when working on (including testing) a program we may introduce Heisenbugs through levels of indirection which are usually hard to spot. In C/C++ such a level of indirection represents uninitialized auto variables which can change every time you run you program. In Java a source of many indirection levels is the platform independence provided by the JVM. In JavaScript a source of Heisenbugs can be processing uncontrolled (browser dependent) events such as the scroll event.

Goal : Chase and fix a Heisenbug within an AKKA actor integration test using Awaitility

Acknowledgement: My gratitude goes to the open source community

An AKKA actor based program : I’ve already given an example of an AKKA actor base program here and you can find the source code here. So to save you and me some time, I’m going to re-use it.

Naïve test case : First we are going to build a test case which is prone to Heisenbugs and then we are going to fix it. I’m going to use JUnit, Spring Test Framework, Mockito, FEST-Assert, and FEST-Reflect to build up the following integration test case (access article’s full source code):

import akka.actor.ActorRef;
import org.honeysoft.akka.Bootstrap;
import org.honeysoft.akka.service.IBusinessService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.fest.reflect.core.Reflection.field;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

@ContextConfiguration(classes = {Bootstrap.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class BusinessActorTest {

    @Autowired
    @Qualifier(Bootstrap.BUSINESS_ACTOR)
    private ActorRef businessActorRef;

    @Autowired
    private IBusinessService businessService;

    @Mock
    private Logger mockBusinessServiceLogger;

    @Before
    public void beforeEach() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void shouldBeValidWhenNoOneIsNull() {
        //GIVEN
        field("logger").ofType(Logger.class).in(businessService).postDecorateWith(mockBusinessServiceLogger);

        //WHEN
        String testString = "test-string";
        businessActorRef.tell(testString);

        //THEN
        verify(mockBusinessServiceLogger, times(1)).info(anyString(), eq(testString));
    }
}

The thing is that this test case is prone to Heisenbugs. More precisely, the verification (at line 50) may pass some times and may fail others. Depending on the speed of execution and thread priority it can happen that the verification comes before the businessService receives the testString for processing. Luckily we have Awaitility at our disposal so the fix is straightforward:

import akka.actor.ActorRef;
import com.jayway.awaitility.Awaitility;
import com.jayway.awaitility.Duration;
import org.fest.assertions.Assertions;
import org.honeysoft.akka.Bootstrap;
import org.honeysoft.akka.service.IBusinessService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static org.fest.reflect.core.Reflection.field;

@ContextConfiguration(classes = {Bootstrap.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class BusinessActorTest {

    @Autowired
    @Qualifier(Bootstrap.BUSINESS_ACTOR)
    private ActorRef businessActorRef;

    @Autowired
    private IBusinessService businessService;

    @Before
    public void beforeEach() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void shouldBeValidWhenNoOneIsNull() throws Exception {
        //GIVEN
        final ConcurrentMap<String, Object> threadSafeMap = new ConcurrentHashMap<String, Object>(1);
        field("logger").ofType(Logger.class).in(businessService).postDecorateWith(new TestLogger(threadSafeMap));

        //WHEN
        String testString = "test-string";
        businessActorRef.tell(testString);

        //THEN
        Awaitility.waitAtMost(Duration.FIVE_SECONDS).until(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                return !threadSafeMap.isEmpty();
            }
        });

        Assertions.assertThat(threadSafeMap).hasSize(1);
        Assertions.assertThat(threadSafeMap.values().iterator().next()).isEqualTo(testString);
    }

    private static final class TestLogger implements Logger {

        private final ConcurrentMap<String, Object> map;

        private TestLogger(ConcurrentMap<String, Object> map) {
            this.map = map;
        }

        @Override
        public void info(String format, Object arg) {
            map.put(format, arg);
        }

        //Other overridden methods go here
    }
}

So what happened is that instead of mocking our logger target we’ve created a thread-safe TestLogger to help us. Then we’ve added an awaitility block to either wait for at most 5 secs or until our piggy-bag map is not empty. Well, that was all. Not too painful right?

 

Tags: , , , , , , , , , , , , ,

Sunday’s recipe

Today’s software recipe includes the following main ingredients (as Maven artifacts) Guice, Hibernate, JPA, HSQLDB, FEST-Assert, Apache Commons Lang, JUnit which we are going to put and mix in Eclipse 3.7.2 pot. Our goal would be a simple one: persist something in HSQLDB using JPA and Hibernate as underlying implementation; take advantage of Guice for Context and Dependency Injection (CDI) and test what we’ve done with JUnit, FEST-Assert and HSQLDB Manager. For a start here is the pom.xml relevant portion which you may want to include:

<dependencies>
 <dependency>
  <groupId>com.google.inject</groupId>
  <artifactId>guice</artifactId>
  <version>3.0</version>
 </dependency>
 <dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-entitymanager</artifactId>
  <version>4.1.1.Final</version>
 </dependency>
 <dependency>
  <groupId>org.hibernate.javax.persistence</groupId>
  <artifactId>hibernate-jpa-2.0-api</artifactId>
  <version>1.0.1.Final</version>
 </dependency>
 <dependency>
  <groupId>org.hsqldb</groupId>
  <artifactId>hsqldb</artifactId>
  <version>2.2.8</version>
 </dependency>
 <dependency>
  <groupId>commons-lang</groupId>
  <artifactId>commons-lang</artifactId>
  <version>2.6</version>
 </dependency>
 <dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.10</version>
  <scope>test</scope>
 </dependency>
 <dependency>
  <groupId>org.easytesting</groupId>
  <artifactId>fest-assert</artifactId>
  <version>1.4</version>
  <scope>test</scope>
 </dependency>
</dependencies>

Since we need something to persist in our database let’s create a very simple Project entity having only an identifier and a name fields. Here is how it looks like:

package org.honeysoft.hsqldbtest.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import org.apache.commons.lang.builder.EqualsBuilder;

@Entity(name = "project")
public class Project {

    @Id
    @GeneratedValue
    private Long id;
    
    @Column(name="name")
    private String name;

    public Project() {
	// JPA needs empty constructor
    }
    
    public Project(String projectName) {
	this.name = projectName;
    }

    public Long getId() {
	return id;
    }

    public void setId(Long id) {
	this.id = id;
    }

    public String getName() {
	return name;
    }

    public void setName(String name) {
	this.name = name;
    }
    @Override
    public boolean equals(Object obj) {
	if (obj == this) {
	    return true;
	}

	if (!(obj instanceof Project)) {
	    return false;
	}
	Project theOtherObject = (Project) obj;
	EqualsBuilder equalsBuilder = new EqualsBuilder();
	equalsBuilder.append(theOtherObject.name, this.name);

	return equalsBuilder.isEquals();
    }
}

Next we need a data access objet (DAO) :

package org.honeysoft.hsqldbtest.dao;

import org.honeysoft.hsqldbtest.entity.Project;

public interface IProjectDao {
    void save(Project project);
}

… and its implementation (which we are going to keep short and simple):

package org.honeysoft.hsqldbtest.service.impl;

import javax.inject.Inject;
import javax.persistence.EntityManager;

import org.honeysoft.hsqldbtest.dao.IProjectDao;
import org.honeysoft.hsqldbtest.entity.Project;

public class ProjectDao implements IProjectDao {

    @Inject
    private EntityManager entityManager;

    @Override
    public void save(Project project) {
	entityManager.getTransaction().begin();
	entityManager.persist(project);
	entityManager.getTransaction().commit();
    }
}

Here comes our service which will do the business logic:

package org.honeysoft.hsqldbtest.service;

public interface IProjectService {
    void create(String projectName);
}

… and its implementation:

package org.honeysoft.hsqldbtest.service.impl;

import javax.inject.Inject;

import org.honeysoft.hsqldbtest.dao.IProjectDao;
import org.honeysoft.hsqldbtest.entity.Project;
import org.honeysoft.hsqldbtest.service.IProjectService;

public class ProjectService implements IProjectService {

    @Inject
    private IProjectDao projectDao;

    /** @{inheritDocs */
    @Override
    public void create(String projectName) {
	projectDao.save(new Project(projectName));
    }
}

So far so good. We do need some plumbing though. Within the scope of our small recipe we have two dimensions of plumbing we have to consider. One of them is the CDI and since we use Guice in our example we have to create a module keeping the dependency bindings, here it goes:

package org.honeysoft.hsqldbtest;

import javax.inject.Singleton;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.honeysoft.hsqldbtest.dao.IProjectDao;
import org.honeysoft.hsqldbtest.dao.impl.ProjectDao;
import org.honeysoft.hsqldbtest.service.IProjectService;
import org.honeysoft.hsqldbtest.service.impl.ProjectService;

import com.google.inject.AbstractModule;
import com.google.inject.Provides;

public class DBModule extends AbstractModule {

    private static final ThreadLocal<EntityManager> ENTITY_MANAGER_CACHE = new ThreadLocal<EntityManager>();

    @Override
    public void configure() {
	bind(IProjectDao.class).to(ProjectDao.class);
	bind(IProjectService.class).to(ProjectService.class);
    }

    @Provides
    @Singleton
    public EntityManagerFactory createEntityManagerFactory() {
	return Persistence.createEntityManagerFactory("db-manager");
    }

    @Provides
    public EntityManager createEntityManager(
	    EntityManagerFactory entityManagerFactory) {
	EntityManager entityManager = ENTITY_MANAGER_CACHE.get();
	if (entityManager == null) {
	    ENTITY_MANAGER_CACHE.set(entityManager = entityManagerFactory
		    .createEntityManager());
	}
	return entityManager;
    }
}

The second aspect of our concerns is the JPA. In order for JPA to be glued with its implementation (in our case Hibernate), we need a persistence.xml file located within META-INF directory in the classpath of our Java application. Since we use Maven, I’m going to put the persistence.xml file in src/main/resources/META-INF/persistence.xml. The actual content of the file is:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
	version="1.0">

	<persistence-unit name="db-manager" transaction-type="RESOURCE_LOCAL">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>

		<properties>
			<property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" />
			<property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:hsql://localhost/tmp/testdb" />
			<property name="javax.persistence.jdbc.user" value="sa" />
			<property name="javax.persistence.jdbc.password" value="" />
			<!-- if this is true, hibernate will print (to stdout) the SQL it executes, 
				so you can check it to ensure it's not doing anything crazy -->

			<property name="hibernate.show_sql" value="true" />
			<!-- since most database servers have slightly different versions of the 
				SQL, Hibernate needs you to choose a dialect so it knows the subtleties of 
				talking to that server -->
			<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
			<!-- this tell Hibernate to update the DDL when it starts, very useful 
				for development, dangerous in production -->
			<property name="hibernate.hbm2ddl.auto" value="update" />
		</properties>
	</persistence-unit>
</persistence>

Here are some important stuff:

  1. On line 7 we use the db-manager which is the same as in our DBModule.createEntityManagerFactory()
  2. On line 12, we’ve specified the URL of our database : jdbc:hsqldb:hsql://localhost/tmp/testdb which is important since we have to actually have a HSQLDB running. I’ve used an Eclipse launcher to run HSQLDB with the following program arguments: –database.0 file:/tmp/testdb –dbname.0 testdb. In case you don’t know how to create such an Eclipse launcher you can follow this link

And last but not least our test:

package org.honeysoft.hsqldbtest.service.impl;

import static org.fest.assertions.Assertions.assertThat;

import java.util.List;

import javax.persistence.EntityManager;

import org.honeysoft.hsqldbtest.DBModule;
import org.honeysoft.hsqldbtest.entity.Project;
import org.honeysoft.hsqldbtest.service.IProjectService;
import org.junit.Test;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class TestProjectService {

    @Test
    public void shouldPersistProject() throws Exception {
	// GIVEN
	Injector injector = Guice.createInjector(new DBModule());
	IProjectService projectService = injector
		.getInstance(IProjectService.class);
	EntityManager entityManager = injector.getInstance(EntityManager.class);

	String projectName = "honeysoft";

	// WHEN
	projectService.create(projectName);
	
	// THEN
	List<Project> resultList = entityManager.createNamedQuery(
		Project.GET_ALL, Project.class).getResultList();

	assertThat(resultList.contains(new Project(projectName))).isTrue();
    }
}

Well, more or less that’s it. If you want you can take advantage of HSQLDB Database Manager. One way to run it is via Eclipse launcher. You don’t need any other dependency within you project in order to run it (except for hsqldb). Create a new Java Application Eclipse launcher where as a Project you choose the one which have the hsqldb as a Maven dependency and in Main class you provide org.hsqldb.util.DatabaseManagerSwing.

Acknowledgements goes to these guys for their work and to the open-source community without which this post would not take place:

 
Leave a comment

Posted by on April 29, 2012 in Eclipse, FEST-Assert, Guice, Hibernate, HSQLDB, Java, JPA, Maven

 

Tags: