org.scalatest

trait FunSuite

[source: org/scalatest/FunSuite.scala]

trait FunSuite
extends Suite
A suite of tests in which each test is represented as a function value. The “Fun” in FunSuite stands for functional. Here's an example FunSuite:
 import org.scalatest.FunSuite

 class MySuite extends FunSuite {

   test("addition") {
     val sum = 1 + 1
     assert(sum === 2)
     assert(sum + 2 === 4)
   }

   test("subtraction") {
     val diff = 4 - 1
     assert(diff === 3)
     assert(diff - 2 === 1)
   }
 }
 

test” is a method, defined in FunSuite, which will be invoked by the primary constructor of MySuite. You specify the name of the test as a string between the parentheses, and the test code itself between curly braces. The test code is a function passed as a by-name parameter to test, which registers it for later execution. One benefit of FunSuite compared to Suite is you need not name all your tests starting with “test.” In addition, you can more easily give long names to your tests, because you need not encode them in camel case, as you must do with test methods.

Test fixtures

A test fixture is objects or other artifacts (such as files, sockets, database connections, etc.) used by tests to do their work. You can use fixtures in FunSuites with the same approaches suggested for Suite in its documentation. The same text that appears in the test fixture section of Suite's documentation is repeated here, with examples changed from Suite to FunSuite.

If a fixture is used by only one test, then the definitions of the fixture objects should be local to the test function, such as the objects assigned to sum and diff in the previous MySuite examples. If multiple tests need to share a fixture, the best approach is to assign them to instance variables. Here's a (very contrived) example, in which the object assigned to shared is used by multiple test functions:

 import org.scalatest.FunSuite

 class MySuite extends FunSuite {

   // Sharing fixture objects via instance variables
   val shared = 5

   test("Addition") {
     val sum = 2 + 3
     assert(sum === shared)
   }

   test("Subtraction") {
     val diff = 7 - 2
     assert(diff === shared)
   }
 }
 

In some cases, however, shared mutable fixture objects may be changed by test methods such that it needs to be recreated or reinitialized before each test. Shared resources such as files or database connections may also need to be cleaned up after each test. JUnit offers methods setup and tearDown for this purpose. In ScalaTest, you can use the BeforeAndAfter trait, which will be described later, to implement an approach similar to JUnit's setup and tearDown, however, this approach often involves reassigning vars between tests. Before going that route, you should consider two approaches that avoid vars. One approach is to write one or more "create" methods that return a new instance of a needed object (or a tuple of new instances of multiple objects) each time it is called. You can then call a create method at the beginning of each test that needs the fixture, storing the fixture object or objects in local variables. Here's an example:

 import org.scalatest.FunSuite
 import scala.collection.mutable.ListBuffer

 class MySuite extends FunSuite {

   // create objects needed by tests and return as a tuple
   def createFixture = (
     new StringBuilder("ScalaTest is "),
     new ListBuffer[String]
   )

   test("Easy") {
     val (builder, lbuf) = createFixture
     builder.append("easy!")
     assert(builder.toString === "ScalaTest is easy!")
     assert(lbuf.isEmpty)
     lbuf += "sweet"
   }

   test("Fun") {
     val (builder, lbuf) = createFixture
     builder.append("fun!")
     assert(builder.toString === "ScalaTest is fun!")
     assert(lbuf.isEmpty)
   }
 }
 

Another approach to mutable fixture objects that avoids vars is to create "with" methods, which take test code as a function that takes the fixture objects as parameters, and wrap test code in calls to the "with" method. Here's an example:

 import org.scalatest.FunSuite
 import scala.collection.mutable.ListBuffer

 class MySuite extends FunSuite {

   def withFixture(testFunction: (StringBuilder, ListBuffer[String]) => Unit) {

     // Create needed mutable objects
     val sb = new StringBuilder("ScalaTest is ")
     val lb = new ListBuffer[String]

     // Invoke the test function, passing in the mutable objects
     testFunction(sb, lb)
   }

   test("Easy") {
     withFixture {
       (builder, lbuf) => {
         builder.append("easy!")
         assert(builder.toString === "ScalaTest is easy!")
         assert(lbuf.isEmpty)
         lbuf += "sweet"
       }
     }
   }

   test("Fun") {
     withFixture {
       (builder, lbuf) => {
         builder.append("fun!")
         assert(builder.toString === "ScalaTest is fun!")
         assert(lbuf.isEmpty)
       }
     }
   }
 }
 
