[](https://search.maven.org/artifact/ch.tutteli.atrium/atrium-fluent-en_GB)
[](https://joinup.ec.europa.eu/collection/eupl/eupl-text-11-12 "License")
[](https://kotlinlang.slack.com/messages/atrium "See invitation link under section FAQ")
[](https://github.com/robstoll/atrium/actions?query=workflow%3AUbuntu+branch%3Amaster)
[](https://github.com/robstoll/atrium/actions?query=workflow%3AWindows+branch%3Amaster)
[](https://codecov.io/github/robstoll/atrium/branch/master)
[](https://github.com/robstoll/atrium/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 "Ask in slack for help")
#
Atrium is an open-source multiplatform assertion library for Kotlin with support for JVM, JS and Android.
It is designed to support multiple [APIs](#api-styles), different error reporting styles and [Internationalization](#internationalization-1) (i18n).
The project was inspired by AssertJ at first but moved on and provides now more
flexibility, features and hints to its users (so to you 😉).
Atrium is designed to be extensible as well as configurable
and allows you to extend it with your own assertion functions, customise reporting
or even replace core components with your own implementation in an easy way.
Atrium currently provides two [API Styles](#api-styles):
pure fluent and infix where both of them have their design focus on usability in conjunction with code completion functionality provided by your IDE.
See [Examples](#examples) below to get a feel for how you could benefit from Atrium.
---
❗ You are taking a *sneak peek* at the next version.
Please have a look at the README of the git tag in case you are looking for the documentation of the corresponding version.
For instance, the [README of v0.16.0](https://github.com/robstoll/atrium/tree/v0.16.0/README.md).
---
**Table of Content**
- [Installation](#installation)
- [JVM](#jvm)
- [JS](#js)
- [Android](#android)
- [Common](#common)
- [Examples](#examples)
- [Your First Assertion](#your-first-assertion)
- [Define Single Assertions or Assertion Groups](#define-single-assertions-or-assertion-groups)
- [Expect an Exception](#expect-an-exception)
- [Feature Assertions](#feature-assertions)
- [Property and Method](#property-and-methods)
- [Arbitrary Features](#arbitrary-features)
- [Type Assertions](#type-assertions)
- [Nullable Types](#nullable-types)
- [Collection Assertions](#collection-assertions)
- [Shortcut Functions](#shortcut-functions)
- [Sophisticated Assertion Builders](#sophisticated-assertion-builders)
- [Map Assertions](#map-assertions)
- [Shortcut Functions](#shortcut-functions-1)
- [Sophisticated Assertion Builders](#sophisticated-assertion-builders-1)
- [Others](#others)
- [Path Assertions](#path-assertions)
- [Attaching a Reason](#attaching-a-reason)
- [Data Driven Testing](#data-driven-testing)
- [Further Examples](#further-examples)
- [Sample Projects](#sample-projects)
- [Third-party Extensions](#third-party-extensions)
- [How is Atrium different from other Assertion Libraries](#how-is-atrium-different-from-other-assertion-libraries)
- [Write own Assertion Functions](#write-own-assertion-functions)
- [Boolean based Assertions](#boolean-based-assertions)
- [Compose Functions](#compose-assertion-functions)
- [Enhanced Reporting](#enhanced-reporting)
- [Own Sophisticated Assertion Builders](#own-sophisticated-assertion-builders)
- [Use own Expectation Verb](#use-own-expectation-verb)
- [Use own Components](#use-own-components)
- [Internationalization](#internationalization-1)
- [API Styles](#api-styles)
- [Java Interoperability](#java-interoperability)
- [KDoc - Code Documentation](#kdoc---code-documentation)
- [Known Limitations](#known-limitations)
- [FAQ](#faq)
- [Roadmap](#roadmap)
- [Contributors and contribute](#contributors-and-contribute)
- [Sponsors](#sponsors)
- [License](#license)
# Installation
## JVM
Atrium is linked to [mavenCentral](https://search.maven.org/search?q=g:ch.tutteli.atrium),
[jcenter](https://bintray.com/bintray/jcenter?filterByPkgName=atrium)
but can also be retrieved directly from [bintray](https://bintray.com/robstoll/tutteli-jars/atrium).
*gradle*:
```
buildscript {
ext { atrium_version='0.16.0' }
}
repositories {
mavenCentral()
}
dependencies {
testImplementation "ch.tutteli.atrium:atrium-fluent-en_GB:$atrium_version"
}
```
We have defined a dependency to the bundle `atrium-fluent-en_GB` in the above example
which provides a pure fluent API (in en_GB) for the JVM platform.
Have a look at the [JVM sample projects](https://github.com/robstoll/atrium/tree/master/samples/jvm) for a quick setup, or
[Maven sample project](https://github.com/robstoll/atrium/tree/master/samples/maven) if you prefer Maven to Gradle.
We currently provide the following extensions for the JVM platform:
- kotlin_1_3: assertion functions for Kotlin 1.3 specific types (e.g. for [Result](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/index.html)).
You can enable them as follows:
```
dependencies {
testImplementation "ch.tutteli.atrium:atrium-api-fluent-en_GB-kotlin_1_3:$atrium_version"
}
```
Also take a look at [Third-party Extensions](#third-party-extensions) which might come in handy as well.
click to see how the setup for the infix API looks like
```
buildscript {
ext { atrium_version='0.16.0' }
}
repositories {
mavenCentral()
}
dependencies {
testImplementation "ch.tutteli.atrium:atrium-infix-en_GB:$atrium_version"
}
```
And for the aforementioned extensions:
```
dependencies {
testImplementation "ch.tutteli.atrium:atrium-api-infix-en_GB-kotlin_1_3:$atrium_version"
}
```
*maven*:
Because maven is a bit more verbose than gradle, the example is not listed here but a
[sample maven project](https://github.com/robstoll/atrium/tree/master/samples/maven)
is provided which shows all necessary setup.
That is all, you are all set. Jump to [Examples](#examples) which shows how to use Atrium.
## JS
```
buildscript {
ext { atrium_version='0.16.0' }
}
repositories {
mavenCentral()
}
dependencies {
testImplementation("ch.tutteli.atrium:atrium-fluent-en_GB-js:$atrium_version")
}
```
We have defined a dependency to the bundle `atrium-fluent-en_GB-js` in the above example
which provides a pure fluent API (in en_GB) for the JS platform.
Have a look at the [JS sample projects](https://github.com/robstoll/atrium/tree/master/samples/jvm) for a quick setup.
Otherwise, you need to setup an explicit dependency on `atrium-fluent-en_GB-js` in your test code in order that you can use Atrium.
This is due to the loosely coupled design of Atrium and dead code elimination performed by the Kotlin compiler for JS.
Atrium itself is using mocha as well
(see [build.gradle -> createJsTestTask](https://github.com/robstoll/atrium/tree/master/build.gradle#L290))
and has tests written in JS modules
(see [AdjustStackTest](https://github.com/robstoll/atrium/tree/master/core/atrium-core-js/src/test/kotlin/ch/tutteli/atrium/reporting/erroradjusters/AdjustStackTest.kt))
as well as tests written in common modules (e.g. [SmokeTest](https://github.com/robstoll/atrium/tree/master/bundles/fluent-en_GB/atrium-fluent-en_GB-common/src/test/kotlin/SmokeTest.kt))
which are executed on the JS platform as well
(actually on all platforms -> JVM uses JUnit for this purpose, see
[build.gradle -> useJupiter](https://github.com/robstoll/atrium/tree/master/build.gradle#L342)).
We currently provide the following extensions for the JS platform:
- kotlin_1_3: assertion functions for Kotlin 1.3 specific types (e.g. for [Result](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/index.html)).
You can enable them as follows:
```
dependencies {
testImplementation "ch.tutteli.atrium:atrium-api-fluent-en_GB-kotlin_1_3-js:$atrium_version"
}
```
click to see how the setup for the infix API looks like
```
buildscript {
ext { atrium_version='0.16.0' }
}
repositories {
mavenCentral()
}
dependencies {
testImplementation "ch.tutteli.atrium:atrium-infix-en_GB-js:$atrium_version"
}
```
and for the aforementioned extensions:
```
dependencies {
testImplementation "ch.tutteli.atrium:atrium-api-infix-en_GB-kotlin_1_3-js:$atrium_version"
}
```
That is all, you are all set. Jump to [Examples](#examples) which shows how to use Atrium.
## Android
Starting with 0.12.0 we no longer deliver a dedicated `-android` jar. Instead you can use the same setup as shown in [JVM setup](#jvm).
We start adding one again in case we have Android specific assertion functions.
Also take a look at [Third-party Extensions](#third-party-extensions) which might come in handy as well.
## Common
The setup for using Atrium in a common module of a multiplatform project is basically the same as for the
[JVM setup](#jvm), you only need to suffix the dependency with `-common` in addition.
For instance `atrium-fluent-en_GB-common` instead of `atrium-fluent-en_GB`.
Have a look at [JVM](#jvm), [JS](#js) or [Android](#android) to see how the setup of a specific platform has to be done.
You might want to have a look at the [Multiplatform sample project](https://github.com/robstoll/atrium/tree/master/samples/multiplatform)
as well for a quick setup.
# Examples
We are using the API provided by the bundle module
[atrium-fluent-en_GB](https://github.com/robstoll/atrium/tree/master/bundles/fluent-en_GB/atrium-fluent-en_GB/build.gradle)
in the following examples.
It provides a pure fluent API for the JVM platform.
Have a look at
[apis/differences.md](https://github.com/robstoll/atrium/tree/master/apis/differences.md)
to see how the infix API looks like, how they differ respectively.
## Your First Assertion
We start off with a simple example:
```kotlin
import ch.tutteli.atrium.api.fluent.en_GB.*
import ch.tutteli.atrium.api.verbs.expect
val x = 10
expect(x).toEqual(9)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/FirstExampleSpec.kt#L31) ↓ [Output](#ex-first)
```text
expected that subject: 10 (kotlin.Int <1234789>)
◆ equals: 9 (kotlin.Int <1234789>)
```
The statement can be read as "I expect, x to be nine" where an equality check is used (for an identity check, you have to use `toBeTheSameInstace`).
Since this is false, an `AssertionError` is thrown with a corresponding message as shown in the Output
where `◆ ...` represents a single assertion for the subject (`10` in the above example) of the assertion.
In this sense the report can be read as `I expected that the subject of the assertion, which is 10, equals 9`
-- and needless to say, this assertion is wrong and thus the thrown error.
We are using the bundle [atrium-fluent-en_GB](https://github.com/robstoll/atrium/tree/master/bundles/fluent-en_GB/atrium-fluent-en_GB/build.gradle)
and the predefined expectation verb `expect` in the examples.
Thus, the corresponding `import`s at the beginning of the file in the above example.
We will omit the `import` statements in the remaining examples for brevity.
**You want to run the examples yourself?**
Have a look at the [Installation](#installation) section which explains how to set up a dependency to Atrium.
The next section shows how you can define multiple assertions for the same subject.
## Define Single Assertions or Assertion Groups
```kotlin
// two single assertions, only first evaluated
expect(4 + 6).toBeLessThan(5).toBeGreaterThan(10)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L24) ↓ [Output](#ex-single)
```text
expected that subject: 10 (kotlin.Int <1234789>)
◆ is less than: 5 (kotlin.Int <1234789>)
```
Atrium allows you to chain assertions or in other words
you only need to write the `expect(...)` part once and can make several single assertions for the same subject.
The expression which determines the subject of the assertion (`4 + 6` in the above example) is evaluated only once.
In this sense we could have written it also as follows (which is only the same because `4 + 6` does not have side effects).
```kotlin
expect(4 + 6).toBeLessThan(5)
expect(4 + 6).toBeGreaterThan(10)
```
Correspondingly, the first `expect` statement (which does not hold) throws an `AssertionError`.
In the above example, `toBeLessThan(5)` is already wrong and thus `toBeGreaterThan(10)` was not evaluated at all
and correspondingly not reported.
If you want that both assertions are evaluated together, then use the assertion group syntax as follows:
```kotlin
// assertion group with two assertions, both evaluated
expect(4 + 6) {
toBeLessThan(5)
toBeGreaterThan(10)
}
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L40) ↓ [Output](#ex-group)
```text
expected that subject: 10 (kotlin.Int <1234789>)
◆ is less than: 5 (kotlin.Int <1234789>)
◆ is greater than: 10 (kotlin.Int <1234789>)
```
An assertion group throws an `AssertionError` at the end of its block; hence reports that both assertions do not hold.
The reporting can be read as `I expected that the subject of the assertion, which is 10, is less than 5 and is greater than 10`
You can use `and` as filling element between single assertions and assertion group blocks:
```kotlin
expect(5).toBeGreaterThan(2).and.toBeLessThan(10)
expect(5) {
// ...
} and { // if the previous block fails, then this one is not evaluated
// ...
}
```
## Expect an Exception
```kotlin
expect {
// this block does something but eventually...
throw IllegalArgumentException("name is empty")
}.toThrow()
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L64) ↓ [Output](#ex-toThrow1)
```text
expected that subject: () -> kotlin.Nothing (readme.examples.MostExamplesSpec$1$7$1 <1234789>)
◆ ▶ thrown exception when called: java.lang.IllegalArgumentException
◾ is instance of type: IllegalStateException (java.lang.IllegalStateException)
ℹ Properties of the unexpected IllegalArgumentException
» message: "name is empty" <1234789>
» stacktrace:
⚬ readme.examples.MostExamplesSpec$1$7$1.invoke(MostExamplesSpec.kt:67)
⚬ readme.examples.MostExamplesSpec$1$7$1.invoke(MostExamplesSpec.kt:22)
⚬ readme.examples.MostExamplesSpec$1$7.invoke(MostExamplesSpec.kt:272)
⚬ readme.examples.MostExamplesSpec$1$7.invoke(MostExamplesSpec.kt:22)
```
You can also pass a lambda to `expect` and then use `toThrow` to make the assertion that
invoking the lambda throws a certain exception (`IllegalStateException` in the example above).
As with all narrowing functions, there are two overloads:
- the first is parameterless and turns only the subject into the expected type;
failing to do so cannot include additional information in error reporting though.
- the second expects an `assertionCreator`-lambda in which you can define sub-assertions.
An `assertionCreator`-lambda has always the semantic of an [assertion group block](#define-single-assertions-or-assertion-groups).
It has also the benefit, that Atrium can provide those sub-assertions in error reporting,
showing some additional context in case of a failure.
The following example uses the first overload
```kotlin
expect {
throw IllegalArgumentException()
}.toThrow().message.toStartWith("firstName")
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L71) ↓ [Output](#ex-toThrow2)
```text
expected that subject: () -> kotlin.Nothing (readme.examples.MostExamplesSpec$1$8$1 <1234789>)
◆ ▶ thrown exception when called: java.lang.IllegalArgumentException
◾ ▶ message: null
◾ is instance of type: String (kotlin.String) -- Class: java.lang.String
```
And this one uses the second overload; notice the difference in reporting.
```kotlin
expect {
throw IllegalArgumentException()
}.toThrow {
message { toStartWith("firstName") }
}
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L77) ↓ [Output](#ex-toThrow3)
```text
expected that subject: () -> kotlin.Nothing (readme.examples.MostExamplesSpec$1$9$1 <1234789>)
◆ ▶ thrown exception when called: java.lang.IllegalArgumentException
◾ ▶ message: null
◾ is instance of type: String (kotlin.String) -- Class: java.lang.String
» starts with: "firstName" <1234789>
```
As side notice, `message` is a shortcut for `feature(Throwable::message).notToBeNull`,
which creates a feature assertion (see next section) about `Throwable::message`.
There is also the counterpart to `toThrow` named `notToThrow`:
```kotlin
expect {
// this block does something but eventually...
throw IllegalArgumentException("name is empty", RuntimeException("a cause"))
}.notToThrow()
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L85) ↓ [Output](#ex-notToThrow)
```text
expected that subject: () -> kotlin.Nothing (readme.examples.MostExamplesSpec$1$10$1 <1234789>)
◆ ▶ invoke(): ❗❗ threw java.lang.IllegalArgumentException
ℹ Properties of the unexpected IllegalArgumentException
» message: "name is empty" <1234789>
» stacktrace:
⚬ readme.examples.MostExamplesSpec$1$10$1.invoke(MostExamplesSpec.kt:88)
⚬ readme.examples.MostExamplesSpec$1$10$1.invoke(MostExamplesSpec.kt:22)
⚬ readme.examples.MostExamplesSpec$1$10.invoke(MostExamplesSpec.kt:89)
⚬ readme.examples.MostExamplesSpec$1$10.invoke(MostExamplesSpec.kt:22)
» cause: java.lang.RuntimeException
» message: "a cause" <1234789>
» stacktrace:
⚬ readme.examples.MostExamplesSpec$1$10$1.invoke(MostExamplesSpec.kt:88)
```
Notice that stacks are filtered so that you only see what is of interest.
Filtering can be configured via [`ReporterBuilder`](#reporterbuilder) by choosing an appropriate
[AtriumErrorAdjuster](https://docs.atriumlib.org/latest#/doc/ch.tutteli.atrium.reporting/-atrium-error-adjuster/index.html).
Stack frames of Atrium and of test runners (Spek, Kotlintest and JUnit for JVM, mocha and jasmine for JS) are excluded per default.
[Create a Feature Request](https://github.com/robstoll/atrium/issues/new?template=feature_request.md&title=[Feature])
in case you use a different runner, we can add yours to the list as well.
## Feature Assertions
Many times you are only interested in certain features of the subject and want to make assertions about them.
There are different use cases for feature assertions.
We will start of with properties and method calls and go on with more complicated scenarios.
### Property and Methods
We are using the `data class Person` in the following examples:
```kotlin
data class Person(val firstName: String, val lastName: String, val isStudent: Boolean) {
fun fullName() = "$firstName $lastName"
fun nickname(includeLastName: Boolean) = when (includeLastName) {
false -> "Mr. $firstName"
true -> "$firstName aka. $lastName"
}
}
val myPerson = Person("Robert", "Stoll", false)
```
The simplest way of defining assertions for a property of an instance or for the return value of a method call is by
using the extension method `its`.
```kotlin
expect(myPerson)
.its({ isStudent }) { toEqual(true) } // fails, subject still Person afterwards
.its { fullName() } // not evaluated anymore, subject String afterwards
.toStartWith("rob") // not evaluated anymore
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/FeatureExtractorSpec.kt#L41) ↓ [Output](#ex-its-single)
```text
expected that subject: Person(firstName=Robert, lastName=Stoll, isStudent=false) (readme.examples.FeatureExtractorSpec$1$Person <1234789>)
◆ ▶ its.definedIn(FeatureExtractorSpec.kt:43): false
◾ equals: true
```
In the above example we created two assertions, one for the property `isStudent` of `myPerson`
and a second one for the return value of calling `fullName()` on `myPerson`.
A feature assertion is indicated as follows in reporting:
It starts with a `▶` followed by the feature's description and its actual value.
So the above output can be read as
> I expected that the subject of the assertion, which is actually the Person(...),
> respectively its property which was defined in FeatureExtractorSpec.kt on line 43,
> which is actually `false`, equals `true`.
The second feature is not shown in reporting as the first already failed and we have chosen to use [single assertions](#define-single-assertions-or-assertion-groups)
which have fail-fast semantic.
Feature assertions follow the common pattern of having two overloads:
- the first expects only the extractor-lambda.
This overload narrows the subject to the feature,
meaning a subsequent call in the fluent chain is about the feature and not the previous subject.
- the second expects an `assertionCreator`-lambda in addition, in which you can define sub-assertions for the feature.
An `assertionCreator`-lambda has always the semantic of an [assertion group block](#define-single-assertions-or-assertion-groups) or in other words, not-fail fast.
It has also the benefit, that Atrium can provide those sub-assertions in error reporting,
Moreover, the subject stays the same so that subsequent calls are still about the same subject.
```kotlin
expect(myPerson) { // forms an assertion group block
its({ firstName }) { // forms an assertion group block
toStartWith("Pe") // fails
toEndWith("er") // is evaluated nonetheless
} // fails as a whole
// still evaluated, as it is in outer assertion group block
its { lastName }.toEqual("Dummy")
}
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/FeatureExtractorSpec.kt#L49) ↓ [Output](#ex-its-group)
```text
expected that subject: Person(firstName=Robert, lastName=Stoll, isStudent=false) (readme.examples.FeatureExtractorSpec$1$Person <1234789>)
◆ ▶ its.definedIn(FeatureExtractorSpec.kt:52): "Robert" <1234789>
◾ starts with: "Pe" <1234789>
◾ ends with: "er" <1234789>
◆ ▶ its.definedIn(FeatureExtractorSpec.kt:58): "Stoll" <1234789>
◾ equals: "Dummy" <1234789>
```
One drawback of `its` (which we plan to improve but most likely not before we drop support for Kotlin < 1.5) is that reading the resulting
feature description does not immediately tell us what feature we extracted.
That is where the `feature` function comes into play. It is based on reflection and uses the name of the feature
as description. Following the first example rewritten to `feature`.
```kotlin
expect(myPerson)
.feature({ f(it::isStudent) }) { toEqual(true) } // fails, subject still Person afterwards
.feature { f(it::fullName) } // not evaluated anymore, subject String afterwards
.toStartWith("rob") // not evaluated anymore
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/FeatureExtractorSpec.kt#L63) ↓ [Output](#ex-property-methods-single)
```text
expected that subject: Person(firstName=Robert, lastName=Stoll, isStudent=false) (readme.examples.FeatureExtractorSpec$1$Person <1234789>)
◆ ▶ isStudent: false
◾ equals: true
```
The report reads much nicer now:
> I expected that the subject of the assertion,
> which is actually the Person(...), respectively its property `isStudent`,
> which is actually `false`, equals `true`
The drawback of `feature` compared to `its` is its syntax. Certainly, one has to get used to it first. Another is that
you might run into [Ambiguity Problems](#ambiguity-problems) due to Kotlin bugs.
`feature` has several overloads, we are looking at the one expecting a lambda in which you have to provide a `MetaFeature`.
Creating a `MetaFeature` is done via the function `f` by passing in a
[bounded reference](https://kotlinlang.org/docs/reference/reflection.html#bound-function-and-property-references-since-11)
of the corresponding property or method (including arguments if required).
`it` within the `MetaFeature`-provider-lambda refers to the subject of the assertion (`myPerson` in the above example).
Also `feature` follows the common pattern of having two overloads where the second expects an `assertionCreator`-lambda.
Following the second example rewritten from `its` to `feature`:
```kotlin
expect(myPerson) { // forms an assertion group block
feature({ f(it::firstName) }) { // forms an assertion group block
toStartWith("Pe") // fails
toEndWith("er") // is evaluated nonetheless
} // fails as a whole
// still evaluated, as it is in outer assertion group block
feature { f(it::lastName) }.toEqual("Dummy")
}
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/FeatureExtractorSpec.kt#L71) ↓ [Output](#ex-property-methods-group)
```text
expected that subject: Person(firstName=Robert, lastName=Stoll, isStudent=false) (readme.examples.FeatureExtractorSpec$1$Person <1234789>)
◆ ▶ firstName: "Robert" <1234789>
◾ starts with: "Pe" <1234789>
◾ ends with: "er" <1234789>
◆ ▶ lastName: "Stoll" <1234789>
◾ equals: "Dummy" <1234789>
```
Atrium provides several shortcuts for commonly used properties so that you can use them instead of writing `its { ... }` / `feature(...)` all the time.
For instance, `message` for Throwable (see [Expect an Exception](#expect-an-exception)), `first` and `second` for `Pair` and others.
Please [open a feature request](https://github.com/robstoll/atrium/issues/new?template=feature_request.md&title=[Feature]) in case you miss a shortcut.
💬 <- _this icon signifies answers/input for advanced users, you might want to skip them if you are new to Atrium._
💬 Wrap each property into an assertion function?
You might be asking yourself whether it is better to [write an own assertion function](#write-own-assertion-functions) or use `feature`.
The only drawback of using an existing property is that a few more key strokes are required compared to
[writing an own assertion function](#write-own-assertion-functions) once and then reuse it (as we did with `message`).
Yet, we do not recommend to write an own assertion function for every single property.
We think it makes sense to add one if you use it a lot and (preferably) it is a stable API.
Why not always? Because one quickly forgets to rename the assertion function
if the property as such is renamed (e.g., as part of an IDE refactoring).
As you can see, you would need to keep the property name and the name of the assertion function in sync to be meaningful
(otherwise one gets quickly confused or has to remember two names for the same thing).
Writing assertion functions for methods is a different story though, especially due to [overload bugs in Kotlin](https://github.com/robstoll/atrium/wiki/Kotlin-Bugs-and-missing-features).
Also, code completion is not yet as good as it should be when it comes to methods.
Last but not least, in case it is not always safe to call a method (e.g. `List.get` => IndexOutOfBound) then it makes
sense to wrap it into an assertion function and use `_logic.extractFeature` instead.
Last but not least, let us have a look at an example where a method with arguments is used as feature:
```kotlin
expect(myPerson)
.feature { f(it::nickname, false) } // subject narrowed to String
.toEqual("Robert aka. Stoll") // fails
.toStartWith("llotS") // not evaluated anymore
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/FeatureExtractorSpec.kt#L85) ↓ [Output](#ex-methods-args)
```text
expected that subject: Person(firstName=Robert, lastName=Stoll, isStudent=false) (readme.examples.FeatureExtractorSpec$1$Person <1234789>)
◆ ▶ nickname(false): "Mr. Robert" <1234789>
◾ equals: "Robert aka. Stoll" <1234789>
```
`f` supports methods with up to 5 arguments.
Atrium provides shortcuts for commonly used methods, e.g. `List.get`, `Map.getExisting`, `Optional.toBePresent` or `Result.toBeSuccess`
where all of them include some additional checking (index bound, existence of the key within the map etc.)
Please [open a feature request](https://github.com/robstoll/atrium/issues/new?template=feature_request.md&title=[Feature])
in case you miss a shortcut.
💬 Write own feature assertion functions with additional checks.
Atrium provides a feature extractor which allows making feature assertions in a safe way in case the extraction is only valid for certain subjects.
It is inter alia used for [`List.get`](https://github.com/robstoll/atrium/tree/master/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultListAssertions.kt#L13)
### Arbitrary Features
A feature does not necessarily have to be directly related to the subject as properties or method calls do.
Either use `its` the overload of `feature` which expects a feature description in form of a `String` as first argument.
Following an example using `feature`.
```kotlin
data class FamilyMember(val name: String)
data class Family(val members: List)
val myFamily = Family(listOf(FamilyMember("Robert")))
expect(myFamily)
.feature("number of members", { members.size }) { toEqual(1) } // subject still Family afterwards
.feature("first member's name") { members.first().name } // subject narrowed to String
.toEqual("Peter")
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/FeatureExtractorSpec.kt#L102) ↓ [Output](#ex-arbitrary-features)
```text
expected that subject: Family(members=[FamilyMember(name=Robert)]) (readme.examples.FeatureExtractorSpec$1$Family <1234789>)
◆ ▶ first member's name: "Robert" <1234789>
◾ equals: "Peter" <1234789>
```
Also this version of `feature` provides two different kind of overloads:
- the first expects a feature description and a feature-provider-lambda
This overload narrows the subject to the feature,
meaning a subsequent call in the fluent chain is about the feature and not the previous subject.
- the second expects an `assertionCreator`-lambda in addition, in which you can define sub-assertions for the feature.
An `assertionCreator`-lambda has always the semantic of an [assertion group block](#define-single-assertions-or-assertion-groups) or in other words, not-fail fast.
It has also the benefit, that Atrium can provide those sub-assertions in error reporting,
Moreover, the subject stays the same so that subsequent calls are still about the same subject.
As you can see, Atrium provides a generic way to postulate assertions about features.
Yet, if you use such feature assertion often or it gets more complicated,
then it might be worth to [write an own assertion function](#write-own-assertion-functions) where we recommend to
use `feature` over `its`.
### Within Assertion Functions
In case you write an own assertion function, then we discourage two things:
- using `its` because the reporting reads less nice and it is also less efficient than `feature`
- using `feature` with a `MetaFeature`-provider-lambda (as shown in [Property and Methods](#property-and-methods))
Instead, we encourage you to pass a [class references](https://kotlinlang.org/docs/reference/reflection.html#class-references)
to `feature`.
This has the benefit, that we can always show the feature name, also in case a previous feature extraction or subject
transformation failed.
Following an example:
```kotlin
fun > Expect.firstToBeDoneWrong(expected: F) =
feature({ f(it::first) }) { toEqual(expected) }
fun > Expect.firstToBe(expected: F) =
feature(Pair::first) { toEqual(expected) }
expect(listOf(1 to "a", 2 to "b")).get(10) {
firstToBeDoneWrong(1)
firstToBe(1)
}
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/FeatureExtractorSpec.kt#L118) ↓ [Output](#ex-within-assertion-functions)
```text
expected that subject: [(1, a), (2, b)] (java.util.Arrays.ArrayList <1234789>)
◆ ▶ get(10): ❗❗ index out of bounds
» ▶ CANNOT show description as it is based on subject which is not defined:
◾ equals: 1 (kotlin.Int <1234789>)
» ▶ first:
◾ equals: 1 (kotlin.Int <1234789>)
```
Also, this version of `feature` provides to kind of overloads, one without and one with `assertionCreator`-lambda.
(see for instance [Arbitrary Features](#arbitrary-features) for more information).
### Ambiguity Problems
Unfortunately there are several Kotlin bugs when it comes to overloading, especially in conjunction with `KFunction`
(see [Kotlin Bugs](https://github.com/robstoll/atrium/wiki/Kotlin-Bugs-and-missing-features) and upvote in case you run into one).
It might happen that you run into such issues using `feature` in conjuction with a `MetaFeature`-provider-lambda (as shown in [Property and Methods](#property-and-methods)).
However, Atrium provides alternative functions next to `f` within the `MetaFeature`-provider-lambda to disambiguate the situation.
Use `p` for properties and `f0` to `f5` for methods.
Likely you need to specify the type parameters manually as Kotlin is not able to infer them correctly.
```kotlin
class WorstCase {
val propAndFun: Int = 1
fun propAndFun(): Int = 1
fun overloaded(): Int = 1
fun overloaded(b: Boolean): Int = 1
}
expect(WorstCase()) {
feature { p(it::propAndFun) }
feature { f0(it::propAndFun) }
feature { f0(it::overloaded) }
feature { f1(it::overloaded, true) }.toEqual(1)
}
```
Notice, that you might run into the situation that Intellij is happy but the compiler is not.
For instance, Intellij will suggest that you can remove the type parameters in the above example.
Yet, if you do so, then the compiler will fail, mentioning ambiguous overloads.
Most of the time this problem stems from the reason that Intellij is using a newer Kotlin version to analyse
than the one you compile your project with.
Next to using the alternative functions, you could also use `its` or the overload of `feauture` which expects
a `String` as description (as shown in [arbitrary features](#arbitrary-features).
### Property does not exist
In case you deal with Java code and are using `feature`, then you might run into the problem that a property does not exist.
This is due to the fact that Kotlin only provides syntactic sugar to access a getter via property syntax.
In such a case, use the `get...` method instead. For instance:
```java
// java
class A {
public String getFoo() { return "bar"; }
}
```
```kotlin
// kotlin
val a = A()
a.foo // syntactic sugar, accesses getFoo via property
expect(a)
// feature{ f(it::foo) } // would result in a compile error
.feature { f(it::getFoo) } // works
.startsWith(...)
```
## Type Assertions
```kotlin
interface SuperType
data class SubType1(val number: Int) : SuperType
data class SubType2(val word: String, val flag: Boolean) : SuperType
val x: SuperType = SubType2("hello", flag = true)
expect(x).toBeAnInstanceOf()
.feature { f(it::number) }
.toEqual(2)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L92) ↓ [Output](#ex-type-assertions-1)
```text
expected that subject: SubType2(word=hello, flag=true) (readme.examples.SubType2 <1234789>)
◆ is instance of type: SubType1 (readme.examples.SubType1)
```
You can narrow a type with the `toBeA` function.
On one hand it checks that the subject of the current assertion (`x` in the above example) is actually the expected type
and on the other hand it turns the subject into this type.
This way you can make specific assertions which are only possible for the corresponding type
-- for instance, considering the above example, `number` is not available on `SuperType` but only on `SubType1`.
```kotlin
expect(x).toBeAnInstanceOf {
feature { f(it::word) }.toEqual("goodbye")
feature { f(it::flag) }.toEqual(false)
}
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L98) ↓ [Output](#ex-type-assertions-2)
```text
expected that subject: SubType2(word=hello, flag=true) (readme.examples.SubType2 <1234789>)
◆ ▶ word: "hello" <1234789>
◾ equals: "goodbye" <1234789>
◆ ▶ flag: true
◾ equals: false
```
There are two `toBeA` overloads:
- the first (shown in the first example) is parameterless and turns only the subject into the expected type;
failing to do so cannot include additional information in error reporting though.
- the second (shown in the second example) expects an `assertionCreator`-lambda in which you can define sub-assertions.
An `assertionCreator`-lambda has always the semantic of an [assertion group block](#define-single-assertions-or-assertion-groups)
-- as a recapitulation, assertions in an assertion group block are all evaluated and failures are reported at the end of the block.
It has also the benefit, that Atrium can provide those sub-assertions in error reporting,
showing some additional context in case of a failure.
## Nullable Types
Let us look at the case where the subject of the assertion has a [nullable type](https://kotlinlang.org/docs/reference/null-safety.html).
```kotlin
val slogan1: String? = "postulating assertions made easy"
expect(slogan1).toEqual(null)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L106) ↓ [Output](#ex-nullable-1)
```text
expected that subject: "postulating assertions made easy" <1234789>
◆ equals: null
```
```kotlin
val slogan2: String? = null
expect(slogan2).toEqual("postulating assertions made easy")
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L110) ↓ [Output](#ex-nullable-2)
```text
expected that subject: null
◆ equals: "postulating assertions made easy" <1234789>
```
On one hand, you can use `toEqual` and pass the same type --
`String?` in the above example, so in other words either `null` as in the first example or a `String` as in the second example.
On the other hand, you can use `notToEqualNull` to turn the subject into its non-null version.
This is a shortcut for `toBeA` where `Xy` is the non-nullable type (see [Type Assertions](#type-assertions)).
Following an example:
```kotlin
expect(slogan2) // subject has type String?
.notToEqualNull() // subject is narrowed to String
.toStartWith("atrium")
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L115) ↓ [Output](#ex-nullable-3)
```text
expected that subject: null
◆ is instance of type: String (kotlin.String) -- Class: java.lang.String
```
Since `notToEqualNull` delegates to `toBeA` it also provides two overloads,
one without (example above) and one with `assertionCreator`-lambda (example below); see
[Type Assertions](#type-assertions) for more information on the difference of the overloads.
```kotlin
expect(slogan2).notToEqualNull { toStartWith("atrium") }
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L120) ↓ [Output](#ex-nullable-4)
```text
expected that subject: null
◆ is instance of type: String (kotlin.String) -- Class: java.lang.String
» starts with: "atrium" <1234789>
```
Atrium provides one additional function which is intended for [data driven testing](#data-driven-testing)
involving nullable types and is explained in the corresponding section.
👓 _<- this icon signifies additional information, worth reading in our opinion but if you are only after code examples,
then you can skip now to the next section (otherwise click on the arrow to expand the section)._
👓 dealing a lot with nullable types from Java...
... in this case we recommend having a look at the [Java Interoperability](#java-interoperability) section.
## Collection Assertions
Atrium provides assertion builders which allow to make sophisticated `toContain` assertions for `Iterable`.
Such a building process allows you to define very specific assertions, where the process is guided by a fluent builder pattern.
You can either use such an
[Assertion Builder](#sophisticated-assertion-builders)
to create a specific assertion or use one of the
[Shortcut Functions](#shortcut-functions) in case you have kind of a common case.
The following sub sections show both use cases by examples.
### Shortcut Functions
```kotlin
expect(listOf(1, 2, 2, 4)).toContain(2, 3)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L124) ↓ [Output](#ex-collection-short-1)
```text
expected that subject: [1, 2, 2, 4] (java.util.Arrays.ArrayList <1234789>)
◆ contains, in any order:
⚬ an element which equals: 3 (kotlin.Int <1234789>)
» but no such element was found
```
The assertion function `toContain(2, 3)` is a shortcut for using a
[Sophisticated Assertion Builder](#sophisticated-assertion-builders) -- it actually calls `toContain.inAnyOrder.atLeast(1).values(2, 3)`.
This is reflected in the output, which tells us that we expected that the `number of such entries`, which is actually `0`, `is at least: 1`.
👓 and what about expected value 2?
Exactly, what about the expected value `2`, why do we not see anything about it in the output?
The output does not show anything about the expected value `2` because the default reporter reports only failing assertions.
Back to the shortcut functions.
Next to expecting that certain values are contained in or rather returned by an `Iterable`,
Atrium allows us to use an `assertionCreator`-lambda to identify an element
(an `assertionCreator`-lambda can also be thought of as matcher / predicate in this context).
An element is considered as identified, if it holds all specified assertions.
Following an example:
```kotlin
expect(listOf(1, 2, 2, 4)).toContain(
{ toBeLessThan(0) },
{ toBeGreaterThan(2).toBeLessThan(4) }
)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L128) ↓ [Output](#ex-collection-short-2)
```text
expected that subject: [1, 2, 2, 4] (java.util.Arrays.ArrayList <1234789>)
◆ contains, in any order:
⚬ an element which:
» is less than: 0 (kotlin.Int <1234789>)
» but no such element was found
⚬ an element which:
» is greater than: 2 (kotlin.Int <1234789>)
» is less than: 4 (kotlin.Int <1234789>)
» but no such element was found
```
In the above example, neither of the two lambdas matched any elements and thus both are reported as failing (sub) assertions.
Another `toContain` shortcut function which Atrium provides for `Iterable` is kind of
the opposite of `inAnyOrder.atLeast(1)` and is named `toContainExactly`.
Again, Atrium provides two overloads for it, one for values,
e.g. `toContainExactly(1, 2)` which calls `toContain.inOrder.only.values(1, 2)`
and a second one which expects one or more `assertionCreator`-lambda,
e.g. `toContainExactly( { toBeGreaterThan(5) }, { toBeLessThan(10) })`
which calls `toContain.inOrder.only.elements({ toBeGreaterThan(5) }, { toBeLessThan(10) })`.
We will spare the examples here and show them in the following sections.
Notice that you can pass `null` to `toContainExactly` instead of an `assertionCreator`-lambda to match `null`.
This makes of course only sense if your `Iterable` contains nullable elements.
Atrium provides also a `notToContain` shortcut function.
Furthermore, it provides aliases for `toContain` and `notToContain` named `toHaveNextAndAny` and `toHaveNextAndNone`,
which might be a better choice if you think in terms of: expect a predicate holds.
These two are completed with an `toHaveNextAndAll` assertion function.
Following each in action:
```kotlin
expect(listOf(1, 2, 3, 4)).toHaveElementsAndAny {
toBeLessThan(0)
}
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L135) ↓ [Output](#ex-collection-any)
```text
expected that subject: [1, 2, 3, 4] (java.util.Arrays.ArrayList <1234789>)
◆ contains, in any order:
⚬ an element which:
» is less than: 0 (kotlin.Int <1234789>)
» but no such element was found
```
```kotlin
expect(listOf(1, 2, 3, 4)).toHaveElementsAndNone {
toBeGreaterThan(2)
}
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L140) ↓ [Output](#ex-collection-none)
```text
expected that subject: [1, 2, 3, 4] (java.util.Arrays.ArrayList <1234789>)
◆ does not contain:
⚬ an element which:
» is greater than: 2 (kotlin.Int <1234789>)
❗❗ following elements were mismatched:
⚬ index 2: 3 (kotlin.Int <1234789>)
⚬ index 3: 4 (kotlin.Int <1234789>)
```
```kotlin
expect(listOf(1, 2, 3, 4)).toHaveElementsAndAll {
toBeGreaterThan(2)
}
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L145) ↓ [Output](#ex-collection-all)
```text
expected that subject: [1, 2, 3, 4] (java.util.Arrays.ArrayList <1234789>)
◆ all entries:
» is greater than: 2 (kotlin.Int <1234789>)
❗❗ following elements were mismatched:
⚬ index 0: 1 (kotlin.Int <1234789>)
⚬ index 1: 2 (kotlin.Int <1234789>)
```
### Sophisticated Assertion Builders
Sophisticated assertion builders implement a fluent builder pattern.
To use the assertion builder for sophisticated `Iterable`-toContain-assertions, you can type `toContain`
-- as you would when using the [Shortcut Functions](#shortcut-functions) `toContain` --
but type `.` as next step (so that you are using the property `toContain` instead of one of the shortcut functions).
Currently, the builder provides two options, either `inAnyOrder` or `inOrder`.
In case you are using an IDE, you do not really have to think too much -- use code completion;
the fluent builders will guide you through your decision-making 😊
Following on the last section we will start with an `inOrder` example:
```kotlin
expect(listOf(1, 2, 2, 4)).toContain.inOrder.only.entries({ toBeLessThan(3) }, { toBeLessThan(2) })
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L151) ↓ [Output](#ex-collection-builder-1)
```text
expected that subject: [1, 2, 2, 4] (java.util.Arrays.ArrayList <1234789>)
◆ ▶ size: 4 (kotlin.Int <1234789>)
◾ equals: 2 (kotlin.Int <1234789>)
◆ contains only, in order:
✔ ▶ element 0: 1 (kotlin.Int <1234789>)
◾ is less than: 3 (kotlin.Int <1234789>)
✘ ▶ element 1: 2 (kotlin.Int <1234789>)
◾ is less than: 2 (kotlin.Int <1234789>)
❗❗ additional elements detected:
⚬ element 2: 2 (kotlin.Int <1234789>)
⚬ element 3: 4 (kotlin.Int <1234789>)
```
Since we have chosen the `only` option, Atrium shows us a summary where we see three things:
- Whether a specified `assertionCreator`-lambda matched (signified by `✔` or `✘`)
the corresponding element or not (e.g. `✘ ▶ entry 1:` was `2` and we expected, it `is less than 2`)
- Whether the expected size was correct or not (`✘ ▶ size:` was `4`, we expected it, `to be: 2` -- see also [Property Assertions](#property-assertions))
- and last but not least, mismatches or additional elements as further clue (`❗❗ additional elements detected`).
😍 We are pretty sure you are going to love this feature as well.
Please star Atrium if you like using it.
💬 too verbose?
As side notice, in case you are dealing with large `Iterable` and do not want such a verbose output,
then let use know by up-voting https://github.com/robstoll/atrium/issues/292
So far the verbose output was always handy for us, but you might have other test cases than we have.
Also notice, that Atrium will not deal with infinite `Iterable`s, i.e. you need to use `take(100)` or the like first.
Following one more example for `inOrder` as well as a few examples for `inAnyOrder`.
We think explanations are no longer required at this stage.
In case you have a question (no matter about which section), then please turn up in the
[atrium Slack channel](https://kotlinlang.slack.com/messages/C887ZKGCQ)
([Invite yourself](https://slack.kotlinlang.org/) in case you do not have an account yet)
and we happily answer your question there.
```kotlin
expect(listOf(1, 2, 2, 4)).toContain.inOrder.only.values(1, 2, 2, 3, 4)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L154) ↓ [Output](#ex-collection-builder-2)
```text
expected that subject: [1, 2, 2, 4] (java.util.Arrays.ArrayList <1234789>)
◆ ▶ size: 4 (kotlin.Int <1234789>)
◾ equals: 5 (kotlin.Int <1234789>)
◆ contains only, in order:
✔ ▶ element 0: 1 (kotlin.Int <1234789>)
◾ equals: 1 (kotlin.Int <1234789>)
✔ ▶ element 1: 2 (kotlin.Int <1234789>)
◾ equals: 2 (kotlin.Int <1234789>)
✔ ▶ element 2: 2 (kotlin.Int <1234789>)
◾ equals: 2 (kotlin.Int <1234789>)
✘ ▶ element 3: 4 (kotlin.Int <1234789>)
◾ equals: 3 (kotlin.Int <1234789>)
✘ ▶ element 4: ❗❗ hasNext() returned false
» equals: 4 (kotlin.Int <1234789>)
```
```kotlin
expect(listOf(1, 2, 2, 4)).toContain.inAnyOrder.atLeast(1).butAtMost(2).entries({ toBeLessThan(3) })
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L157) ↓ [Output](#ex-collection-builder-3)
```text
expected that subject: [1, 2, 2, 4] (java.util.Arrays.ArrayList <1234789>)
◆ contains, in any order:
⚬ an element which:
» is less than: 3 (kotlin.Int <1234789>)
⚬ ▶ number of such entries: 3
◾ is at most: 2
```
```kotlin
expect(listOf(1, 2, 2, 4)).toContain.inAnyOrder.only.values(1, 2, 3, 4)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L160) ↓ [Output](#ex-collection-builder-4)
```text
expected that subject: [1, 2, 2, 4] (java.util.Arrays.ArrayList <1234789>)
◆ contains only, in any order:
✔ an element which equals: 1 (kotlin.Int <1234789>)
✔ an element which equals: 2 (kotlin.Int <1234789>)
✘ an element which equals: 3 (kotlin.Int <1234789>)
✔ an element which equals: 4 (kotlin.Int <1234789>)
❗❗ following elements were mismatched:
⚬ 2 (kotlin.Int <1234789>)
```
```kotlin
expect(listOf(1, 2, 2, 4)).toContain.inAnyOrder.only.values(4, 3, 2, 2, 1)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L163) ↓ [Output](#ex-collection-builder-5)
```text
expected that subject: [1, 2, 2, 4] (java.util.Arrays.ArrayList <1234789>)
◆ ▶ size: 4 (kotlin.Int <1234789>)
◾ equals: 5 (kotlin.Int <1234789>)
◆ contains only, in any order:
✔ an element which equals: 4 (kotlin.Int <1234789>)
✘ an element which equals: 3 (kotlin.Int <1234789>)
✔ an element which equals: 2 (kotlin.Int <1234789>)
✔ an element which equals: 2 (kotlin.Int <1234789>)
✔ an element which equals: 1 (kotlin.Int <1234789>)
```
## Map Assertions
Map assertions are kind of very similar to [Collection Assertions](#collection-assertions), also regarding reporting.
That is the reason why we are not going into too much detail here because we assume you are already familiar with it.
We provide again [Shortcut Functions](#shortcut-functions-1) for the most common scenarios
and more [Sophisticated Assertion Builder](#sophisticated-assertion-builders-1) for the other cases.
### Shortcut Functions
```kotlin
expect(mapOf("a" to 1, "b" to 2)).toContain("c" to 2, "a" to 1, "b" to 1)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L167) ↓ [Output](#ex-map-1)
```text
expected that subject: {a=1, b=2} (java.util.LinkedHashMap <1234789>)
◆ contains, in any order:
⚬ ▶ entry "c": ❗❗ key does not exist
» equals: 2 (kotlin.Int <1234789>)
⚬ ▶ entry "b": 2 (kotlin.Int <1234789>)
◾ equals: 1 (kotlin.Int <1234789>)
```
Next to making assertions based on key-value `Pair`s one can also define sub assertions for the value of an entry with
the help of the parameter object `KeyValue`:
```kotlin
expect(mapOf("a" to 1, "b" to 2)).toContain(
KeyValue("c") { toEqual(2) },
KeyValue("a") { toBeGreaterThan(2) },
KeyValue("b") { toBeLessThan(2) }
)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L170) ↓ [Output](#ex-map-2)
```text
expected that subject: {a=1, b=2} (java.util.LinkedHashMap <1234789>)
◆ contains, in any order:
⚬ ▶ entry "c": ❗❗ key does not exist
» equals: 2 (kotlin.Int <1234789>)
⚬ ▶ entry "a": 1 (kotlin.Int <1234789>)
◾ is greater than: 2 (kotlin.Int <1234789>)
⚬ ▶ entry "b": 2 (kotlin.Int <1234789>)
◾ is less than: 2 (kotlin.Int <1234789>)
```
In case you expect that a map only contains certain entries, then you can use the shortcut `toContainOnly`.
Again both overloads are provided, one for key-value `Pair`s:
```kotlin
expect(mapOf("a" to 1, "b" to 2)).toContainOnly("b" to 2)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L178) ↓ [Output](#ex-map-only-1)
```text
expected that subject: {a=1, b=2} (java.util.LinkedHashMap <1234789>)
◆ ▶ size: 2 (kotlin.Int <1234789>)
◾ equals: 1 (kotlin.Int <1234789>)
◆ contains only, in any order:
✔ ▶ entry "b": 2 (kotlin.Int <1234789>)
◾ equals: 2 (kotlin.Int <1234789>)
❗❗ additional entries detected:
⚬ entry "a": 1 (kotlin.Int <1234789>)
```
And the other overload which expects a `KeyValue` and allows defining sub asertions for the value:
```kotlin
expect(mapOf("a" to 1, "b" to 2)).toContainOnly(
KeyValue("c") { toEqual(2) },
KeyValue("a") { toBeLessThan(2) },
KeyValue("b") { toBeLessThan(2) }
)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L181) ↓ [Output](#ex-map-only-2)
```text
expected that subject: {a=1, b=2} (java.util.LinkedHashMap <1234789>)
◆ ▶ size: 2 (kotlin.Int <1234789>)
◾ equals: 3 (kotlin.Int <1234789>)
◆ contains only, in any order:
✘ ▶ entry "c": ❗❗ key does not exist
» equals: 2 (kotlin.Int <1234789>)
✔ ▶ entry "a": 1 (kotlin.Int <1234789>)
◾ is less than: 2 (kotlin.Int <1234789>)
✘ ▶ entry "b": 2 (kotlin.Int <1234789>)
◾ is less than: 2 (kotlin.Int <1234789>)
```
### Sophisticated Assertion Builders
Most functionality for `Map.toContain` are provided as shortcut functions but there is a handy one
in case you deal with ordered Maps: `.toContain.inOrder.only`
There are multiple methods finalising the building process : `entry`/`entries`/`entriesOf` where `entry` and `entries`
again provide two overloads, one expecting key-value `Pair`s:
```kotlin
expect(mapOf("a" to 1, "b" to 2)).toContain.inOrder.only.entries("b" to 2, "a" to 1)
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L189) ↓ [Output](#ex-map-builder-1)
```text
expected that subject: {a=1, b=2} (java.util.LinkedHashMap <1234789>)
◆ contains only, in order:
✘ ▶ element 0: a=1 (java.util.LinkedHashMap.Entry <1234789>)
◾ ▶ key: "a" <1234789>
◾ equals: "b" <1234789>
◾ ▶ value: 1 (kotlin.Int <1234789>)
◾ equals: 2 (kotlin.Int <1234789>)
✘ ▶ element 1: b=2 (java.util.LinkedHashMap.Entry <1234789>)
◾ ▶ key: "b" <1234789>
◾ equals: "a" <1234789>
◾ ▶ value: 2 (kotlin.Int <1234789>)
◾ equals: 1 (kotlin.Int <1234789>)
```
And the other expecting `KeyValue`s which allow specifying sub assertions for the value
```kotlin
expect(mapOf("a" to 1, "b" to 2)).toContain.inOrder.only.entries(
KeyValue("a") { toBeLessThan(2) },
KeyValue("b") { toBeLessThan(2) })
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L192) ↓ [Output](#ex-map-builder-2)
```text
expected that subject: {a=1, b=2} (java.util.LinkedHashMap <1234789>)
◆ contains only, in order:
✔ ▶ element 0: a=1 (java.util.LinkedHashMap.Entry <1234789>)
◾ ▶ key: "a" <1234789>
◾ equals: "a" <1234789>
◾ ▶ value: 1 (kotlin.Int <1234789>)
◾ is less than: 2 (kotlin.Int <1234789>)
✘ ▶ element 1: b=2 (java.util.LinkedHashMap.Entry <1234789>)
◾ ▶ key: "b" <1234789>
◾ equals: "b" <1234789>
◾ ▶ value: 2 (kotlin.Int <1234789>)
◾ is less than: 2 (kotlin.Int <1234789>)
```
### Others
In case you want to postulate an assertion about a value of one particular key, then you can use `getExisting`.
For instance:
```kotlin
data class Person(val firstName: String, val lastName: String, val age: Int)
val bernstein = Person("Leonard", "Bernstein", 50)
expect(mapOf("bernstein" to bernstein))
.getExisting("bernstein") {
feature { f(it::firstName) }.toEqual("Leonard")
feature { f(it::age) }.toEqual(60)
}
.getExisting("einstein") {
feature { f(it::firstName) }.toEqual("Albert")
}
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L202) ↓ [Output](#ex-map-3)
```text
expected that subject: {bernstein=Person(firstName=Leonard, lastName=Bernstein, age=50)} (java.util.Collections.SingletonMap <1234789>)
◆ ▶ get("bernstein"): Person(firstName=Leonard, lastName=Bernstein, age=50) (readme.examples.MostExamplesSpec$1$Person <1234789>)
◾ ▶ age: 50 (kotlin.Int <1234789>)
◾ equals: 60 (kotlin.Int <1234789>)
```
In case you want to make an assertion only about the keys or values of the `Map` then you can use `keys` or `values`:
```kotlin
expect(mapOf("a" to 1, "b" to 2)) {
keys { toHaveElementsAndAll { toStartWith("a") } }
values { toHaveElementsAndNone { toBeGreaterThan(1) } }
}
```
↑ [Example](https://github.com/robstoll/atrium/tree/master/misc/tools/readme-examples/src/main/kotlin/readme/examples/MostExamplesSpec.kt#L214) ↓ [Output](#ex-map-4)
```text
expected that subject: {a=1, b=2} (java.util.LinkedHashMap <1234789>)
◆ ▶ keys: [a, b] (java.util.LinkedHashMap.LinkedKeySet <1234789>)
◾ all entries:
» starts with: "a" <1234789>
❗❗ following elements were mismatched:
⚬ index 1: "b" <1234789>
◆ ▶ values: [1, 2] (java.util.LinkedHashMap.LinkedValues <1234789>)
◾ does not contain:
⚬ an element which:
» is greater than: 1 (kotlin.Int <1234789>)
❗❗ following elements were mismatched:
⚬ index 1: 2 (kotlin.Int <1234789>)
```
Last but not least, you can use the non-reporting `asEntries()` function which
turns `Expect