First Example

Let’s start with a simple, pure test suite, using the most basic harness type provided. We’ll be using testz-core and testz-stdlib, but the actual test code will only use testz-core.

Obligatory imports:

import testz.{Harness, PureHarness, assert}

And here’s the meat.

final class MathTests {
  def tests[T](harness: Harness[T]): T = {
    import harness._
    section(
      test("say 1 + 1 == 2") { () =>
        assert(1 + 1 == 2)
      },
      test("say 2 * 2 == 4") { () =>
        assert(2 * 2 == 4)
      }
    )
  }
}

To run this test suite, pass it the Harness it wants. First to do that we have to construct the Harness, using PureHarness.makeFromPrinter; makeFromPrinter expects an impure function which can be used to print test results ((List[String], Result) => Unit), and that’s what we give it.

It will return a T, in this case PureHarness.Uses[Unit], which is (Unit, List[String]) => TestOutput. TestOutput has a method print() which you can use to see the output.

val harness: Harness[PureHarness.Uses[Unit]] =
  PureHarness.makeFromPrinter((result, name) =>
    println(s"${name.reverse.mkString("[\"", "\"->\"", "\"]:")} $result")
  )
// harness: testz.Harness[testz.PureHarness.Uses[Unit]] = testz.ResourceHarness$$anon$1@50e3ee5c

(new MathTests()).tests(harness)((), Nil).print()
// ["say 1 + 1 == 2"]: Succeed
// ["say 2 * 2 == 4"]: Succeed

For an example of how this is practically used, see the tests for testz itself here.

I went through a lot there; let’s dissect that.

import testz.{Harness, PureHarness, assert}

Here I import Harness (the simplest type of test harnesses), PureHarness (an actual test harness implementation), and the assert function.

PureHarness is from testz-stdlib, and the others are from testz-core.

Conventionally, test suites are written with an abstract method that takes some type of harness as a parameter and returns a value it can only obtain from the harness.

Note: Test registration, intuitively, is not side effectful because of this. You can’t accidentally register a test from inside an assertion; the only tests you register are a single value.

I also import assert from testz; assertions are just values in testz. assert returns either Success() if its argument is True, or Failure.noMessage otherwise (a failure, with no message).

final class MathTests {

We’re making a test suite class called MathTests. We’ll be defining our tests inside.

Note: This is intentionally a class and not an object. testz encourages you not to use singleton objects as test suites. Using objects will prevent fields of test suites from being garbage collected during the run.

def tests[T](harness: Harness[T]): T = {

Here we define a method in which we will define our tests.

Note: The type returned by tests is abstract, and in its parameters only appears in Harness[T]. This is because the only way to create a T is to define some tests with that harness. You can write your own suite type and give it any signature you want; you don’t even need to use testz’s Harness test harness types.

import harness._

We also import all of the methods from harness; we’ll use section and test, the test registration primitives in Harness which are included in all harness types.

section(

Declaring a test section. Takes varargs parameters, of type T. Returns a T. The only way other than section to get a T (when it’s abstract) is test.

You can also include a name with the section by calling namedSection, which accepts an additional parameter (name: String).

test("say 1 + 1 == 2") { () =>

And here’s a test definition, using test.apply. The first parameter is the name of the test. The parameter in the second (curried) parameter list is a function () => TestResult.

Note: () => is actually needed to avoid computing test registrations and tests at the same time. testz doesn’t use by-names. If you do want a Harness with by-names or with test registrations adjacent to tests, this is something you can alter.

assert(1 + 1 == 2)

Here’s the assertion inside. It’ll give you a Result which is Fail() if the two arguments aren’t equal using ===, and otherwise Succeed().

The next assertion, all together:

test("say 2 * 2 == 4") { () =>
  assert(2 * 2 == 4)
}

And we’re done.