ScalaTest User Guide

Getting started

Selecting testing styles

Defining base classes

Writing your first test

Using assertions

Tagging your tests

Running your tests

Sharing fixtures

Sharing tests

Using matchers

Testing with mock objects

Property-based testing

Asynchronous testing

Using Scala-js

Using Inside

Using OptionValues

Using EitherValues

Using PartialFunctionValues

Using PrivateMethodTester

Using WrapWith

Philosophy and design

Migrating to 3.0

Migrating from ScalaTest 1.x to 2.0

We have always worked very hard to maintain source compatibility from one release of ScalaTest to the next. Upgrading is usually simply a matter of bumping the ScalaTest version number in your build doing a full recompile, and over time cleaning up any deprecation warnings. With ScalaTest 2.0, however, we made some improvements for which it was not possible to deprecate, so some of your existing code may not compile. Neverthless, we expect the vast majority of existing ScalaTest user code to just recompile as is.

To ensure the smoothest migration, first please eliminate any deprecation warnings being emitted by your current version of ScalaTest on your existing code. (For best results, upgrade to 1.9.2 first and clear any deprecation warnings, though many of those reported will still be deprecated and working in 2.0.) Then just bump the ScalaTest version number to 2.0 and do a full build. If you encounter compilation errors, check the list below for help in resolving them.

These are the points of potential breakage in ScalaTest 2.0, which are described in the sections that follow:

  1. Custom Reporters (rare, but requires some rewriting)
  2. withFixture methods (common, but easy to fix)
  3. run method signatures (rare and easy to fix)
  4. List to immutable.IndexedSeq (extremely rare, easy to fix)
  5. length, size and HavePropertyMatchers (extremely rare, easy to fix)
  6. Style classes (rare, easy to fix)
  7. The fragile base class problem (occasional, usually easy to fix)

If you have any confusion about how to migrate your ScalaTest 1.x code to ScalaTest 2.0, or encounter other problems, please post your specific issues to scalatest-users and we'll be happy to help.

Custom Reporters

We greatly enhanced ScalaTest's event model in 2.0 to improve tools integration and reporting. Because it is not possible in Scala to deprecate extractors, custom Reporters written for ScalaTest 1.x will usually need to be migrated. You still write custom Reporters in 2.0 the same way as in 1.x: you implement apply and handle events, usually by pattern matching on them. In 2.0, however, more events exist, and several of the pre-existing events now have more fields in them (though all the old fields still exist and mean the same thing). So if an existing pattern match no longer compiles, look at the event (located in package org.scalatest.events) and make necessary changes to your pattern to accomodate the new structure of the event. You may also want or need to handle events newly added in ScalaTest 2.0. For a nice list of all the 2.0 events, with links to the appropriate Scaladoc documentation, see the main documentation for Reporter.

withFixture methods

In 2.0, the withFixture methods declared in Suite and fixture.Suite, and the NoArgTest and OneArgTest functions passed to them, now return an Outcome. Because withFixture previously returned Unit, you will need to insert an = before the open curly brace of your overridden withFixture implementations if you didn't already do so, and in some cases, : Outcome =.

I.e., change a withFixture like this:

override def withFixture(test: NoArgTest) { // before
  setupDb()
  try super.withFixture(test)
  finally cleanupDb()
}

To this (by adding the = sign):

override def withFixture(test: NoArgTest) = { // after
  setupDb()
  try super.withFixture(test)
  finally cleanupDb()
}

That will most often be all that's required, but if you were catching exceptions in a withFixture implementation, you will likely also need to change your catch clauses to a corresponding pattern match on Outcome types.

I.e., change a catch clause like this one:

override def withFixture(test: NoArgTest) { // before
  try super.withFixture(test)
  catch {
    case ex: Throwable =>
      // log ex (the exception) and a screenshot
      throw ex
  }
}

To a corresponding pattern match on Outcome:

override def withFixture(test: NoArgTest) = { // after
  val outcome = super.withFixture(test)
  outcome match {
    case Failed(ex) =>
      // log ex (the exception) and a screenshot
  }
  outcome
}

run method signatures

We modified the signature of four of Suite's lifecycle methods: run, runNestedSuites, runTests, and runTest. First, to make these methods more pleasant to override, we gathered the parameters common to these methods into an Args value object. If you have previously overridden them, you'll need to change the method signature. Because the parameters have the exact same name inside the Args object, however, you can simply import args._ to get them back in scope in your existing code.

