Tag Archives: Scala

ScalaTest vs Specs2

Setting out to write some unit tests in Scala, I was faced with a problem that many others must have faced: which testing framework to build upon.

ScalaTest Wanting to embue my tests with a particularly Scala-some flavour, I discarded the option of using JUnit directly, narrowing the field to ScalaTest and Specs2.

I asked the question on Twitter,
specs2

How do I choose between Specs2 and ScalaTest?

and received answers (not only on Twitter) from the authors of both frameworks.

I also heard podcast interviews with both Bill Venners (ScalaTest) and Eric Torreborre (Specs2). It’s great to know that for both frameworks, constructive, friendly help is only a tweet away.

In the end, I figured that the only way to make my choice, was to implement a test first in one framework, and then in the other, and see how I felt about the results. So here it is.

Enterprise Directory and Org Chart for Atlassian ConfluenceThe test is very simple in its goals. It tests a Java class that is part of an AppFusions product called Enterprise Directory and Org Chart for Confluence. It tests that a certain condition is true after each of four different method calls. The test logic is trivial, but there is a non-trivial fixture; not an unusual testing scenario for many web application developers, I imagine. I’m using Mockito for the mock objects.

I hope that I’ve done each framework justice. In any case, my situation will be that of many a budding Scala programmer: limited time in which to read the relevant documentation, and come up with something that seems idiomatic.

First, the test written with Specs2. I’ve not shown the import statements, since they didn’t weigh into my decision-making process.

class EnterpriseDirectoryActionSpec extends Specification { def is = s2"""
  An enterprise directory action should provide enabled fields
    after a call to doDefault                                        ${fixture().e1}
    after a call to doSearchPrevious                                 ${fixture().e2}
    after a call to doSearchNext                                     ${fixture().e3}
    after a call to doExecuteSearch                                  ${fixture().e4}
    """

  case class fixture() extends Mockito {
    val fields = Seq("one", "two", "three")

    // some mock objects
    val directoryManager = mock[EnterpriseDirectoryManager]
    directoryManager.isLicenseValid returns true
    directoryManager.getEnabledFields returns fields.asJava
    directoryManager.shouldShowOrgChart("fred") returns true

    val user = mock[User]
    user.getName returns "fred"

    val sessionAccessor = mock[SessionAccessor]
    sessionAccessor.getSession returns mock[HttpSession]

    /** an instance of the class under test */
    val action = new EnterpriseDirectoryAction
    action.setEnterpriseDirectoryManager(directoryManager)
    action.setSessionAccessor(sessionAccessor)

    AuthenticatedUserThreadLocal.setUser(user)
    def e1 = { action.doDefault(); checkEnabledFields }
    def e2 = { action.doSearchPrevious(); checkEnabledFields }
    def e3 = { action.doSearchNext(); checkEnabledFields }

    def e4 = {
      action.setLastName("Bloggs")
      action.doExecuteSearch()
      checkEnabledFields
    }

    def checkEnabledFields = action.getEnabledFields.asScala must containTheSameElementsAs(fields)
  }
}

And now for the same test, written using ScalaTest.

class EnterpriseDirectoryActionFlatSpec extends FlatSpec with ShouldMatchers with MockitoSugar {
  def fixture = new {
    val fields = Seq("one", "two", "three")

    // some mock objects
    val directoryManager = mock[EnterpriseDirectoryManager]
    when(directoryManager.isLicenseValid).thenReturn(true)
    when(directoryManager.getEnabledFields).thenReturn(fields.asJava)
    when(directoryManager.shouldShowOrgChart("fred")).thenReturn(true)

    val user = mock[User]
    when(user.getName).thenReturn("fred")

    val sessionAccessor = mock[SessionAccessor]
    when(sessionAccessor.getSession).thenReturn(mock[HttpSession])

    /** an instance of the class under test */
    val action = new EnterpriseDirectoryAction
    action.setEnterpriseDirectoryManager(directoryManager)
    action.setSessionAccessor(sessionAccessor)

    AuthenticatedUserThreadLocal.setUser(user)
  }

  "An enterprise directory action" should "provide enabled fields after a call to doDefault" in {
    val f = fixture
    f.action.doDefault()
    f.action.getEnabledFields.asScala should be (f.fields)
  }

  it should "provide enabled fields after a call to doSearchPrevious" in {
    val f = fixture
    f.action.doSearchPrevious()
    f.action.getEnabledFields.asScala should be (f.fields)
  }

  it should "provide enabled fields after a call to doSearchNext" in {
    val f = fixture
    f.action.doSearchNext()
    f.action.getEnabledFields.asScala should be (f.fields)
  }

  it should "provide enabled fields after a call to doExecuteSearch" in {
    val f = fixture
    f.action.setLastName("Bloggs")
    f.action.doExecuteSearch()
    f.action.getEnabledFields.asScala should be (f.fields)
  }
}

So there you have it. I think I prefer the ScalaTest version, but that may be because it most closely resembles the JUnit-style tests that I’m familiar with. Although slightly more verbose, I figure that it will also be the most comfortable for my colleagues, who I hope will want to experiement with Scala too. Even with the recently-introduced string interpolation in Specs2, I find the manner of specifying test examples in a column on the right-hand side of my test conditions a bit awkward. Yes, it is possible to embed test conditions with the text – described in the Specs2 documentation as unit-testing-style test specification – but then mixing in the Mockito sugar seemed like a tricky business.

Specs2 does appeal from the somewhat philosophical point of view of cleaving more closely to the immutability everywhere ideal, but heck, in this case I’ll sacrifice that notional purity for niceness.