Showing posts with label JUnit. Show all posts
Showing posts with label JUnit. Show all posts

Friday, 1 March 2019

AWS, Spring, Localstack

Using the AWS Java client is very straightforward.  Unit testing is also quite simple by just mocking the AWS classes.  However, integration testing is more complicated.  Here is an example of using LocalStack, TestContainers and Spring to wire AWS objects to point to the LocalStack instance.

Localstack: An implementation of AWS which runs locally with natively or in a docker container
TestContainers: A java library that lets a docker container be run locally for testing

Here TestContainers is used to start the localstack docker image so that the AWS calls can be made against it.


Maven dependencies

Using the v2 dependencies for the AWS library requires bringing in the AWS bom (bill of materials) so that any dependency can just be declared and the bom takes care of getting the correct versions of each dependency.  In the example below the S3 and SQS dependencies are configured.

Dependency management & dependencies

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>bom</artifactId>
        <version>2.4.11</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>s3</artifactId>
    </dependency>
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>sqs</artifactId>
    </dependency>

Test dependencies:

    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>testcontainers</artifactId>
      <version>1.10.6</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>localstack</artifactId>
      <version>1.10.6</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>cloud.localstack</groupId>
      <artifactId>localstack-utils</artifactId>
      <version>0.1.18</version>
      <scope>test</scope>
    </dependency>


AWS Configuration

The normal spring configuration for aws clients is very straight forward.  Here is an example of an S3Client and an SqsClient using the AWS v2 objects.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.sqs.SqsClient;

@Configuration
public class AwsConfiguration {

  @Bean
  public S3Client s3Client(){
    return S3Client.builder().region(Region.EU_WEST_1).build();
  }

  @Bean
  public SqsClient sqsClient(){
    return SqsClient.builder().region(Region.EU_WEST_1).build();
  }
}

These objects will use the default AwsCredentialProvider but this can be overridden here.


TestConfiguration

To create the test configuration we need to start Localstack using TestContainers.  This test configuration starts the Localstack and then uses it to configure the S3Client and SqsClient to point to localstack

import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3;
import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS;

import java.net.URI;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.containers.wait.strategy.DockerHealthcheckWaitStrategy;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain;
import software.amazon.awssdk.auth.credentials.ContainerCredentialsProvider;
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.CreateQueueRequest;

@TestConfiguration
public class AwsConfigurationTest {

  @Bean
  public LocalStackContainer localStackContainer() {
    LocalStackContainer localStackContainer = new LocalStackContainer().withServices(SQS, S3);
    localStackContainer.start();
    return localStackContainer;
  }

  @Bean
  public S3Client s3Client() {

    final S3Client client = S3Client.builder()
        .endpointOverride(URI.create(localStackContainer().getEndpointConfiguration(S3).getServiceEndpoint()))
        .build();

    client.createBucket(CreateBucketRequest.builder().bucket("test_bucket").build());

    return client;
  }

  @Bean
  public SqsClient sqsClient() {

    final SqsClient sqs = SqsClient.builder()
        .endpointOverride(URI.create(localStackContainer().getEndpointConfiguration(SQS).getServiceEndpoint()))
        .build();

    sqs.createQueue(CreateQueueRequest.builder().queueName("test_queue").build());

    return sqs;
  }
}




Thursday, 9 March 2017

Mocking new instances with Powermock

Using Powermock it is possible to mock a new instance to avoid unnecessary depth to your unit test. This is relatively unusual in an IoC environment (inversion of control eg spring) because you'd inject the PropertyLoader and therefore mocking would be easy.  However, particularly in legacy code this can be found.

If a class has this method you many not want to traverse into the PropertyLoader.

public class MyClass {

    /**
     * Method to enrich the object with a property applicable for today.
     *
     * @param myObj The object to be enriched
     */
    public void enrichWithProperties(MyObject myObj) {
        final PropertyLoader propertyLoader = new PropertyLoader();
        myObj.setProperty(propertyLoader.getTodaysProperty());
    }
}

It could be that new PropertyLoader() has a whole series of dependencies that you don't want to end up mocking as this will make the unit test really unclear.  Instead you can use Powermock to return a mocked PropertyLoader instance to greatly simply things.



import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.whenNew;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

/**
 * Test class for the .....
 */
@RunWith(PowerMockRunner.class)
@PrepareForTest(MyClass.class)
public class MyClassTest {

    /** The class under test. */
    private MyClass classUnderTest = new MyClass();