One advantage of this approach compared to the create method approach shown previously is that you can more easily perform cleanup after each test executes. For example, you could create a temporary file before each test, and delete it afterwords, by doing so before and after invoking the test function in a withTempFile method. Here's an example:
 import org.scalatest.FunSuite
 import java.io.FileReader
 import java.io.FileWriter
 import java.io.File
 
 class MySuite extends FunSuite {
 
   def withTempFile(testFunction: FileReader => Unit) {
 
     val FileName = "TempFile.txt"
  
     // Set up the temp file needed by the test
     val writer = new FileWriter(FileName)
     try {
       writer.write("Hello, test!")
     }
     finally {
       writer.close()
     }
  
     // Create the reader needed by the test
     val reader = new FileReader(FileName)
  
     try {
       // Run the test using the temp file
       testFunction(reader)
     }
     finally {
       // Close and delete the temp file
       reader.close()
       val file = new File(FileName)
       file.delete()
     }
   }
 
   test("Reading from the temp file") {
     withTempFile {
       (reader) => {
         var builder = new StringBuilder
         var c = reader.read()
         while (c != -1) {
           builder.append(c.toChar)
           c = reader.read()
         }
         assert(builder.toString === "Hello, test!")
       }
     }
   }
 
   test("First char of the temp file") {
     withTempFile {
       (reader) => {
         assert(reader.read() === 'H')
       }
     }
   }
 }
 

If you are more comfortable with reassigning instance variables, however, you can instead use the BeforeAndafter trait, which provides methods that will be run before and after each test. BeforeAndAfter's beforeEach method will be run before, and its afterEach method after, each test (like JUnit's setup and tearDown methods, respectively). For example, here's how you'd write the previous test that uses a temp file with BeforeAndAfter:

 import org.scalatest.FunSuite
 import org.scalatest.BeforeAndAfter
 import java.io.FileReader
 import java.io.FileWriter
 import java.io.File

 class MySuite extends FunSuite with BeforeAndAfter {

   private val FileName = "TempFile.txt"
   private var reader: FileReader = _

   // Set up the temp file needed by the test
   override def beforeEach() {
     val writer = new FileWriter(FileName)
     try {
       writer.write("Hello, test!")
     }
     finally {
       writer.close()
     }

     // Create the reader needed by the test
     reader = new FileReader(FileName)
   }

   // Close and delete the temp file
   override def afterEach() {
     reader.close()
     val file = new File(FileName)
     file.delete()
   }

   test("Reading from the temp file") {
     var builder = new StringBuilder
     var c = reader.read()
     while (c != -1) {
       builder.append(c.toChar)
       c = reader.read()
     }
     assert(builder.toString === "Hello, test!")
   }

   test("First char of the temp file") {
     assert(reader.read() === 'H')
   }
 }
 

In this example, the instance variable reader is a var, so it can be reinitialized between tests by the beforeEach method. If you want to execute code before and after all tests (and nested suites) in a suite, such as you could do with @BeforeClass and @AfterClass annotations in JUnit 4, you can use the beforeAll and afterAll methods of BeforeAndAfter. See the documentation for BeforeAndAfter for an example.

Test groups

A FunSuite's tests may be classified into named groups. As with any suite, when executing a FunSuite, groups of tests can optionally be included and/or excluded. To place FunSuite tests into groups, you pass objects that extend abstract class org.scalatest.Group to methods that register tests. Class Group takes one parameter, a string name. If you have created Java annotation interfaces for use as group names in direct subclasses of org.scalatest.Suite, then you will probably want to use group names on your FunSuites that match. To do so, simply pass the fully qualified names of the Java interfaces to the Group constructor. For example, if you've defined Java annotation interfaces with fully qualified names, com.mycompany.groups.SlowTest and com.mycompany.groups.DBTest, then you could create matching groups for FunSuites like this:

 import org.scalatest.Group

 object SlowTest extends Group("com.mycompany.groups.SlowTest")
 object DBTest extends Group("com.mycompany.groups.DBTest")
 

Given these definitions, you could place FunSuite tests into groups like this:

 import org.scalatest.FunSuite

 class MySuite extends FunSuite {

   test("addition", SlowTest) {
     val sum = 1 + 1
     assert(sum === 2)
     assert(sum + 2 === 4)
   }

   test("subtraction", SlowTest, DBTest) {
     val diff = 4 - 1
     assert(diff === 3)
     assert(diff - 2 === 1)
   }
 }
 

