Stateful tests

Not all tests can be implemented without setting the scene, still many test frameworks only focus on a expressive way to assert expectations. For Intent state management is front and center.

Lets go straight to the code:

class StatefulTest extends TestSuite with State[Cart]:
  "an empty cart" using Cart() to :
    "with two items" using (_.add(CartItem("beach-chair", 2))) to :
      "and another three items" using (_.add(CartItem("sunscreen", 3))) to :
        "contains 5 items" in :
          cart => expect(cart.totalQuantity).toEqual(5)

case class CartItem(artNo: String, qty: Int)

case class Cart(items: Seq[CartItem] = Seq.empty):
  def add(item: CartItem): Cart = copy(items = items :+ item)
  def totalQuantity = items.map(_.qty).sum

A test suite that needs state must implement State[T], where T is the type carrying the state you need. There are no requirements on the type T or its signature, you are free to use whatever type you want. We prefer to use case class as they are immutable, but any type will do.

The root state gets created in the "root context" and is passed downstream to any child context. Each child context has the possiblity to transform the state before it is passed to the actual test as a parameter.

"check the stuff" in :
  state => expect(state.stuff).toHaveLength(2)

A suite must either be stateless or stateful. There is no support in writing a test that does not take a state when you derive from State and vice versa. While not the same, it resembles how Scala separate a class and an object.

There are a few conventions or recommendations on how to use state:

  • Put the state implementation below the test
  • Prefer to call methods on the state object over doing it in the test itself
  • Keep state focused and create a new suite ands state class when needed (cost is low)