Grouping Assertions with and or with

Strikt provides the and and with functions, and several varieties of with*, that are used to add a block of assertions to a chain. This is useful in a couple of scenarios.

Grouping assertions after a null or type check

It's frequently useful to be able to perform a block of assertions after narrowing the subject type. For example, if the declared type of an assertion subject is nullable it can be awkward to apply a block of assertions directly with expectThat as every individual assertion in the block needs to deal with the nullable type.

The same is true when the subject type is overly broad, and you need to narrow the type with isA<T> in order to use assertion functions that are specific to the runtime type.

The and method is helpful in these scenarios. For example:

expectThat(subject)
  .isNotNull()
  .and {
    // perform other assertions on a known non-null subject
  }

The type after expectThat is Assertion.Builder<T?> (assuming subject has a nullable declared type) but the receiver of and is Assertion.Builder<T> as isNotNull has narrowed the subject type.

Making assertions on sub-trees of a subject

Another use for and is to create a branch of assertions that apply to a sub-tree of the subject. For example, if testing a complex value type with nested properties:

expectThat(person)
  .and {
    get { name }.isEqualTo("David")
  }
  .and {
    get { birthDate.year }.isEqualTo(1947)
  }

The with function gives you another option for doing this:

expectThat(person)
  .with(Person::name) {
    isEqualTo("David")
  }
  .with({ birthDate.year }) {
    isEqualTo(1947)
  }

Of course, it may be better to structure the same assertion with separate assertions. This is a lot more readable:

expect {
  that(person.name).isEqualTo("David")
  that(person.birthDate.year).isEqualTo(1947)
}

Testing properties of a collection can be done similarly:

expectThat(albums)
  .hasSize(26)
  .and { first().get { name }.isEqualTo("David Bowie") }
  .and { last().get { name }.isEqualTo("Blackstar") }

with* extension functions

Strikt provides some variants of with that are also useful in these kinds of tests. These include:

  • for Iterable subjects:
    • withElementAt(index, lambda)
    • withFirst(lambda)
    • withLast(lambda)
    • withFirst(predicate, lambda)
  • for Map subjects:
    • withValue(key, lambda)
  • for CapturingSlot subjects:
    • withCaptured(lamda)

For example, the previous assertions could also be written as:

expectThat(albums)
  .hasSize(26)
  .withFirst {
    get { name }.isEqualTo("David Bowie")
  }
  .withLast {
    get { name }.isEqualTo("Blackstar")
  }