    @Test
    public void testEnrichWithProperties() {

        // Arrange
        final String todaysValue = "todaysValue";
        final MyObject myObj = new MyObject();
        final PropertyLoader mockedPropertyLoader = mock(PropertyLoader.class);
        whenNew(PropertyLoader.class).withAnyArguments().thenReturn(mockedPropertyLoader);
        when(mockedPropertyLoader.getTodaysProperty()).thenReturn(todaysValue);

        // Act
        classUnderTest.enrichWithProperties(myObj);

        // Assert
        assertEquals(todaysValue, myObj.getProperty());
    }

}


Note: Unlike the mocking statics, you have to PrepareForTest the class which instantiates the mocked class not the mocked class itself. So above MyClass is prepared not PropertyLoader.

Friday, 11 November 2016

Mocking Static Classes with Powermock

I always forget how to do this and have to look it up every time so here is a quick example for future reference!

- Annotate the class with @RunWith(PowerMockRunner.class)
- Annotate the class with @PrepareForTest(<class-to-mock>.class)
- Mock the static in @Before or in @Test PowerMockito.mockStatic(<class-to-mock>.class)
- Set expectations with when(<class-to-mock.class>.something()).thenReturn(something_else)

Example

This is a simple example which mocks StringUtils (apache commons-lang3).  This shows that the StringUtils.isBlank() method can be mocked to return different values.

    import static org.junit.Assert.assertFalse;
    import static org.junit.Assert.assertTrue;
    import static org.mockito.Mockito.when;

    import org.apache.commons.lang3.StringUtils;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.powermock.api.mockito.PowerMockito;
    import org.powermock.core.classloader.annotations.PrepareForTest;
    import org.powermock.modules.junit4.PowerMockRunner;

    /**
     * An example of using powermock to mock a static class.
     */
    @RunWith(PowerMockRunner.class)
    @PrepareForTest(StringUtils.class)
    public class ExampleTest {

        /**
         * Test blank when mocked to return that an empty string isn't blank!
         */
        @Test
        public void testBlankMocked()
        {
            // Arrange
            PowerMockito.mockStatic(StringUtils.class);
            when(StringUtils.isBlank("")).thenReturn(false);
        
            // Act
            final boolean blank = StringUtils.isBlank("");

            // Assert
            assertFalse(blank);
        }
    
        /**
         * Test blank using the standard non-mocked behaviour.
         */
        @Test
        public void testBlankNormal()
        {
            // Act
            final boolean blank = StringUtils.isBlank("");

            // Assert
            assertTrue(blank);
        }
    }


Maven Dependencies

The powermock dependencies used for this test are,

    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>1.6.5</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito</artifactId>
        <version>1.6.5</version>
        <scope>test</scope>
    </dependency>




Wednesday, 15 June 2016

AEM Unit Testing

AEM Unit testing of java code can be a bit of a pain.  Here is an example search servlet and the unit test that goes with it.

Servlet

This is a really simple servlet which searches for resources on a particular path

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Logger;

import javax.management.Query;
import javax.naming.directory.SearchResult;

import org.w3c.dom.Node;

/**
 * Servlet that can be used to search for nodes flagged as current = true on nt:unstructured nodes.
 */
@SlingServlet(paths = "/bin/search/current", methods = "GET")
public class SearchCurrentServlet extends SlingSafeMethodsServlet
{
    /**
     * Default serialisation.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Logger.
     */
    private static final Logger LOG = LoggerFactory.getLogger(SearchCurrentServlet.class);

    /**
     * The path to search.
     */
    private static final String SEARCH_PATH = "/content/my/website/data";

    /**
     * The JSON indentation level.
     */
    private static final int JSON_INDENTATION_LEVEL = 4;

