ScalaTest – A Popular DSL

Note

ScalaTest was introduced in Chapter 1, Setting up the Development Environment, but as we'll use it extensively in this lecture, we'll do a little recap here and make sure that everyone has a working ScalaTest environment.

In this section, we'll have a look at a popular library for testing your Scala programs, ScalaTest, and see how the library uses DSLs to allow its users to write readable tests in various styles.

The purpose of looking at ScalaTest is twofold. First off, ScalaTest is a widely used testing library for Scala projects, so you're likely to end up using it when you're using Scala professionally. Secondly, it's a good example of how to use DSLs to make your code more readable.

By the end of this section, you should be able to:

  • Identify how to use ScalaTest in your own projects
  • Identify the various styles that ScalaTest offers and be able to pick the one that's relevant to your project
  • Write ScalaTest tests using the FlatSpec style

Adding ScalaTest to Your Project

ScalaTest is a Scala library like any other, so you simply add it as a library dependency to your project. As we're using SBT in this book, we'll use that as an example here. Create a new SBT project with the following build.sbt file:

name := "Lession2-ScalaTest"
scalaVersion := "2.12.4"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.4" % "test"

Note

For more information, refer to the installation section (http://www.scalatest.org/install) from the documentation if you want to see how to use it outside of SBT.

Create a simple test and place it in your src/test/scala/com/example/ExampleSpec.scala project:

package com.example

import collection.mutable.Stack
import org.scalatest._

class ExampleSpec extends FlatSpec with Matchers {
  "A Stack" should "pop values in last-in-first-out order" in {
    val stack = new Stack[Int]
    stack.push(1)
    stack.push(2)
    stack.pop() should be (2)
    stack.pop() should be (1)
  }
}

To verify that your setup is correct, start an SBT session in the root of your project and run the following command:

test:compile                     # to compile your tests
test                             # to run your test-suite
testOnly com.example.ExampleSpec # To run just that test

You should see output similar to the following:

testOnly com.example.ExampleSpec
[info] ExampleSpec:
[info] A Stack
[info] - should pop values in last-in-first-out order
[info] Run completed in 282 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 6 s, completed Dec 4, 2017 9:50:04 PM

As we'll be writing a few tests using ScalaTest in the following section, it's important that you have a correctly configured SBT project that you can use for the exercises. Follow these steps:

  1. Create a new SBT project using the previous build.sbt definition.
  2. Create a new test file at src/test/scala/com/example/ExampleSpec.scala with the previous contents.
  3. Run the tests using the sbt test command and make sure that it has detected the tests and that they pass.

You've seen how to add ScalaTest to your Scala project and how to run the tests using SBT. You should now have a correctly configured Scala project that you can use for the remainder of the exercises in this chapter. In the next section, we'll have a look at the various styles of tests you can write using ScalaTest.

Overview of ScalaTest Styles

ScalaTest offers a selection of different styles that you can use when you're writing your tests. What style to use depends on your team's experience and preference.

In this section, we'll have a look at some of the different styles so you can get a feeling for what style you prefer:

  • FunSuite is a simple style that will be familiar to most people:
    describe("A Set") {
    
      describe("(when empty)") {
        it("should have size 0") {
    
          assert(Set.empty.size == 0)
    
        }
    
      }
    
    }
  • FlatSpec is very similar to FunSuite but it focuses more on Behavior-Driven Design (BDD) by forcing you to name your tests in a manner that reads more like a specification:
    "An empty Set" should "have size 0" in {
    
        assert(Set.empty.size == 0)
    
    }
  • FunSpec is a good general-purpose style for writing specification-style tests:
    describe("A Set") {
    
      describe("(when empty)") {
    
        it("should have size 0") {
    
          assert(Set.empty.size == 0)
    
        }
    
      }
    
    }
  • FreeSpec focuses on specification-style testing but doesn't enforce any structure upon your tests:
    "A Set" - {
    
      "(when empty)" - {
    
        "should have size 0" in {
    
          assert(Set.empty.size == 0)
    
        }
    
      }
    
    }
  • PropSpec is for if you want to write tests exclusively in terms of property checks:
    property("An empty Set should have size 0") {
    
        assert(Set.empty.size == 0)
    
    }
  • FeatureSpec is primarily intended for acceptance testing:
    class TVSetSpec extends FeatureSpec with GivenWhenThen {
    
      info("As a TV set owner")
    
      info("I want to be able to turn the TV on and off")
    
    
    
      feature("TV power button") {
        scenario("User presses power button when TV is off") {
    
          Given("a TV set that is switched off")
    
          val tv = new TVSet
          assert(!tv.isOn)
    
          When("the power button is pressed")
    
          tv.pressPowerButton()
    
          Then("the TV should switch on")
    
          assert(tv.isOn)
    
        }
    
      }
    
    }

Example: FunSuite

Let's have a look at the test case we created in the previous section:

package com.example

import collection.mutable.Stack
import org.scalatest._

class ExampleSpec extends FlatSpec with Matchers {
 "A Stack" should "pop values in last-in-first-out order" in {
   val stack = new Stack[Int]
   stack.push(1)
   stack.push(2)
   stack.pop() should be (2)
   stack.pop() should be (1)
}
}

There are two internal DSLs in action here. The first one is used to write your test specifications in the readable form of "X" should "Y" in { <code> }. This style is made available by extending FlatSpec. The other DSL is used to write your assertions in the form of <expression> should be <expression>, which is made available by extending Matchers.

The DSLs are implemented as a combination of classes and extension methods, but we'll look into that in greater detail when we implement our own little DSL in the next section.

Activity: Implementing ScalaTest Styles

The best way to get a feeling for the different styles is to use them. Select three of the styles from the previous list and convert the following test to those styles.

  1. Continue using the Scala project you created in the previous activity.
  2. Create a file for each style you've selected. If you picked FunSpec, then create a FunSpecExample.scala file.
  3. For each style, convert the following test into a test that uses that style:
    import collection.mutable.Stack
    import org.scalatest._
    
    class ExampleSpec extends FlatSpec with Matchers {
     "A Stack" should "pop values in last-in-first-out order" in {
       val stack = new Stack[Int]
       stack.push(1)
       stack.push(2)
       stack.pop() should be (2)
       stack.pop() should be (1)
     }
    }

You've seen the different styles that ScalaTest offers and have a rough feeling for the difference between them.

ScalaTest is a testing library that uses DSLs to make it possible to write very readable tests. We have seen how you can add it to your own Scala projects, we got an overview of the different styles that it supports, and we have written a few tests using different styles. In the next section, we'll look at the Scala features that Scala provides which make it possible to write DSLs in Scala.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset