Learn Scala with Specs2 Spring

Use Scala code to safely test and debug Spring-based Java apps

Using Specs2 Spring to test your Java apps is an efficient and safe way to learn the patterns of object-functional programming with Scala: you get the benefits of Specs2 (a Scala-based open source testing framework) without leaving the Spring framework or rewriting perfectly good Java code. Specs2 Spring creator Jan Machachek walks through three testing examples, including a short introduction to acceptance testing, which will be integrated in the forthcoming Specs2 Spring v1.0.

The Spring framework in its early days revolutionized Java enterprise application development. Lightweight and sensible, Spring simplified the boilerplate code that made Java programming time-consuming and tedious. It abstracted the details of essential Java libraries so that developers could just use them in their programs. It made Java programming fun again, and it did so at a time when many Java developers had become resigned to complexity. Those were the days of Java 1.4.

Today, the lightweight programming paradigm that Spring popularized has become nearly a gold standard for Java enterprise tools and frameworks. Spring has become less of a radical alternative and more of a reliable workhorse, used by hundreds of thousands of software developers. Collectively, we've invested millions of lines of code into our Spring-based applications, and we'll write millions more. But we might not be writing our next applications in Java code.

This article is for the Java developer who wants to learn Scala, but doesn't want to rewrite existing Java apps or leave the Spring framework to do it. I'll briefly discuss the benefits of writing Spring components in Scala, and then introduce the Specs2 test framework and Specs2 Spring -- an extension I wrote for Cake Solutions, where we use it to introduce Scala code to Spring-based Java applications. I'll walk through several scenarios of using Scala to test Java code, including a unit-testing example and an integration-testing example. I'll conclude with a brief demonstration of acceptance testing using Specs2 Spring, which will be part of the forthcoming Spec2 Spring v 1.0 release.

Note that this article is intended for developers familiar with the Spring framework.

Spring and Scala

As a Spring developer, you probably know that there are advantages to integrating Scala with Spring, and also some challenges. For one thing, Scala rejects Spring's dependency injection model. In Scala, we do not inject instances of other objects into existing components but compose functionality. Scala replaces Spring's dependency injection with composition inheritance. But as a strongly-typed object-functional language, Scala offers much more than just a different approach to dependency injection. Scala lets you construct rich type systems and leverage functional programming, while still doing object-oriented programming. With Scala, you can express concepts that are difficult or impossible to implement in Java code, an increasingly important advantage for developers building (for instance) highly concurrent applications.

Spring DI meets Scala composition

The Spring framework at its core is a dependency injection container. It lets developers create loosely coupled components and then specify metadata, which it uses to construct and wire up an application's components. A die-hard Scala programmer would reject Spring's dependency injection model, however. In Scala, we do not inject instances of other objects but compose functionality. Scala replaces Spring's dependency injection with its own composition inheritance.

Rather than rewrite Spring-based Java applications in pure Scala, perhaps using Scala's Cake pattern (an alternative to dependency injection), you can start integrating Scala into your Java projects as test code. This approach will let you leverage the functional features of the Scala language and its testing frameworks without rewriting your main code. You will learn Scala while also keeping your Java programs relatively safe: safe from Scala beginner code. As you use Scala in your tests, you'll gradually discover the patterns of a Scala system. You'll learn over time where it's appropriate to apply Scala's patterns in your Java apps. Eventually, you'll begin to write new components in Scala, even in your main codebase.

Using Scala to test Java code

We'll start out with a typical Spring-based Java application. Let's say we've created a domain class using JPA annotations, then used Hibernate to persist the objects in an RDBMS. We've also used Hibernate in our service object. We have a domain with a Rider class and a RiderManager that depends on Hibernate's API. We've used the Spring framework to manage dependencies between all of the components that make up the application. The application is implemented entirely in Java code.

Now we can implement a standard JUnit test with Scala. This is no problem because Scala compiles to Java bytecode. As far as the JVM is concerned, there is no difference between Scala and Java. Writing a JUnit test in Scala lets us benefit from Scala's concise syntax with inferred types; you can see this in Listing 1.

Listing 1. Old-school JUnit test written in Scala

@RunWith(classOf[JUnit4])
    class RiderManagerOldTest {
        private var riderManager: RiderManager = _
        private val hibernateTemplate: HibernateTemplate = Mockito.mock(classOf[HibernateTemplate])

        @Before
        def setup() {
            this.riderManager = new RiderManager(this.hibernateTemplate)
        }

        @Test
        def generate() {
            this.riderManager.generate(2)
            val allRiders = this.riderManager.findAll
            Assert.assertThat(allRiders.size(), CoreMatchers.is(2))
        }
    }

The Scala code in Listing 1 matches exactly the Java code it has replaced; it's just a lot cleaner. I've removed all public keywords, replacing @Test-annotated methods returning void with def. The fields use the keyword var for a variable that can be bound-to more than once, and val for a variable that can be bound-to only once. Listing 1 gives you a glimpse of some of the benefits of Scala: no need for semicolons at the end of every line and type inference of the allRiders variable. In Listing 2, I mechanically translate the Scala code back into Java.

Listing 2. Scala code translated back to Java

@RunWith(JUnit4.class)
    public class RiderManagerOldTest {
        private RiderManager riderManager;
        private HibernateTemplate hibernateTemplate = Mockito.mock(HibernateTemplate.class);

        @Before
        public void setup() {
            this.riderManager = new RiderManager(this.hibernateTemplate);
        }

        @Test
        public void generate() {
            this.riderManager.generate(2);
            List <Rider> allRiders = this.riderManager.findAll();
            Assert.assertThat(allRiders.size(), CoreMatchers.is(2));
        }
    }