For example, you'd change an overridden method like this:

override def runTests(testName: Option[String], reporter: Reporter, // before
    stopper: Stopper, filter: Filter, configMap: Map[String, Any],
    distributor: Option[Distributor], tracker : Tracker) {

  // Your existing code
}

To this:

override def runTests(testName: Option[String], args: Args): Status = { // after

  import args._

  // Your existing code
}

By importing the members of args, all the parameter names in scope before—reporter, stopper, etc.—will again be in scope. Note that invocations of methods using the old signatures will also need to be changed to use the new signatures.

In addition to collecting parameters in Args, we changed the result type from Unit to Status. A Status is like a Future[Boolean] that ultimately will indicate whether any failed test or aborted suite occurred. The Status ``completes'' when the tests and/or nested suites started by that method have all completed. Thus you may also need change your code so that an appropriate Status is returned. If so, familiarize yourself with the convenience Status subclasses: SuccceededStatus, FailedStatus, StatefulStatus, and CompositeStatus.

Here's an example overriden runTests method that cancels an entire suite of tests if its Selenium driver is not supported. If the driver is supported, it just invokes super.runTests and returns the resulting Status. Otherwise it fires an alert message and returns SucceededStatus. (A succeeded Status doesn't necessarily mean that any tests ran, just that no test failed and no suite aborted.)

abstract override def runTests(testName: Option[String], args: Args): Status = {
  if (driverSupported)
    super.runTests(testName, args)
  else {
    alert("Cancelling " + this.suiteName + " : " + browserType + " not installed")
    SucceededStatus
  }
}

List to immutable.IndexedSeq

Where List appeared as a result type in ScalaTest 1.x, immutable.IndexedSeq is used instead in ScalaTest 2.0; for example, the result type of Suite's nestedSuites lifecycle method. (Under the covers we return a Vector.) When ScalaTest 1.0 was initially released, Vector did not yet exist in the Scala library and List was considered by many to be the default Seq type to use. In practice, it was discovered that it is too easy to use List (and other LinearSeq's) in a non-performant way. In 2.0 we wanted to take the opportunity to make the potentially breaking change of replacing List result types with immutable.IndexedSeq. This potentially breaking change is expected to affect very little, if any, user code, essentially only code that called nestedSuites then used :: or ::: operators on the result. To fix such a break, use +: instead of :: and ++ instead of :::.

length, size and HavePropertyMatchers

Although ScalaTest's matchers have undergone a major refactor in 2.0, all previously documented syntax for matchers should continue to work exactly the same with one potential exception, which should in practice be extremely rare. The potential breakage is that if you included length or size along with custom have-property matchers that you wrote, you'll get a compiler error. To fix such an error, add after your length or size invocation an (of [<type>]) clause, as described in the main Scaladoc documentation for Matchers.

Style classes

In ScalaTest 1.9.2, we refactored each style trait into a style class/style trait pair, to obtain speedier compiles. For example, in ScalaTest 1.9.1 and prior versions, FlatSpec was a trait. In 1.9.2 and 2.0, FlatSpec is a class; FlatSpecLike is the trait. Same for FunSuite (now a class) FunSuiteLike (now the trait), and so on. Usages that directly extend a style trait will continue to work fine (but should compile slightly faster). For example, this will continue to compile as before:

class MySpec extends FlatSpec with Matchers { // OK as is
  // ...
}

However, if you were mixing in a style trait after with, you'll need to add Like. For example, you'd need to change this:

class MySpec extends TestKit(ActorSystem("my actors")) // before
    with FlatSpec with Matchers {
  // ...
}

To this (by adding “Like” to FlatSpec):

class MySpec extends TestKit(ActorSystem("my actors")) // after
    with FlatSpecLike with Matchers {
  // ...
}

The fragile base class problem

To our knowledge, the only other source of potential breakage in ScalaTest 2.0 is the fragile base class problem. We have added fields and methods to traits such as Matchers and Suite in 2.0 that may conflict with fields and methods in your existing classes and cause a compiler error. Such issues can usually be easily fixed locally with simple renames or refactors.

ScalaTest is brought to you by Bill Venners and Artima.
ScalaTest is free, open-source software released under the Apache 2.0 license.

If your company loves ScalaTest, please consider sponsoring the project.

Copyright © 2009-2024 Artima, Inc. All Rights Reserved.

artima