    /**
     * The method called by the get.
     * {@inheritDoc}
     */
    @Override
    protected void doGet(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException, IOException
    {
        // Get the properties that have been requested
        final Map<String, Object> map = new HashMap<>();
        map.put("type", "nt:unstructured");
        map.put("path", SEARCH_PATH);

        map.put("1_property", "current");
        map.put("1_property.value", true);

        // Get the QueryBuilder and do the query from the Map above.
        final Session session = request.getResourceResolver().adaptTo(Session.class);
        final QueryBuilder builder = request.getResourceResolver().adaptTo(QueryBuilder.class);
        final Query query = builder.createQuery(PredicateGroup.create(map), session);
        query.setHitsPerPage(Integer.MAX_VALUE);
        final SearchResult result = query.getResult();

        // Iterate over the results
        final Iterator<Resource> resources = result.getResources();
        try
        {
            while (resources.hasNext())
            {
                // Get the next resource, convert to Node and make it as JSON.
                final Resource resource = resources.next();
                final Node node = resource.adaptTo(Node.class);
                final JsonJcrNode jsonJcrNode = new JsonJcrNode(node, getNodeIgnoreSet());
                response.getOutputStream().println(jsonJcrNode.toString(JSON_INDENTATION_LEVEL));
            }
        }
        catch (final RepositoryException | JSONException e)
        {
            response.getOutputStream().println("Error iterating over the results " + e.getMessage());
            LOG.error("Error iterating over the search results", e);
        }

        response.flushBuffer();
    }
}


Unit Test

The unit test uses AemContext to set up a load of mocks which are needed for the test.  However, the AemContext does not give a QueryBuilder object out of the box so an adapter has to be defined for this.  It is added to the AemContext.

import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.jcr.Session;
import javax.servlet.ServletException;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.testing.mock.sling.ResourceResolverType;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.result.SearchResult;

import io.wcm.testing.mock.aem.junit.AemContext;

/**
 * Test class for the {@link SearchCurrentServlet}.
 */
@RunWith(MockitoJUnitRunner.class)
public class SearchCurrentServletTest
{
    /**
     * The mocked query builder.
     */
    @Mock
    private QueryBuilder queryBuilder;

    /**
     * The AEM context to test with.
     */
    @Rule
    public final AemContext context = new AemContext(aemContext ->
    {
        aemContext.load().json("/result.json", "/result.json");
        aemContext.registerAdapter(ResourceResolver.class, QueryBuilder.class, queryBuilder);
    }, ResourceResolverType.JCR_MOCK);

