technology from back to front

Stackable traits for ScalaTest test suites

When your application is based on Spring it makes a lot of sense to fire up
a Spring context within your integration tests and functional tests.

For a particular Scala-based project it was necessary to manage not only the
lifetime of the Spring context, but also the lifetime of an annotation-based REST library component called Jersey, which works together with Spring.

Each of these have their own setup and teardown actions. Some tests require just Spring, and others require both Jersey and Spring to be started (and stopped). It gets even more involved because there’s the choice of starting and stopping either of these options before and after the whole test suite, or before and after every test.

Yes, it seems like a lot of work, but the results are well worth it: it means we can test the behaviour of the Jersey REST API by setting expectations on Spring components just before invoking API calls.

It turns out that there’s an elegant way in which Scala’s traits can express these various startup / teardown behaviours through [stackable modifications](http://www.artima.com/pins1ed/traits.html#12.5). Traits isolate cross-cutting concerns and further allow them to be declaratively mixed-in to test suites that `with` them as needed (Scala jargon, think `implements` in Java!). Using the approach we’re also explicit about how Jersey is started **after Spring is started**, remembering the order in which traits are mixed into a class does matter!

We didn’t start off with that approach though…

## Before…

So when I look at this problem, I see it as a number of orthogonal concerns:

– Before & After **Tests** vs. **Test Suite**
– **Spring** vs. **Spring+Jersey**
– **Start/Stop** Sequencing

Fortunately the first of these is already taken care of. ScalaTest authors have
taken care to provide the `BeforeAndAfter[Each]` and `BeforeAndAfterAll` traits,
an idiomatic way to specify whether the specified code should run before/after every
test or before/after all tests in the suite.

The code sure did make use of these, but in wholly the wrong way. Spring-related tests
were made to extend a custom subclass of `FunSpec`:

class SpringContextSpec extends FunSpec { var ctx: ApplicationContext = _ def startSpring() { ctx = new ClassPathXmlApplicationContext("classpath*:/applicationContext-test.xml") } def stopSpring() { ctx.destroy() } // Interesting auto-wired bean definitions as Scala vals... // Helper functions... // Spring-related oddities... }

This is somewhat undesirable, because in every test suite it is necessary to explicitly call `startSpring()`
and `stopSpring()`, like so:

class NotSoAwesomeTestSuite1 extends SpringContextSpec with BeforeAndAfter { before { startSpring() } after { stopSpring() } // Here be dragons }

The reason that we want to do this is because `startSpring()` and `stopSpring()` is useful for `beforeAll()` and `afterAll()` as well.

class NotSoAwesomeTestSuite2 extends SpringContextSpec with BeforeAndAfterAll override def beforeAll() { startSpring() } override def afterAll() { stopSpring() } // Here be dragons }

Both of these kinds of TestSuite do indeed occur. The boilerplate is ugly, but it is necessary under this design. The only way to hide the code’s hideousness is to introduce two new abstractions called `SpringContextSpecEach` and `SpringContextSpecAll` and have tests inherit from those accordingly.

And what if some tests needed Jersey? Say they extended a class called `JerseySpringContextSpec` defined like below:

class JerseySpringContextSpec extends SpringContextSpec { startJersey() stopJersey() // ... }

Then `JerseySpringContextSpec` too would require its own ‘Each’ and ‘All’ subclasses: `JerseySpringContextSpecEach`
`JerseySpringContextSpecAll` etc. In doing so we would be advocating an inflexible inheritance-based solution, exposing ourselves to a combinatorial explosion of noisy and confusing subclasses. It’s just plain nasty.

Stackable traits to the rescue.

## After…

Ideally we want all of that behaviour directly configurable in the test suite declaration like the below.
Square brackets and pipe (|) denote option and alternative respectively:

class IdealTestSuite extends FunSpec with BeforeAndAfter(Each|All) with Spring [with Jersey] with ... { // Here be quite genuinely useful tests }

… and have the code do exactly the right thing before and after as we’d expect. This declaration can also
ensure that Spring is started before Jersey.

That’s the theory anyway. Practise is often another wildly different beast. Though you’ll be pleased to know that my
solution for this was actually incredibly close to the ideal:
class RealTestSuite extends FunSpec with StartStopBeforeAndAfter(Each|All) with (Spring|Jersey) with ... { // Here be quite genuinely useful tests }

Not only is it more readable, it is much more declarative. It allows the
programmer to change the execution behaviour of the test suite by changing 4
characters!

The key here is a special trait that I haven’t shown you yet. It’s called
`StartStop` and its job is to represent the concept of ‘something that can be
started and stopped’.

trait StartStop { def start() {} def stop() {} }

The implementations of `start()` and `stop()` do nothing at the moment but very soon they will be given proper meaning. Now we’d like to say connect this to the two sorts of before/after:

trait StartStopBeforeAndAfterEach extends Suite with BeforeAndAfterEach with StartStop { override def beforeEach() { start() } override def afterEach() { stop() } }
and
trait StartStopBeforeAndAfterAll extends Suite with BeforeAndAfterAll with StartStop { override def beforeAll() { start() } override def afterAll() { stop() } }

Great; so `start()` and `stop()` are now invoked before/after each/all as we please. They still do nothing at the moment, so now is a good time to imbue meaning upon the `start()` and `stop()` methods.

Start and stop Spring:

trait Spring extends StartStop { var ctx: ApplicationContext = def springContext = new ClassPathXmlApplicationContext("classpath*:/applicationContext-test.xml") abstract override def start() { super.start() ctx = springContext } abstract override def stop() { ctx.destroy() super.stop() } // Spring-specific fields and methods }

… and start and stop Jersey (necessarily after Spring):

trait Jersey extends StartStop with Spring { var jerseyTest: JerseyTest = null abstract override def start() { super.start() jerseyTest = new JerseyTest(new LowLevelAppDescriptor.Builder("net.lshift.very.important.project").build()) { override def getTestContainerFactory = new JerseyInMemoryContainerFactory(ctx) } jerseyTest.setUp() } abstract override def stop() { jerseyTest.tearDown() super.stop() } // Jersey-specific fields and methods }

Et voilà. What clear-cut code!

What we’ve done here is separated out the concerns just by using Scala traits: remarkably there are no annotations to be seen. Had this been Java we’d have to use a language extension like [AspectJ](http://www.eclipse.org/aspectj/).

The code also describes that Jersey initialisation necessarily follows Spring initialisation thanks to the way trait linearisation works.

Much of the magic comes from a combination of the `abstract override def (start|stop)` declaration and calls to `super.(start|stop)()`.
When you mix-in a trait that has `abstract override` methods in it, you’re expecting an existing implementation of that method already on your class stack. This is why is important to provide a no-op implementation of `start()` and `stop()` methods in `StartStop` trait: so that they can be overridden!

The `super.(start|stop)()` calls form form a chain of invocations, just like reading along the class declaration line from right to left. This ensures everything is started and stopped in the right order. No more horrible `startSpring()` and `stopSpring()` methods to be seen.

For more details on delinearization and how stackable modifications actually work
under the covers, I recommend the chapter on [Programming in
Scala](http://www.artima.com/pins1ed/traits.html#12.5).

#### Disclaimer

I don’t particularly recommend using Spring with Scala by the way.
Spring being Spring, there is the official ‘Spring way’ of writing and
structuring tests, but then we stuck to the annotation-free real-world.
If you find that the above code can work better with annotation-based Spring configuration, please do that instead of the way we manage the `ClassPathXmlApplicationContext` explicitly.

And anyway, if all you’re using Spring for is just DI, consider tools native to Scala first, like [Subcut](https://github.com/dickwall/subcut).

Hope this helps!

by
hok
on
24/11/12
 
 


9 − four =

2000-14 LShift Ltd, 1st Floor, Hoxton Point, 6 Rufus Street, London, N1 6PE, UK+44 (0)20 7729 7060   Contact us