Clairvoyance is an extension to Specs2, a Scala test library. Clairvoyance provides a few extensions to capture what is happening in your tests, and then produce business and tester friendly documentation.
It's a pastiche of Yatspec, a Java testing library written by my colleague Dan Bodart. It addresses the deficiencies we experienced with Fit and Concordion.
class LoggingExample extends ClairvoyantSpec {
"The coordinator" should {
"invoke the Doomsday Device on the 21st of December 2012" in new context {
givenTheDateIs("21/12/2012")
whenTheCoordinatorRuns
theDoomsdayDevice should beUnleashed
}
}
trait context extends ClairvoyantContext {
// test set up and fixtures
}
}
It breaks down like this:
- Create a Spec which extends
ClairvoyantSpec
- Write the spec in the mutable spec style (for various historical reasons)
- Create a context which extends
ClairvoyantContext
InterestingGivens
can be added with statements such asinterestingGivens += ("Current date" -> "21/12/2012")
- The Scala code within the spec method is interpreted into a text specification, to encourage readability.
The full source to this example is here.
Here is the output of this spec.
Add this to your SBT build:
// for Scala 2.10 and above:
libraryDependencies += "com.github.rhyskeepence" %% "clairvoyance-specs2" % <latest-version>
// for scala 2.9.x:
libraryDependencies += "com.github.rhyskeepence" %% "clairvoyance" % "27"
resolvers += "releases" at "http://oss.sonatype.org/content/repositories/releases"
Or in Maven:
<dependency>
<groupId>com.github.rhyskeepence</groupId>
<artifactId>clairvoyance-specs2_2.10</artifactId>
<version>${latest-version}</version>
<scope>test</scope>
</dependency>
...
<repository>
<id>sonatype-releases</id>
<name>sonatype releases</name>
<snapshots>
<enabled>false</enabled>
</snapshots>
<url>http://oss.sonatype.org/content/repositories/releases</url>
</repository>
These are inputs into your test, which may not be specified in the spec, but should be logged to the output:
interestingGivens += ("Current date" -> "21/12/2012")
or
("Current date" -> "21/12/2012").isInteresting
These are the inputs or outputs to your system, which may not be practical to assert upon, but should be logged.
Perhaps you are using a stub rather than communicating with a third party in your spec:
class StubGizmometer extends Gizmometer {
}
To capture inputs and outputs, just add the ProducesCapturedInputsAndOutputs
trait and call captureValue
:
class StubGizmometer extends Gizmometer with ProducesCapturedInputsAndOutputs {
def scan(brain: Brain) {
captureValue("Brain" -> brain)
}
}
and in your context, register the stub so that clairvoyant knows about it:
trait context extends ClairvoyantContext {
val gizmometer = new StubGizmometer
override def capturedInputsAndOutputs = Seq(gizmometer)
}
The value that is captured can be of any type. XML is formatted nicely, and HTML can be inlined into the output by capturing a value in a Html type:
captureValue("Inline HTML" -> Html(<em>Hello!</em>))
All other values are displayed as their String representation, unless a custom renderer has been defined.
When you capture a value or an interesting given, it will be rendered to the screen. XML and Strings are formatted nicely by default, but you may wish to capture your own domain objects and have them presented in readable format.
A full example is here: [clairvoyance/specs2/examples/CustomRenderingExample.scala] (https://github.com/rhyskeepence/clairvoyance/blob/master/specs2/src/test/scala/clairvoyance/specs2/examples/CustomRenderingExample.scala)
The juicy bits are shown below:
class CustomRenderingExample extends ClairvoyantSpec with CustomRendering {
def customRendering = {
case Brain(iq) => "a Brain with an IQ of %d".format(iq)
}
}
customRendering
is a partial function, which will be run before the default rendering.
And behold, custom rendering of Brains:
If your spec describes interactions between many systems, it can be nice to generate a sequence diagram automatically
from CapturedInputsAndOutputs. Just add the SequenceDiagram
trait to your context, ie:
trait context extends ClairvoyantContext with SequenceDiagram {
override def capturedInputsAndOutputs = Seq(system_x, system_y)
}
The name of the captured values should be in the following formats in order to appear on the diagram:
captureValue("SOMETHING from X to Y" -> ...)
or
captureValue("SOMETHING from X" -> ...)
or
captureValue("SOMETHING to Y" -> ...)
In the last two cases, the default actor will be used, which can be set using this statement in the context:
override def defaultSequenceDiagramActor = "Name of my component"
An example can be found here, which produces the following output:
Alternatively, a graph can be produced:
trait context extends ClairvoyantContext with Graph
Markdown is supported in specification descriptions, to whet your appetite see this example.
- Scenario tables
- Browse existing issues