    /**
     * Test the output in json includes fields from the node.
     *
     * @throws IOException thrown by the servlet method
     * @throws ServletException thrown by the servlet method
     */
    @Test
    public void testDoGet() throws ServletException, IOException
    {
        // Arrange
        final SearchCurrentServlet servlet = new SearchCurrentServlet();
        final MockSlingHttpServletRequest request = context.request();
        final MockSlingHttpServletResponse response = context.response();

        final Query query = mock(Query.class);
        final SearchResult searchResult = mock(SearchResult.class);
        final Resource resource = context.currentResource("/result.json");
        final List<Resource> results = new ArrayList<>();
        results.add(resource);

        when(queryBuilder.createQuery(any(PredicateGroup.class), any(Session.class))).thenReturn(query);
        when(query.getResult()).thenReturn(searchResult);
        when(searchResult.getResources()).thenReturn(results.iterator());

        // Act
        servlet.doGet(request, response);

        // Assert
        // System.out.println(response.getOutputAsString());
        assertTrue(response.getOutputAsString().contains("\"name\": \"Bob\""));
        assertTrue(response.getOutputAsString().contains("\"cost\": \"12.34\""));
    }
}


Unit Test Data

The AemContext loads a json file from src/test/resources (the classpath) and makes it available for the path specified.  When the resource is loaded with context.currentResource("/result.json"); the Resource object returned is made up of the values in the result.json file.

result.json has only the following lines

{
"jcr:primaryType":"nt:unstructured",
        "current":true,
        "name":"Bob",
        "cost":12.34
}



Tuesday, 19 May 2015

Database Testing with H2

H2 is an in-memory database which can be really useful for running unit tests against.  It allows the JPA annotated classes (Entities) to be used to write to and read from a real database.

Schema Generation

Use the previous post on Schema Generation from JPA objects to make sure that a schema.sql file exists in the test resources directory.  This can be used to create the database tables for H2 to then use.

Pom Dependencies

There are a few dependencies that need to be included in the pom file if they aren't already.  These include JUnit, H2 and spring, as well as hibernate entity manager.

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.3.174</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${spring.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>${hibernate.version}</version>
        <scope>test</scope>
    </dependency>

Spring Context for H2

This spring context (domain-test-context.xml) defines the use of H2 as the data source and initialises the database with a createSchema.sql file and the schema.sql file generated above by the hibernate4 maven plugin.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="  
        http://www.springframework.org/schema/beans       
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
        http://www.springframework.org/schema/tx       
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd  
        http://www.springframework.org/schema/jdbc   
        http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd" >

    
    <jdbc:embedded-database id="dataSource" type="H2" />
    
    <jdbc:initialize-database data-source="dataSource" ignore-failures="ALL">
        <jdbc:script location="createSchema.sql" />
        <jdbc:script location="schema.sql" />
        <!-- We could put other initialising data stuff in here -->
    </jdbc:initialize-database>
    
    <!-- This manages the entities and interactions with the database -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="packagesToScan" value="com.my.classes.impl" />
        <property name="dataSource" ref="wiDataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false" />
                <property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
                <property name="generateDdl" value="false" />
            </bean>
        </property>
        <property name="jpaProperties">
            <props>
                <!-- General settings -->
                <prop key="hibernate.archive.autodetection">class, hbm</prop>
                <prop key="hibernate.current_session_context_class">jta</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
            </props>
        </property>
    </bean>
    
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
    
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
       
    <bean id="myRepository" class="com.my.classes.repo.MyRepository" />
</beans>

This context creates the H2 database and wires the MyRepository ready for testing.

The createSchema.sql file contains the line as the schema.sql generated won't generate a database schema itself only the tables / columns which are part of that schema.

create schema MY_SCHEMA;

Test Class

This is the sample test class.  It uses the Spring Runner with the text context above.  It is ApplicationContextAware so that we can load the repository (@Autowire could be used instead) and trust spring to use the PersistenceAnnotationBeanPostProcessor to inject the EntityManager.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:domain-test-context.xml"})
public class WorkInstructionRepositoryTest implements ApplicationContextAware
{
    /**
     * This is the application context that we can load beans from.
     */
    private ApplicationContext applicationContext;

    /**
     * JPA Entity Manager.
     */
    @PersistenceContext
    private EntityManager entityManager;

    /**
     * The class under test.
     */
    private MyRepository repo;

    /**
     * {@inheritDoc}
     */
    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException
    {
        this.applicationContext = applicationContext;
    }

    /**
     * Before each test get the repository.
     */
    @Before
    public void before()
    {
        this.repo = (MyRepository) applicationContext.getBean("myRepository");
    }

    /**
     * Test creating and loading some data.
     */
    @Test
    @Transactional
    @Rollback(true)
    public void testCreateAndReadData()
    {
        // Arrange
        final SomeData data = new SomeData();
        data.setActive(true);
        data.setText("Some Data Text");

        // Act
        repo.createData(data);
        final List<DataInterface> loadedDataObjs = repo.getAllData();

        // Assert
        final DataInterface loadedData = loadedDataObjs.get(0);
        assertEquals(data.isActive(), loadedData.isActive());
        assertEquals(data.getText(), loadedData.getText());
    }
}

Wednesday, 30 April 2014

Spring Context Unit Testing

It is always a good idea to unit test the spring contexts that define an application.  This can be as simple as a unit test

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath:com/me/my_context.xml" })
    public class SpringContextTest implements ApplicationContextAware
    {
        /**
         * The spring context.
         */
        private ApplicationContext applicationContext;

        /**
         * Test the spring wiring.
         */
        @Test
        public void testSpringWiring()
        {
            final Object obj = applicationContext.getBean("myBean");
            assertTrue(obj instanceof MyBean);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void setApplicationContext(final ApplicationContext applicationContext)
        {
            this.applicationContext = applicationContext;
        }
    }


However, where there is interaction with a JNDI resource as part of the spring start up this needs to be modified to make the JNDI resource available.  Adding this @Before method will set up a JNDI resource for a MySql database.

    /**
     * Set up the jndi data source so that the wiring tests still work.
     */
    @BeforeClass
    public static void setUpClass()
    {
        // Setup the jndi context and the datasource
        try
        {
            // Create a database connection to satisfy the data loading requirements.
            final DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://server:port");
            dataSource.setUsername("username");
            dataSource.setPassword("password");

            final SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
            builder.bind("jdbc/MY_JNDI_NAME", dataSource);
            builder.activate();
        }
        catch (final NamingException ex)
        {
            LOG.error("Error creating JNDI resource", ex);
        }
    }



Thursday, 18 April 2013

JavaFX JUnit Testing

Unit testing in JavaFX is important but normal JUnit tests will fail because they are not run on the JavaFX thread.  Below is an example of how to solve this. The two classes below take care of loading JavaFX and a JUnit test runner which guarantees that JavaFX is running and makes sure that all tests are run in the JavaFX thread.

This solution is based on a JUnit4 Runner.  The new runner class extends the BlockJUnit4ClassRunner which is the default runner for JUnit tests.  If you also need the spring support then just extend the SpringJUnit4ClassRunner instead. Both runners can co-exist if necessary because they both use the JavaFxJUnit4Application class to guarantee a single JavaFx instance.

JavaFxJUnit4ClassRunner

Runs all the unit tests in the JavaFX Thread.

import java.util.concurrent.CountDownLatch;

import javafx.application.Platform;

import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

/**
 * This basic class runner ensures that JavaFx is running and then wraps all the runChild() calls 
 * in a Platform.runLater().  runChild() is called for each test that is run.  By wrapping each call
 *  in the Platform.runLater() this ensures that the request is executed on the JavaFx thread.
 */
public class JavaFxJUnit4ClassRunner extends BlockJUnit4ClassRunner
{
    /**
     * Constructs a new JavaFxJUnit4ClassRunner with the given parameters.
     * 
     * @param clazz The class that is to be run with this Runner
     * @throws InitializationError Thrown by the BlockJUnit4ClassRunner in the super()
     */
    public JavaFxJUnit4ClassRunner(final Class<?> clazz) throws InitializationError
    {
        super(clazz);
        
        JavaFxJUnit4Application.startJavaFx();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void runChild(final FrameworkMethod method, final RunNotifier notifier)
    {
        // Create a latch which is only removed after the super runChild() method
        // has been implemented.
        final CountDownLatch latch = new CountDownLatch(1);
        Platform.runLater(new Runnable()
        {
            @Override
            public void run()
            {
                // Call super to actually do the work
                JavaFxJUnit4ClassRunner.super.runChild(method, notifier);
                
                // Decrement the latch which will now proceed.
                latch.countDown();
            }
        });
        try
        {
            latch.await();
        }
        catch (InterruptedException e)
        {
            // Waiting for the latch was interruped
            e.printStackTrace();
        }
    }
}


JavaFxJUnit4Application

Manages starting JavaFX.  Uses a static flag to make sure that FX is only started once.

Manages starting JavaFX.  Uses a static flag to make sure that FX is only started once and a LOCK to make sure that multiple calls will pause if the JavaFX thread is in the process of being started.  This improves on the previous version because it doesn't involve a sleep.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javafx.application.Application;
import javafx.stage.Stage;

/**
 * This is the application which starts JavaFx.  It is controlled through the startJavaFx() method.
 */
public class JavaFxJUnit4Application extends Application
{

    /** The lock that guarantees that only one JavaFX thread will be started. */
    private static final ReentrantLock LOCK = new ReentrantLock();

    /** Started flag. */
    private static AtomicBoolean started = new AtomicBoolean();

    /**
     * Start JavaFx.
     */
    public static void startJavaFx()
    {
        try
        {
            // Lock or wait.  This gives another call to this method time to finish
            // and release the lock before another one has a go
            LOCK.lock();

            if (!started.get())
            {
                // start the JavaFX application
                final ExecutorService executor = Executors.newSingleThreadExecutor();
                executor.execute(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        JavaFxJUnit4Application.launch();
                    }
                });

                while (!started.get())
                {
                    Thread.yield();
                }
            }
        }
        finally
        {
            LOCK.unlock();
        }
    }

    /**
     * Launch.
     */
    protected static void launch()
    {
        Application.launch();
    }

    /**
     * An empty start method.
     *
     * @param stage The stage
     */
    @Override
    public void start(final Stage stage)
    {
        started.set(Boolean.TRUE);
    }
}

Sample Test

A sample test class which instantiates a Scene object.  This will fail if it isn't on the JavaFX thread.  It can be shown to fail if the @RunWith annotation is removed.


/**
 * This is a sample test class for java fx tests.
 */
@RunWith(JavaFxJUnit4ClassRunner.class)
public class ApplicationTestBase
{
    /**
     * Daft normal test.
     */
    @Test
    public void testNormal()
    {
        assertTrue(true);
    }

    /**
     * Test which would normally fail without running on the JavaFX thread.
     */
    @Test
    public void testNeedsJavaFX()
    {
        Scene scene = new Scene(new Group());
        assertTrue(true);
    }
}


JavaFxJUnit4Application - Previous version

Manages starting JavaFX.  Uses a static flag to make sure that FX is only started once.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javafx.application.Application;
import javafx.stage.Stage;

/**
 * This is the application which starts JavaFx.  It is controlled through the startJavaFx() method.
 */
public class JavaFxJUnit4Application extends Application
{
    /**
     * Flag stating if javafx has started. Static so that it
     * is shared across all instances.
     */
    private static boolean started;
    
    /**
     * Start JavaFx.
     */
    static void startJavaFx()
    {
        if (started)
        {
            return;
        }
        
        started = true;
        
        /**
         * The executor which starts JavaFx.
         */
        final ExecutorService executor = Executors.newSingleThreadExecutor();

        // Start the java fx application
        executor.execute(new Runnable()
        {
            @Override
            public void run()
            {
                JavaFxJUnit4Application.launch();
            }
        });

        // Pause briefly to give FX a chance to start
        try
        {
            Thread.sleep(1000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
    
    /**
     * Launch.
     */
    static void launch()
    {
        Application.launch();
    }

    /**
     * An empty start method.
     * 
     * @param stage The stage
     */
    @Override
    public final void start(final Stage stage)
    {
        // Empty
    }
}