This code places both tests, "addition" and "subtraction," into the com.mycompany.groups.SlowTest group, and test "subtraction" into the com.mycompany.groups.DBTest group.

The primary execute method takes two Set[String]s called groupsToInclude and groupsToExclude. If groupsToInclude is empty, all tests will be executed except those those belonging to groups listed in the groupsToExclude Set. If groupsToInclude is non-empty, only tests belonging to groups mentioned in groupsToInclude, and not mentioned in groupsToExclude, will be executed.

Ignored tests

To support the common use case of “temporarily” disabling a test, with the good intention of resurrecting the test at a later time, FunSuite provides registration methods that start with ignore instead of test. For example, to temporarily disable the test named addition, just change “test” into “ignore,” like this:

 import org.scalatest.FunSuite

 class MySuite extends FunSuite {

   ignore("addition") {
     val sum = 1 + 1
     assert(sum === 2)
     assert(sum + 2 === 4)
   }

   test("subtraction") {
     val diff = 4 - 1
     assert(diff === 3)
     assert(diff - 2 === 1)
   }
 }
 

If you run this version of MySuite with:

 scala> (new MySuite).execute()
 

It will run only subtraction and report that addition was ignored:

 Test Ignored - MySuite: addition
 Test Starting - MySuite: subtraction
 Test Succeeded - MySuite: subtraction
 

As with org.scalatest.Suite, the ignore feature is implemented as a group. The execute method that takes no parameters adds org.scalatest.Ignore to the groupsToExclude Set it passes to the primary execute method, as does Runner. The only difference between org.scalatest.Ignore and the groups you may define and exclude is that ScalaTest reports ignored tests to the Reporter. The reason ScalaTest reports ignored tests is as a feeble attempt to encourage ignored tests to be eventually fixed and added back into the active suite of tests.

Informers

One of the parameters to the primary execute method is a Reporter, which will collect and report information about the running suite of tests. Information about suites and tests that were run, whether tests succeeded or failed, and tests that were ignored will be passed to the Reporter as the suite runs. Most often the reporting done by default by FunSuite's methods will be sufficient, but occasionally you may wish to provide custom information to the Reporter from a test. For this purpose, an Informer that will forward information to the current Reporter is provided via the info parameterless method. You can pass the extra information to the Informer via one of its apply methods. The Informer will then pass the information to the Reporter's infoProvided method. Here's an example:

 import org.scalatest.FunSuite

 class MySuite extends FunSuite {

   test("addition") {
     val sum = 1 + 1
     assert(sum === 2)
     assert(sum + 2 === 4)
     info("Addition seems to work")
   }
 }
 
If you run this Suite from the interpreter, you will see the following message included in the printed report:
 Test Starting - MySuite: addition
 Info Provided - MySuite.addition: Addition seems to work
 Test Succeeded - MySuite: addition
 
Author
Bill Venners
Direct Known Subclasses:
FunSuite, PropSuite