Specs2 and Specs2 Spring

Specs2 is a Scala testing framework written by Eric Torreborre. Rather than tests, testing in Specs2 is based on specifications. Like unit testing, Specs2 lets you focus on and verify the expected behavior of a single class via unit specifications. Alternately, you can verify the entire system, via acceptance specifications (see Resources).

Using Specs2, you can write the body of a Java application test and execute it. As with JUnit, Specs2 lets you configure automated build tools to execute Specs2 specifications as part of the build process. Tests in Specs2 are written in Scala, a boon to developers who prefer more concise tests written in a more natural language.

Specs2 is well integrated with JUnit. In fact, you can utilize JUnit to execute Specs2 test code, just by annotating your specification classes with @RunWith(classOf[JUnitRunner]). The JUnit runner specifies that JUnit will run the specifications; however, the body of the Specs2 specification remains full Specs2 code. There is no need for @Test, @Before, and other JUnit annotations.

Licensing

Specs2 is available under the MIT license; Specs2 Spring is available also under the MIT license.

Specs2 Spring is an extension of Specs2 that makes it simpler to test Spring applications written in Java or Scala. Specs2 Spring includes code that will help you set up the context for an entire integration test. The appropriate entries in the JNDI environment, as well as the beans under test, are autowired into the instance of the test under execution. Specs2 Spring also can be configured to run every example in its own transaction that rolls back automatically when the example completes.

Unit testing with Specs2 Spring

We can get started with Specs2 Spring by writing unit specifications that verify the behavior of our Spring components. Consider this test of the RiderManager class, which uses Spring's HibernateTemplate dependency with mock objects via Mockito.

Listing 3. Unit specification with Specs2

class RiderManagerUnitSpec extends Specification with Mockito {
        private val hibernateTemplate = mock[HibernateTemplate]
        private val riderManager = new RiderManager(hibernateTemplate)

        "The RiderManager should" {
            "findAll Riders" in {
                // setup the mock of HibernateTemplate here
                riderManager.findAll.size() must_==(0)
            }

            "generate generate n Riders" in {
                riderManager.generate(2)
                there was two(hibernateTemplate).saveOrUpdate(any[Rider])
            }
        }
    }

Note that the specification in Listing 3 is essentially a behavior-driven development (BDD) specification (see Resources). It is a more expressive equivalent of a standard unit test. Writing unit tests (or specifications) means writing a lot of code, especially in a system whose components have many dependencies between them. To get more bang out of specification code, let's see what happens when we switch from unit testing to integration testing.

Integration testing in Specs2 Spring

An integration specification tests several application components working together. Integration testing is complicated by the fact that Java EE application components often have external dependencies such as javax.sql.DataSource, javax.mail.Session, javax.jms.Queue, javax.jms.Topic, javax.transaction.TransactionManager, and many others.

In order to keep application source code as maintainable as possible, and to remove as much complexity from the build process as possible, we want to keep the source code that is running on the development machines in sync with the source code that is deployed on the pre-production and production servers.

Each machine will of course have its own RDBMS and JMS configuration, and some details of the configuration likely are (and should be) inaccessible to us as developers. For instance, the RDBMS configuration for the production machine should not be left to developers.

If we were testing the Spring way, we'd use the spring-test artifact with the @ContextConfiguration annotation on a JUnit test. Spring would then construct the components under test by examining the Spring configuration files. The spring-test would leave us to set up the JNDI environment, a non-trivial and repetitive task. So we have two challenges: first, to bring the equivalent of spring-test to Scala and Specs2, then to set up the JNDI environment for the test.

Setting up the JNDI environment

Java allows us to register resources in JNDI; these resources could include a JDBC DataSource, a JMS ConnectionFactory, a Queue, a JavaMail Session, and so on. While it's a simplification, you could think of JNDI as a Map<String, Object> (or, speaking in Scala: Map[String, AnyRef]). Essentially, the application consumes objects from JNDI and uses them in its code. In Spring applications, the consumed JNDI resources will most likely become beans in the Spring ApplicationContext.

Learn more

For further details of the JNDI and Spring setup code, see the Specs2 Spring documentation from Cake Solutions.

In addition to test code we need to include code that sets up JNDI for the test. The JDNI code should be reusable, and it also must run prior to other code in the test case. We see this in Listing 4, where the specification's setup code prepares the JNDI environment, then adds the Spring ApplicationContext, and finally injects the required beans into the specification, as shown in Listing 4.

As developers, we code the @DataSource, @TransactionManager, and @ContextConfiguration annotations. Specs2 Spring then sets up the JNDI environment for the test and builds the Spring ApplicationContext with the components under test. Finally, it injects the instances of the components from the ApplicationContext into the test. (Note @Autowired var riderManager: RiderManager = _ in Listing 4.)

Listing 4. Specs2 Spring

@DataSource(name = "java:comp/env/jdbc/test",
        driverClass = classOf[JDBCDriver], url = "jdbc:hsqldb:mem:test")
    @TransactionManager(name = "java:comp/TransactionManager")
    @ContextConfiguration(Array("classpath*:/META-INF/spring/module-context.xml"))
    class RiderManagerSpec extends Specification {
        @Autowired var riderManager: RiderManager = _

        "Generate 10 riders" in {
            val count = 10
            this.riderManager.generate(count)
            this.riderManager.findAll.size() must be_==(count)
        }

    }


1 2 Page
Recommended
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more