Method Summary
override def execute (testName : scala.Option[java.lang.String], reporter : Reporter, stopper : Stopper, groupsToInclude : scala.collection.immutable.Set[java.lang.String], groupsToExclude : scala.collection.immutable.Set[java.lang.String], goodies : scala.collection.immutable.Map[java.lang.String, Any], distributor : scala.Option[Distributor]) : Unit
Execute this Suite.
override def groups : scala.collection.immutable.Map[java.lang.String, scala.collection.immutable.Set[java.lang.String]]
A Map whose keys are String group names to which tests in this FunSuite belong, and values the Set of test names that belong to each group. If this FunSuite contains no groups, this method returns an empty Map.
protected def ignore (testName : java.lang.String, testGroups : Group*)(f : => Unit) : Unit
Register a test to ignore, which has the specified name, optional groups, and function value that takes no arguments. This method will register the test for later ignoring via an invocation of one of the execute methods. This method exists to make it easy to ignore an existing test method by changing the call to test to ignore without deleting or commenting out the actual test code. The test will not be executed, but a report will be sent that indicates the test was ignored. The passed test name must not have been registered previously on this FunSuite instance.
implicit def info : Informer
Returns an Informer that during test execution will forward strings (or reports) passed to its apply method to the current reporter. If invoked inside a test function, it will forward the information to the current reporter immediately. If invoked outside a test function, but in the primary constructor, it will register the info for forwarding later during test execution. If invoked at any other time, it will throw an exception.
protected override def runTest (testName : java.lang.String, reporter : Reporter, stopper : Stopper, goodies : scala.collection.immutable.Map[java.lang.String, Any]) : Unit
Run a test. This trait's implementation runs the test registered with the name specified by testName.
protected override def runTests (testName : scala.Option[java.lang.String], reporter : Reporter, stopper : Stopper, groupsToInclude : scala.collection.immutable.Set[java.lang.String], groupsToExclude : scala.collection.immutable.Set[java.lang.String], goodies : scala.collection.immutable.Map[java.lang.String, Any]) : Unit
protected def test (testName : java.lang.String, testGroups : Group*)(f : => Unit) : Unit
Register a test with the specified name, optional groups, and function value that takes no arguments. This method will register the test for later execution via an invocation of one of the execute methods. The passed test name must not have been registered previously on this FunSuite instance.
override def testNames : scala.collection.immutable.Set[java.lang.String]
An immutable Set of test names. If this FunSuite contains no tests, this method returns an empty Set.
Methods inherited from Suite
nestedSuites, execute, execute, runNestedSuites, suiteName, expectedTestCount
Methods inherited from Assertions
assert, assert, assert, assert, convertToEqualizer, intercept, intercept, intercept, expect, expect, fail, fail, fail, fail
Methods inherited from AnyRef
getClass, hashCode, equals, clone, toString, notify, notifyAll, wait, wait, wait, finalize, ==, !=, eq, ne, synchronized
Methods inherited from Any
==, !=, isInstanceOf, asInstanceOf
Method Details
implicit def info : Informer
Returns an Informer that during test execution will forward strings (or reports) passed to its apply method to the current reporter. If invoked inside a test function, it will forward the information to the current reporter immediately. If invoked outside a test function, but in the primary constructor, it will register the info for forwarding later during test execution. If invoked at any other time, it will throw an exception.

protected def test(testName : java.lang.String, testGroups : Group*)(f : => Unit) : Unit
Register a test with the specified name, optional groups, and function value that takes no arguments. This method will register the test for later execution via an invocation of one of the execute methods. The passed test name must not have been registered previously on this FunSuite instance.
Throws
TestFailedException - if testName had been registered previously

protected def ignore(testName : java.lang.String, testGroups : Group*)(f : => Unit) : Unit
Register a test to ignore, which has the specified name, optional groups, and function value that takes no arguments. This method will register the test for later ignoring via an invocation of one of the execute methods. This method exists to make it easy to ignore an existing test method by changing the call to test to ignore without deleting or commenting out the actual test code. The test will not be executed, but a report will be sent that indicates the test was ignored. The passed test name must not have been registered previously on this FunSuite instance.
Throws
TestFailedException - if testName had been registered previously

override def testNames : scala.collection.immutable.Set[java.lang.String]
An immutable Set of test names. If this FunSuite contains no tests, this method returns an empty Set.

This trait's implementation of this method will return a set that contains the names of all registered tests. The set's iterator will return those names in the order in which the tests were registered.

Overrides
Suite.testNames

protected override def runTest(testName : java.lang.String, reporter : Reporter, stopper : Stopper, goodies : scala.collection.immutable.Map[java.lang.String, Any]) : Unit
Run a test. This trait's implementation runs the test registered with the name specified by testName.
Parameters
testName - the name of one test to execute.
reporter - the Reporter to which results will be reported
stopper - the Stopper that will be consulted to determine whether to stop execution early.
goodies - a Map of properties that can be used by the executing Suite of tests.
Throws
NullPointerException - if any of testName, reporter, stopper, or goodies is null.
Overrides
Suite.runTest

override def groups : scala.collection.immutable.Map[java.lang.String, scala.collection.immutable.Set[java.lang.String]]
A Map whose keys are String group names to which tests in this FunSuite belong, and values the Set of test names that belong to each group. If this FunSuite contains no groups, this method returns an empty Map.

This trait's implementation returns groups that were passed as strings contained in Group objects passed to methods test and ignore.

Overrides
Suite.groups

protected override def runTests(testName : scala.Option[java.lang.String], reporter : Reporter, stopper : Stopper, groupsToInclude : scala.collection.immutable.Set[java.lang.String], groupsToExclude : scala.collection.immutable.Set[java.lang.String], goodies : scala.collection.immutable.Map[java.lang.String, Any]) : Unit

Run zero to many of this Suite's tests.

This method takes a testName parameter that optionally specifies a test to invoke. If testName is Some, this trait's implementation of this method invokes runTest on this object, passing in:

  • testName - the String value of the testName Option passed to this method
  • reporter - the Reporter passed to this method, or one that wraps and delegates to it
  • stopper - the Stopper passed to this method, or one that wraps and delegates to it
  • goodies - the goodies Map passed to this method, or one that wraps and delegates to it

This method takes a Set of group names that should be included (groupsToInclude), and a Set that should be excluded (groupsToExclude), when deciding which of this Suite's tests to execute. If groupsToInclude is empty, all tests will be executed except those those belonging to groups listed in the groupsToExclude Set. If groupsToInclude is non-empty, only tests belonging to groups mentioned in groupsToInclude, and not mentioned in groupsToExclude will be executed. However, if testName is Some, groupsToInclude and groupsToExclude are essentially ignored. Only if testName is None will groupsToInclude and groupsToExclude be consulted to determine which of the tests named in the testNames Set should be run. This trait's implementation behaves this way, and it is part of the general contract of this method, so all overridden forms of this method should behave this way as well. For more information on trait groups, see the main documentation for this trait.

If testName is None, this trait's implementation of this method invokes testNames on this Suite to get a Set of names of tests to potentially execute. (A testNames value of None essentially acts as a wildcard that means all tests in this Suite that are selected by groupsToInclude and groupsToExclude should be executed.) For each test in the testName Set, in the order they appear in the iterator obtained by invoking the elements method on the Set, this trait's implementation of this method checks whether the test should be run based on the groupsToInclude and groupsToExclude Sets. If so, this implementation invokes runTest, passing in:

  • testName - the String name of the test to run (which will be one of the names in the testNames Set)
  • reporter - the Reporter passed to this method, or one that wraps and delegates to it
  • stopper - the Stopper passed to this method, or one that wraps and delegates to it
  • goodies - the goodies Map passed to this method, or one that wraps and delegates to it
Parameters
testName - an optional name of one test to execute. If None, all relevant tests should be executed. I.e., None acts like a wildcard that means execute all relevant tests in this Suite.
reporter - the Reporter to which results will be reported
stopper - the Stopper that will be consulted to determine whether to stop execution early.
groupsToInclude - a Set of String group names to include in the execution of this Suite
groupsToExclude - a Set of String group names to exclude in the execution of this Suite
goodies - a Map of key-value pairs that can be used by the executing Suite of tests.
Throws
NullPointerException - if any of testName, reporter, stopper, groupsToInclude, groupsToExclude, or goodies is null. This trait's implementation of this method executes tests in the manner described in detail in the following paragraphs, but subclasses may override the method to provide different behavior. The most common reason to override this method is to set up and, if also necessary, to clean up a test fixture used by all the methods of this Suite.
Overrides
Suite.runTests

override def execute(testName : scala.Option[java.lang.String], reporter : Reporter, stopper : Stopper, groupsToInclude : scala.collection.immutable.Set[java.lang.String], groupsToExclude : scala.collection.immutable.Set[java.lang.String], goodies : scala.collection.immutable.Map[java.lang.String, Any], distributor : scala.Option[Distributor]) : Unit
Execute this Suite.

If testName is None, this trait's implementation of this method calls these two methods on this object in this order:

  1. runNestedSuites(wrappedReporter, stopper, groupsToInclude, groupsToExclude, goodies, distributor)
  2. runTests(testName, wrappedReporter, stopper, groupsToInclude, groupsToExclude, goodies)

If testName is Some, then this trait's implementation of this method calls runTests, but does not call runNestedSuites.

Parameters
testName - an optional name of one test to execute. If None, all relevant tests should be executed. I.e., None acts like a wildcard that means execute all relevant tests in this Suite.
reporter - the Reporter to which results will be reported
stopper - the Stopper that will be consulted to determine whether to stop execution early.
groupsToInclude - a Set of String group names to include in the execution of this Suite
groupsToExclude - a Set of String group names to exclude in the execution of this Suite
goodies - a Map of key-value pairs that can be used by the executing Suite of tests.
distributor - an optional Distributor, into which to put nested Suites to be executed by another entity, such as concurrently by a pool of threads. If None, nested Suites will be executed sequentially.
Throws
NullPointerException - if any passed parameter is null.
Overrides
Suite.execute


Copyright (C) 2001-2009 Artima, Inc. All rights reserved.