{"id":13504763,"url":"https://github.com/robstoll/atrium","last_synced_at":"2025-05-14T11:09:13.693Z","repository":{"id":37601559,"uuid":"78367168","full_name":"robstoll/atrium","owner":"robstoll","description":"A multiplatform expectation library for Kotlin","archived":false,"fork":false,"pushed_at":"2025-05-06T07:33:46.000Z","size":72370,"stargazers_count":595,"open_issues_count":46,"forks_count":218,"subscribers_count":25,"default_branch":"main","last_synced_at":"2025-05-10T23:03:33.445Z","etag":null,"topics":["assert","assertion-framework","assertion-library","assertions","hacktoberfest","kotlin","kotlin-multiplatform","kotlin-testing"],"latest_commit_sha":null,"homepage":"https://docs.atriumlib.org","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"eupl-1.2","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robstoll.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"robstoll"}},"created_at":"2017-01-08T19:59:59.000Z","updated_at":"2025-05-06T07:33:49.000Z","dependencies_parsed_at":"2023-09-30T09:28:04.919Z","dependency_job_id":"c35b8a82-f179-4034-aaca-34685adba5d0","html_url":"https://github.com/robstoll/atrium","commit_stats":{"total_commits":3354,"total_committers":122,"mean_commits":"27.491803278688526","dds":"0.22689326177698266","last_synced_commit":"bc29c4590dd5a551af0d87ba2b812aedae0c7439"},"previous_names":[],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robstoll%2Fatrium","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robstoll%2Fatrium/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robstoll%2Fatrium/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robstoll%2Fatrium/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robstoll","download_url":"https://codeload.github.com/robstoll/atrium/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254129488,"owners_count":22019628,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assert","assertion-framework","assertion-library","assertions","hacktoberfest","kotlin","kotlin-multiplatform","kotlin-testing"],"created_at":"2024-08-01T00:00:51.007Z","updated_at":"2025-05-14T11:09:13.652Z","avatar_url":"https://github.com/robstoll.png","language":"Kotlin","readme":"\u003c!-- for main --\u003e\n\n[![Download](https://img.shields.io/badge/Download-1.2.0-%23007ec6)](https://central.sonatype.com/artifact/ch.tutteli.atrium/atrium-fluent/1.2.0)\n[![EUPL](https://img.shields.io/badge/%E2%9A%96-EUPL%201.2-%230b45a6)](https://joinup.ec.europa.eu/collection/eupl/eupl-text-11-12 \"License\")\n[![atrium @ kotlinlang.slack.com](https://img.shields.io/static/v1?label=kotlinlang\u0026message=atrium\u0026color=blue\u0026logo=slack)](https://kotlinlang.slack.com/messages/atrium \"See invitation link under section FAQ\")\n[![Quality Assurance](https://github.com/robstoll/atrium/actions/workflows/quality-assurance.yml/badge.svg?event=push\u0026branch=main)](https://github.com/robstoll/atrium/actions/workflows/quality-assurance.yml?query=branch%3Amain)\n[![Coverage](https://codecov.io/gh/robstoll/atrium/branch/main/graph/badge.svg)](https://app.codecov.io/github/robstoll/atrium/branch/main) \n[![Newcomers Welcome](https://img.shields.io/badge/%F0%9F%91%8B-Newcomers%20Welcome-blueviolet)](https://github.com/robstoll/atrium/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 \"Ask in slack for help\")\n\n\u003c!-- for a specific release --\u003e\n\u003c!--\n[![Download](https://img.shields.io/badge/Download-1.2.0-%23007ec6)](https://central.sonatype.com/artifact/ch.tutteli.atrium/atrium-fluent/1.2.0)\n[![EUPL](https://img.shields.io/badge/%E2%9A%96-EUPL%201.2-%230b45a6)](https://joinup.ec.europa.eu/collection/eupl/eupl-text-11-12 \"License\")\n[![atrium @ kotlinlang.slack.com](https://img.shields.io/static/v1?label=kotlinlang\u0026message=atrium\u0026color=blue\u0026logo=slack)](https://kotlinlang.slack.com/messages/C887ZKGCQ \"See invitation link under section FAQ\")\n[![Newcomers Welcome](https://img.shields.io/badge/%F0%9F%91%8B-Newcomers%20Welcome-blueviolet)](https://github.com/robstoll/atrium/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 \"Ask in slack for help\")\n--\u003e\n\n# \u003cimg src=\"https://raw.githubusercontent.com/robstoll/atrium/gh-pages/images/logo.svg?sanitize=true\" alt=\"Atrium\" title=\"Atrium\"/\u003e\nAtrium is an open-source multiplatform expectation/assertion library for Kotlin with support for JVM, JS and Android.\nIt is designed to support multiple [APIs](#api-styles), focuses on helping developers to understand what went wrong and prevents common pitfalls. \nThe project was inspired by AssertJ at first but moved on and provides now more \nflexibility, features and hints to its users (so to you 😉).\n\nAtrium is designed to be extensible as well as configurable \nand allows you to extend it with your own expectation functions, customise reporting \nor even replace core components with your own implementation easily.\n\nSee [Examples](#examples) below to get a feel for how you could benefit from Atrium.\n\n---\n❗ You are taking a *sneak peek* at the next version. It could be that some features you find on this page are not released yet.  \nPlease have a look at the README of the corresponding release/git tag. Latest version: [README of v1.2.0](https://github.com/robstoll/atrium/tree/main/README.md).\n\n---\n\n**Table of Content**\n- [Installation](#installation)\n  - [Extensions](#extensions)\n  - [Sample Projects](#sample-projects)\n- [Examples](#examples)\n  - [Your First Expectation](#your-first-expectation)\n  - [Define Single Expectations or an Expectation-Group](#define-single-expectations-or-an-expectation-group)\n    - [Soft-Expectations](#soft-expectations)\n  - [Expect an Exception](#expect-an-exception)\n  - [Feature Extractors](#feature-extractors)\n    - [Property and Method](#property-and-methods)\n    - [Arbitrary Features](#arbitrary-features)\n    - [Extract Subject](#subject-extraction)\n  - [Type Expectations](#type-expectations)\n  - [Nullable Types](#nullable-types)\n  - [Collection Expectations](#collection-expectations)\n    - [Shortcut Functions](#shortcut-functions)\n    - [Sophisticated Expectation Builders](#sophisticated-expectation-builders)\n  - [Map Expectations](#map-expectations)\n    - [Shortcut Functions](#shortcut-functions-1)\n    - [Sophisticated Expectation Builders](#sophisticated-expectation-builders-1)\n    - [Others](#others)\n  - [Path Expectations](#path-expectations)\n  - [Attaching a Reason](#attaching-a-reason)\n  - [Integrate other Assertion/Expectation Libraries](#integrate-other-assertionexpectation-libraries)\n  - [Data Driven Testing](#data-driven-testing)\n  - [Further Examples](#further-examples)\n- [How is Atrium different from other Expectation/Assertion Libraries](#how-is-atrium-different-from-other-expectationassertion-libraries)\n- [Write own Expectation Functions](#write-own-expectation-functions)\n    - [Boolean based Expectation Functions](#boolean-based-expectation-functions)\n    - [Throwable based Expectation Functions](#throwable-based-expectation-functions) \n    - [Compose Functions](#compose-expectation-functions)\n    - [Enhanced Reporting](#enhanced-reporting)\n    - [Own Sophisticated Expectation Builders](#own-sophisticated-expectation-builders)\n- [Use own Expectation Verb](#use-own-expectation-verb)\n  - [Use own Components](#use-own-components)\n- [API Styles](#api-styles)\n- [Java Interoperability](#java-interoperability)\n- [KDoc - Code Documentation](#kdoc---code-documentation)\n- [FAQ](#faq)\n  - [My expectation function is not available for subtypes](#my-expectation-function-is-not-available-for-subtypes)\n  - [I have problems in conjunction with `feature`](#i-have-problems-in-conjunction-with-feature)\n  - [Does Atrium provide something like AssertJ's soft assertion?](#does-atrium-provide-something-like-assertjs-soft-assertion)\n  - [Are there toContain/toHaveElementsAndAll/None/Any expectation functions for `Sequence`/`Array`?](#are-there-tocontaintohaveelementsandallnoneany-expectation-functions-for-sequencearray)\n  - [Where are the expectation functions for java.io.File?](#where-are-the-expectation-functions-for-javaiofile)\n  - [Where are the expectation functions for java.util.Date?](#where-are-the-expectation-functions-for-javautildate)\n  - [Where do I find a list of all available functions?](#where-do-i-find-a-list-of-all-available-functions)\n- [Roadmap](#roadmap)\n- [Contributors and contribute](#contributors-and-contribute)\n- [Sponsors](#sponsors)\n- [License](#license)\n\n# Installation\n\nAtrium is published to [mavenCentral](https://search.maven.org/search?q=g:ch.tutteli.atrium).\nIt has the following minimum requirement:\n- Kotlin: 1.4\n- JVM: 11\n- JS: IR-Backend (LEGACY support was dropped with Atrium 1.2.0)\n\nIn case you use Kotlin 1.5 or newer, then regardless of the target platform, you can use the following group and artifactId\n\n*build.gradle.kts*:\n```kotlin\nrepositories {\n    mavenCentral()\n}\ndependencies {\n    testImplementation(\"ch.tutteli.atrium:atrium-fluent:1.2.0\")\n}\n```\n\nAnd in case of an MPP project accordingly:\n```kotlin\nrepositories {\n    mavenCentral()\n}\nkotlin {\n    sourceSets {\n        val commonTest by getting {\n            implementation(\"ch.tutteli.atrium:atrium-fluent:1.2.0\")\n        }\n        // no need to add it to specific targets such as jvmTest, is done automatically starting with Kotlin 1.5\n    }\n}\n```\n\nExchange `fluent` with `infix` depending on your taste (see [API styles](#api-styles) for more information).\n\nThat is all, you are all set. Jump to [Examples](#examples) which shows how to use Atrium.\n\n\n\u003ca name=\"installation-prior-to-kotlin-1.5\"\u003e\u003c/a\u003e\n\u003cdetails\u003e\n\u003csummary\u003eI use a version prior to Kotlin 1.5\u003c/summary\u003e\n\nIn case you use a version prior to Kotlin 1.5, then use the following depending on the target platform:\n- common: atrium-fluent\n- jvm: atrium-fluent-jvm\n- android: atrium-fluent-jvm\n- js: atrium-fluent-js\n\n\u003c/details\u003e\n\nI have other problems: please take a look at the [Sample Projects](#sample-projects) for further guidance.\n\n## Sample Projects\n\nHave a look into the [samples](https://github.com/robstoll/atrium/tree/main/samples)\nfolder, it currently contains sample projects for\n- [js - kotlin-test](https://github.com/robstoll/atrium/tree/main/samples/js/kotlin-test)\n- [jvm -- junit5](https://github.com/robstoll/atrium/tree/main/samples/jvm/junit5)\n- [jvm -- maven](https://github.com/robstoll/atrium/tree/main/samples/jvm/maven/)\n- [jvm -- kotest](https://github.com/robstoll/atrium/tree/main/samples/jvm/kotest)\n- [jvm -- TestNG](https://github.com/robstoll/atrium/tree/main/samples/jvm/testng)\n- [multiplatform project](https://github.com/robstoll/atrium/tree/main/samples/multiplatform/)\n\nAre you using a different runner? A PR would be appreciated 😊.\n\n# Examples\nWe are using the API provided by the bundle module \n[atrium-fluent](https://github.com/robstoll/atrium/tree/main/bundles/fluent/atrium-fluent/build.gradle.kts)\nin the following examples. \nIt provides a pure fluent API for the JVM platform.\nHave a look at \n[apis/differences.md](https://github.com/robstoll/atrium/tree/main/apis/differences.md)\nto see how the infix API looks like, how they differ respectively.\n\n## Your First Expectation\nSee also [AnyExpectationSamples](https://github.com/robstoll/atrium/blob/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/AnyExpectationSamples.kt)\nfor further examples.\n\nWe start off with a simple example:\n\n\u003cex-first\u003e\n\n```kotlin\nimport ch.tutteli.atrium.api.fluent.en_GB.*\nimport ch.tutteli.atrium.api.verbs.expect\n\nval x = 10\nexpect(x).toEqual(9)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/FirstExample.kt#L20)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-first)\u003c/sub\u003e\n\u003ca name=\"ex-first\"\u003e\u003c/a\u003e\n```text\nI expected subject: 10        (kotlin.Int \u003c1234789\u003e)\n◆ to equal: 9        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-first\u003e\n\nThe statement can be read as \"I expect x to equal nine\" where an equality check is used (for an identity check, you would have to use `toBeTheSameInstace`). \nSince this is false, an `AssertionError` is thrown with a corresponding message as shown in the [Output](#ex-first)\nwhere on the first line the actual subject  (`10` in the above example) is shown and on following lines which start with, \n`◆ ...`  (here only one) we see the expectations we had about the subject\nIn this sense the report can be read as `I expected the subject of the expectation, which was 10, to equal 9` \n-- and needlessly to say, this expectation was not met and thus the thrown error.\n\nWe are using the bundle [atrium-fluent](https://github.com/robstoll/atrium/tree/main/bundles/fluent/atrium-fluent/build.gradle)\nand the predefined expectation verb `expect` in the examples. \nThus, the corresponding `import`s at the beginning of the file in the above example.\nWe will omit the `import` statements in the remaining examples for brevity. \n\n**You want to run the examples yourself?**\nHave a look at the [Installation](#installation) section which explains how to set up a dependency to Atrium.\n\nThe next section shows how you can define multiple expectations for the same subject.\n\n## Define Single Expectations or an Expectation-Group\n\u003cex-single\u003e\n\n```kotlin\n// two single expectations, only first evaluated\nexpect(4 + 6).toBeLessThan(5).toBeGreaterThan(10)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L15)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-single)\u003c/sub\u003e\n\u003ca name=\"ex-single\"\u003e\u003c/a\u003e\n```text\nI expected subject: 10        (kotlin.Int \u003c1234789\u003e)\n◆ to be less than: 5        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-single\u003e\n\nAtrium allows you to chain expectations or in other words\nyou only need to write the `expect(...)` part once and can state several single expectations for the same subject.\nThe expression which determines the subject of the expectations (`4 + 6` in the above example) is evaluated only once. \n\nIn this sense we could have written it also as follows (which is only the same because `4 + 6` does not have side effects).\n\n\u003ccode-single-explained\u003e\n\n```kotlin\nexpect(4 + 6).toBeLessThan(5)\nexpect(4 + 6).toBeGreaterThan(10)\n```\n\u003c/code-single-explained\u003e\n\nThe first `expect` statement throws an `AssertionError` as it does not hold. \nIn the above example, `toBeLessThan(5)` is already wrong and thus `toBeGreaterThan(10)` was not evaluated at all \nand correspondingly not reported.\n\n\u003ca name=\"expecation-groups-are-better-soft-assertions\"\u003e\u003c/a\u003e\nIf you want that both expectations are evaluated together, then use the expectation-group syntax as follows:\n \n\u003cex-group\u003e\n\n```kotlin\n// expectation-group with two expectations, both evaluated\nexpect(4 + 6) {\n    toBeLessThan(5)\n    toBeGreaterThan(10)\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L33)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-group)\u003c/sub\u003e\n\u003ca name=\"ex-group\"\u003e\u003c/a\u003e\n```text\nI expected subject: 10        (kotlin.Int \u003c1234789\u003e)\n◆ to be less than: 5        (kotlin.Int \u003c1234789\u003e)\n◆ to be greater than: 10        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-group\u003e\n\nAn expectation-group throws an `AssertionError` at the end of its block (i.e. at the closing `}`); \nhence reports that both expectations do not hold.\nThe reporting can be read as `I expected the subject of the expectation, which was 10, to be less than 5 and to be greater than 10`\n\n\u003chr/\u003e\n\nYou can use `and` as filling element between single expectations and expectation-groups:\n\n\u003ccode-and\u003e\n\n```kotlin\nexpect(5).toBeGreaterThan(2).and.toBeLessThan(10)\n\nexpect(5) {\n    // ...\n} and { // if the previous block fails, then this one is not evaluated\n    // ...\n}\n```\n\u003c/code-and\u003e\n\n### Soft-Expectations\n\nAn [expectation-group](#define-single-expectations-or-an-expectation-group) is similar to the concept of \nsoft assertions in AssertJ although with a few differences:\n- you do not need an extra utility such as `assertSoftly` if you define expectations about the same subject,  \n  you can just use `expect` as always.\n- you do not have to repeat the subject\n\nThe [above example](#ex-group) is the equivalent of the following AssertJ example:\n```kotlin\nassertSoftly {\n    assertThat(4 + 6).isLessThan(5)\n    assertThat(4 + 6).isGreatThan(10)\n}\n\nfun assertSoftly(body: SoftAssertions.() -\u003e Unit) =\n    SoftAssertions.assertSoftly(body)\n```\n\nMoreover, in contrast to AssertJ, the block syntax is provided at many places and not only on the top-level. \nAs an example, the following AssertJ example:\n```kotlin\nassertSoftly {\n    assertThat(mansion.numOfGuests).isEqualTo(7)\n    assertThat(mansion.kitchen.status).isEqualTo(\"clean\")\n    assertThat(mansion.kitchen.numOfTables).isGreaterThan(5).isLessThan(10)\n}\n\nfun assertSoftly(body: SoftAssertions.() -\u003e Unit) =\n    SoftAssertions.assertSoftly(body)\n```\ncould be written as follows in Atrium (see also [Feature Extractors](#feature-extractors)). \n```kotlin\nexpect(mansion) {\n    its { numOfGuests }.toEqual(7)\n    its({ kitchen }) {\n        its { status }.toEqual(\"clean\")\n        its { numOfTables }.toBeGreaterThan(5).toBeLessThan(10)\n    }\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e💬 fail-fast in expectation-groups \u003c/summary\u003e\n\nNote that you are free to choose the dot-notation (e.g. `toBeGreaterThan(5).toBeLessThan(10)`) at any level, however\nonce you are within an expectation-group block, all of them are evaluated (no more fail-fast behaviour applies).\nIn other words, `toBeLessThan(10)` is still reported, even though `toBeGreaterThan(5)` already fails \nin the above example.\n\n\u003c/details\u003e\n\nIf you want to state expectations about multiple unrelated subjects and want to report them together (or introduce groups),\nthen you might be interested in using `expectGrouped` instead of `expect` -\u003e take a look at the [data driven testing](#data-driven-testing) section.\n \n## Expect an Exception\nSee also [Fun0ExpectationSamples](https://github.com/robstoll/atrium/blob/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/Fun0ExpectationSamples.kt)\nfor further examples.\n\n\u003cex-toThrow1\u003e\n\n```kotlin\nexpect {\n    // this lambda does something but eventually...\n    throw IllegalArgumentException(\"name is empty\")\n}.toThrow\u003cIllegalStateException\u003e()\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/ToThrowExamples.kt#L15)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-toThrow1)\u003c/sub\u003e\n\u003ca name=\"ex-toThrow1\"\u003e\u003c/a\u003e\n```text\nI expected subject: () -\u003e kotlin.Nothing        (readme.examples.ToThrowExamples$ex-toThrow1$1 \u003c1234789\u003e)\n◆ ▶ thrown exception when called: java.lang.IllegalArgumentException\n    ◾ to be an instance of type: IllegalStateException (java.lang.IllegalStateException)\n    ℹ Properties of the unexpected IllegalArgumentException\n      » message: \"name is empty\"        \u003c1234789\u003e\n      » stacktrace: \n        ⚬ readme.examples.ToThrowExamples$ex-toThrow1$1.invoke(ToThrowExamples.kt:18)\n        ⚬ readme.examples.ToThrowExamples$ex-toThrow1$1.invoke(ToThrowExamples.kt:16)\n        ⚬ readme.examples.ToThrowExamples.ex-toThrow1(ToThrowExamples.kt:47)\n        ⚬ java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n        ⚬ java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n        ⚬ java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n        ⚬ java.base/java.lang.reflect.Method.invoke(Method.java:566)\n```\n\u003c/ex-toThrow1\u003e\n\nYou can also pass a lambda to `expect` and then use `toThrow` to state the expectation that \ninvoking the lambda throws a certain exception (`IllegalStateException` in the example above).\n\nAs with all narrowing functions, there are two overloads:\n- the first expects an `assertionCreator`-lambda in which you can define sub-expectations.\n    An `assertionCreator`-lambda has always the semantic of an [expectation-group](#define-single-expectations-or-an-expectation-group).\n    It has also the benefit, that Atrium can show those sub-expectations in error reporting,\n    even if a failure happens before, giving some additional context to a failure.\n- the second overload expects all the parameters except the `assertionCreator`-lambda and turns the subject into the expected type; \n  failing to do so cannot include additional information in error reporting though.\n\n\nThe following example uses the first overload\n\n\u003cex-toThrow2\u003e\n\n```kotlin\nexpect {\n    throw IllegalArgumentException()\n}.toThrow\u003cIllegalArgumentException\u003e {\n    message { toStartWith(\"firstName\") }\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/ToThrowExamples.kt#L23)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-toThrow2)\u003c/sub\u003e\n\u003ca name=\"ex-toThrow2\"\u003e\u003c/a\u003e\n```text\nI expected subject: () -\u003e kotlin.Nothing        (readme.examples.ToThrowExamples$ex-toThrow2$1 \u003c1234789\u003e)\n◆ ▶ thrown exception when called: java.lang.IllegalArgumentException\n    ◾ ▶ message: null\n        ◾ not to equal: null but to be an instance of: String (kotlin.String) -- Class: java.lang.String\n          » to start with: \"firstName\"        \u003c1234789\u003e\n```\n\u003c/ex-toThrow2\u003e\n\nAnd this one uses the second overload; notice the difference in reporting, \nthis one does not include what sub-expectations would have been made if the narrowing succeeded\n\n\u003cex-toThrow3\u003e\n\n```kotlin\nexpect {\n    throw IllegalArgumentException()\n}.toThrow\u003cIllegalArgumentException\u003e().message.toStartWith(\"firstName\")\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/ToThrowExamples.kt#L32)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-toThrow3)\u003c/sub\u003e\n\u003ca name=\"ex-toThrow3\"\u003e\u003c/a\u003e\n```text\nI expected subject: () -\u003e kotlin.Nothing        (readme.examples.ToThrowExamples$ex-toThrow3$1 \u003c1234789\u003e)\n◆ ▶ thrown exception when called: java.lang.IllegalArgumentException\n    ◾ ▶ message: null\n        ◾ not to equal: null but to be an instance of: String (kotlin.String) -- Class: java.lang.String\n```\n\u003c/ex-toThrow3\u003e\n\nAs side notice, `message` is a shortcut for `feature(Throwable::message).notToEqualNull`, \nwhich creates a feature extractor (see next section) about `Throwable::message`.  \n\nThere is also the counterpart of `toThrow` named `notToThrow`:\n\n\u003cex-notToThrow\u003e\n\n```kotlin\nexpect {\n    // this block does something but eventually...\n    throw IllegalArgumentException(\"name is empty\", RuntimeException(\"a cause\"))\n}.notToThrow()\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/ToThrowExamples.kt#L39)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-notToThrow)\u003c/sub\u003e\n\u003ca name=\"ex-notToThrow\"\u003e\u003c/a\u003e\n```text\nI expected subject: () -\u003e kotlin.Nothing        (readme.examples.ToThrowExamples$ex-notToThrow$1 \u003c1234789\u003e)\n◆ ▶ invoke(): ❗❗ threw java.lang.IllegalArgumentException\n    ℹ Properties of the unexpected IllegalArgumentException\n      » message: \"name is empty\"        \u003c1234789\u003e\n      » stacktrace: \n        ⚬ readme.examples.ToThrowExamples$ex-notToThrow$1.invoke(ToThrowExamples.kt:42)\n        ⚬ readme.examples.ToThrowExamples$ex-notToThrow$1.invoke(ToThrowExamples.kt:40)\n        ⚬ readme.examples.ToThrowExamples.ex-notToThrow(ToThrowExamples.kt:43)\n        ⚬ java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n        ⚬ java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n        ⚬ java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n        ⚬ java.base/java.lang.reflect.Method.invoke(Method.java:566)\n      » cause: java.lang.RuntimeException\n          » message: \"a cause\"        \u003c1234789\u003e\n          » stacktrace: \n            ⚬ readme.examples.ToThrowExamples$ex-notToThrow$1.invoke(ToThrowExamples.kt:42)\n```\n\u003c/ex-notToThrow\u003e\n\nNotice that stacks are filtered so that you only see what is of interest. \nYou can [use your own](#use-own-components) \n[AtriumErrorAdjuster](https://docs.atriumlib.org/latest#/kdoc/atrium-core/ch.tutteli.atrium.reporting/-atrium-error-adjuster)\nto adjust the filtering.\nStack frames of Atrium and of test runners (JUnit, Kotest, TestNG and Spek for JVM, mocha and jasmine for JS) are excluded per default.\n[Create a Feature Request](https://github.com/robstoll/atrium/issues/new?template=feature_request.md\u0026title=[Feature])\nin case you use a different runner, we can add yours to the list as well. \n\n\u003ca name=\"property-assertions\"\u003e\u003c/a\u003e\n\u003ca name=\"method-assertions\"\u003e\u003c/a\u003e\n\u003ca name=\"feature-assertions\"\u003e\u003c/a\u003e\n## Feature Extractors\nSee also [FeatureExtractorSamples](https://github.com/robstoll/atrium/blob/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/FeatureExtractorSamples.kt)\nfor further examples.\n\nMany times you are only interested in certain features of the subject and want to state expectations about them. \n\nThere are different use cases for feature extractors. \nWe will start of with properties and method calls and go on with more complicated scenarios.\n\n### Property and Methods\nWe are using the `data class Person` in the following examples:\n\n\u003ccode-Person\u003e\n\n```kotlin\ndata class Person(val firstName: String, val lastName: String, val isStudent: Boolean) {\n    fun fullName() = \"$firstName $lastName\"\n    fun nickname(includeLastName: Boolean) = when (includeLastName) {\n        false -\u003e \"Mr. $firstName\"\n        true -\u003e \"$firstName aka. $lastName\"\n    }\n}\n\nval myPerson = Person(\"Robert\", \"Stoll\", false)\n```\n\u003c/code-Person\u003e\n\nThe simplest way of defining expectations for a property of an instance or for the return value of a method call is by\nusing the extension method `its`.\n\n\u003cex-its-single\u003e\n\n```kotlin\nexpect(myPerson)\n    .its({ isStudent }) { toEqual(true) } // fails, subject still Person afterwards\n    .its { fullName() }                   // not evaluated anymore, subject String afterwards\n    .toStartWith(\"rob\")                   // not evaluated anymore\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/FeatureExtractorExamples.kt#L33)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-its-single)\u003c/sub\u003e\n\u003ca name=\"ex-its-single\"\u003e\u003c/a\u003e\n```text\nI expected subject: Person(firstName=Robert, lastName=Stoll, isStudent=false)        (readme.examples.FeatureExtractorExamples.Person \u003c1234789\u003e)\n◆ ▶ its.definedIn(FeatureExtractorExamples.kt:35): false\n    ◾ to equal: true\n```\n\u003c/ex-its-single\u003e\n\nIn the above example we created two expectations, one for the property `isStudent` of `myPerson`\nand a second one for the return value of calling `fullName()` on `myPerson`.\nA feature extractor is indicated as follows in reporting:\nIt starts with a `▶` followed by the feature's description and its actual value.\nSo the above output can be read as \n\n\u003e I expected the subject of the expectation, which was actually Person(...), \n\u003e respectively its property which was defined in FeatureExtractorSpec.kt on line 43, \n\u003e which was actually `false`, to equal `true`.\n\nThe second feature is not shown in reporting as the first expectation about the property `isStudent` already failed, \nand we have chosen to use [single expectations](#define-single-expectations-or-an-expectation-group)\nwhich have fail-fast semantic.\n\nFeature extractors follow the common pattern of having two overloads:\n- the first expects an `assertionCreator`-lambda, in which you can define sub-expectations for the feature.\n    An `assertionCreator`-lambda has always the semantic of an [expectation-group](#define-single-expectations-or-an-expectation-group)\n    or in other words, not-fail fast. It has also the benefit, that Atrium can provide those sub-expectations in error reporting.\n    Moreover, the subject stays the same so that subsequent calls are still about the same subject\n- the second overload expects all the parameters except the `assertionCreator`-lambda and changes the subject to the feature,\n  meaning a subsequent call in the fluent chain is about the feature and not the previous subject.\n\n  \u003cex-its-group\u003e\n  \n  ```kotlin\n  expect(myPerson) { // forms an expectation-group\n  \n      its({ firstName }) {   // forms an expectation-group\n          toStartWith(\"Pe\")  // fails\n          toEndWith(\"er\")    // is evaluated nonetheless\n      }                      // fails as a whole\n  \n      // still evaluated, as it is in outer expectation-group\n      its { lastName }.toEqual(\"Dummy\")\n  }\n  ```\n  ↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/FeatureExtractorExamples.kt#L42)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-its-group)\u003c/sub\u003e\n  \u003ca name=\"ex-its-group\"\u003e\u003c/a\u003e\n  ```text\n  I expected subject: Person(firstName=Robert, lastName=Stoll, isStudent=false)        (readme.examples.FeatureExtractorExamples.Person \u003c1234789\u003e)\n  ◆ ▶ its.definedIn(FeatureExtractorExamples.kt:45): \"Robert\"        \u003c1234789\u003e\n      ◾ to start with: \"Pe\"        \u003c1234789\u003e\n      ◾ to end with: \"er\"        \u003c1234789\u003e\n  ◆ ▶ its.definedIn(FeatureExtractorExamples.kt:51): \"Stoll\"        \u003c1234789\u003e\n      ◾ to equal: \"Dummy\"        \u003c1234789\u003e\n  ```\n  \u003c/ex-its-group\u003e\n\n\u003cbr/\u003e\n\nOne drawback of `its` (which we plan to improve but most likely not before we drop support for Kotlin \u003c 1.5) is that reading the resulting\nfeature description does not immediately tell us what feature we extracted.\n\nThat is where the `feature` function comes into play. It is based on reflection and uses the name of the feature \nas description. Following the first example rewritten to `feature`.\n\n\u003cex-property-methods-single\u003e\n\n```kotlin\nexpect(myPerson)\n    .feature({ f(it::isStudent) }) { toEqual(true) } // fails, subject still Person afterwards\n    .feature { f(it::fullName) }                     // not evaluated anymore, subject String afterwards\n    .toStartWith(\"rob\")                              // not evaluated anymore\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/FeatureExtractorExamples.kt#L57)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-property-methods-single)\u003c/sub\u003e\n\u003ca name=\"ex-property-methods-single\"\u003e\u003c/a\u003e\n```text\nI expected subject: Person(firstName=Robert, lastName=Stoll, isStudent=false)        (readme.examples.FeatureExtractorExamples.Person \u003c1234789\u003e)\n◆ ▶ isStudent: false\n    ◾ to equal: true\n```\n\u003c/ex-property-methods-single\u003e\n\nThe report reads much nicer now: \n\n\u003e I expected the subject of the expectation, \n\u003e which was actually Person(...), respectively its property `isStudent`, \n\u003e which was actually `false`, to equal `true`\n\nThe drawback of `feature` compared to `its` is its syntax. Certainly, one has to get used to it first. Another is that\nyou might run into [Ambiguity Problems](#ambiguity-problems) due to Kotlin bugs.\n\n`feature` has several overloads, we are looking at the one expecting a lambda in which you have to provide a `MetaFeature`.\nCreating a `MetaFeature` is done via the function `f` by passing in a \n[bounded reference](https://kotlinlang.org/docs/reference/reflection.html#bound-function-and-property-references-since-11) \nof the corresponding property or method (including arguments if required).\n`it` within the `MetaFeature`-provider-lambda refers to the subject of the expectation (`myPerson` in the above example).\n\nAlso `feature` follows the common pattern of having two overloads where the first expects an `assertionCreator`-lambda and\nthe second has the same parameters except the `assertionCreator`-lambda and changes the subject to the feature,\nmeaning a subsequent call in the fluent chain is about the feature and not the previous subject.\nFollowing the second example rewritten from `its` to `feature`:\n\n\u003cex-property-methods-group\u003e\n\n```kotlin\nexpect(myPerson) { // forms an expectation-group\n\n    feature({ f(it::firstName) }) { // forms an expectation-group\n        toStartWith(\"Pe\")           // fails\n        toEndWith(\"er\")             // is evaluated nonetheless\n    }                               // fails as a whole\n\n    // still evaluated, as it is in outer expectation-group\n    feature { f(it::lastName) }.toEqual(\"Dummy\")\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/FeatureExtractorExamples.kt#L66)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-property-methods-group)\u003c/sub\u003e\n\u003ca name=\"ex-property-methods-group\"\u003e\u003c/a\u003e\n```text\nI expected subject: Person(firstName=Robert, lastName=Stoll, isStudent=false)        (readme.examples.FeatureExtractorExamples.Person \u003c1234789\u003e)\n◆ ▶ firstName: \"Robert\"        \u003c1234789\u003e\n    ◾ to start with: \"Pe\"        \u003c1234789\u003e\n    ◾ to end with: \"er\"        \u003c1234789\u003e\n◆ ▶ lastName: \"Stoll\"        \u003c1234789\u003e\n    ◾ to equal: \"Dummy\"        \u003c1234789\u003e\n```\n\u003c/ex-property-methods-group\u003e\n\nAtrium provides several shortcuts for commonly used properties so that you can use them instead of writing `its { ... }` / `feature(...)` all the time.\nFor instance, `message` for Throwable (see [Expect an Exception](#expect-an-exception)), `first` and `second` for `Pair` and others.\nPlease [open a feature request](https://github.com/robstoll/atrium/issues/new?template=feature_request.md\u0026title=[Feature]) in case you miss a shortcut.\n\n💬 \u0026lt;- _this icon signifies answers/input for advanced users, you might want to skip them if you are new to Atrium._\u003cbr/\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e💬 Provide a feature extractor for each property? \u003c/summary\u003e\n\nYou might be asking yourself whether it is better to [write an own feature extractor](#write-own-expectation-functions) or use `feature`. \n\nThe only drawback of using an existing property is that a few more keystrokes are required compared to \n[writing an own feature extractor](#write-own-expectation-functions) once and then reuse it (as we did with `message`).\nYet, we do not recommend writing an own feature extractor for every single property.\nWe think it makes sense to add one if you use it a lot and (preferably) it is a stable API. \nWhy not always? Because one quickly forgets to rename the feature extractor \nif the property as such is renamed (e.g., as part of an IDE refactoring). \nAs you can see, you would need to keep the property name and the name of the feature extractor in sync to be meaningful \n(otherwise one gets quickly confused or has to remember two names for the same thing). \n\nWriting feature extractors for method calls is a different story though, especially due to [overload bugs in Kotlin](https://github.com/robstoll/atrium/wiki/Kotlin-Bugs-and-missing-features).\nAlso, code completion is not yet as good as it should be when it comes to methods. \nLast but not least, in case it is not always safe to call a method (e.g. `List.get` =\u003e IndexOutOfBound) then it makes\nsense to wrap it into an own feature extractor and use `_logic.extractFeature`.\n\n\u003c/details\u003e\n  \nLast but not least, let us have a look at an example where a method with arguments is used as feature:\n\n\u003cex-methods-args\u003e\n\n```kotlin\nexpect(myPerson)\n    .feature { f(it::nickname, false) } // subject narrowed to String\n    .toEqual(\"Robert aka. Stoll\")       // fails\n    .toStartWith(\"llotS\")               // not evaluated anymore\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/FeatureExtractorExamples.kt#L81)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-methods-args)\u003c/sub\u003e\n\u003ca name=\"ex-methods-args\"\u003e\u003c/a\u003e\n```text\nI expected subject: Person(firstName=Robert, lastName=Stoll, isStudent=false)        (readme.examples.FeatureExtractorExamples.Person \u003c1234789\u003e)\n◆ ▶ nickname(false): \"Mr. Robert\"        \u003c1234789\u003e\n    ◾ to equal: \"Robert aka. Stoll\"        \u003c1234789\u003e\n```\n\u003c/ex-methods-args\u003e\n\n`f` supports methods with up to 5 arguments.\n\nAtrium provides shortcuts for commonly used methods, e.g. `List.get`, `Map.getExisting`, `Optional.toBePresent` or `Result.toBeSuccess` \nwhere all of them include some additional checking (index bound, existence of the key within the map etc.)\nPlease [open a feature request](https://github.com/robstoll/atrium/issues/new?template=feature_request.md\u0026title=[Feature]) \nin case you miss a shortcut. \n\n\u003cdetails\u003e\n\u003csummary\u003e💬 Write own feature extractors with additional checks.\u003c/summary\u003e\n\nAtrium provides a feature extractor which allows to extract in a safe way in case the extraction is only valid for certain subjects.\nIt is inter alia used for [`List.get`](https://github.com/robstoll/atrium/tree/main/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/impl/DefaultListAssertions.kt#L13)\n\n\u003c/details\u003e\n\n### Arbitrary Features\nA feature does not necessarily have to be directly related to the subject as properties or method calls do.\nEither use `its` or the overload of `feature` which expects a feature description in form of a `String` as first argument.\nFollowing an example using `feature`.\n\n\u003cex-arbitrary-features\u003e\n\n```kotlin\ndata class FamilyMember(val name: String)\n\ndata class Family(val members: List\u003cFamilyMember\u003e)\n\nval myFamily = Family(listOf(FamilyMember(\"Robert\")))\nexpect(myFamily)\n    .feature(\"the number of members\", { members.size }) { toEqual(1) } // subject still Family afterwards\n    .feature(\"the first member's name\") { members.first().name }       // subject narrowed to String\n    .toEqual(\"Peter\")\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/FeatureExtractorExamples.kt#L99)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-arbitrary-features)\u003c/sub\u003e\n\u003ca name=\"ex-arbitrary-features\"\u003e\u003c/a\u003e\n```text\nI expected subject: Family(members=[FamilyMember(name=Robert)])        (readme.examples.FeatureExtractorExamples.Family \u003c1234789\u003e)\n◆ ▶ the first member's name: \"Robert\"        \u003c1234789\u003e\n    ◾ to equal: \"Peter\"        \u003c1234789\u003e\n```\n\u003c/ex-arbitrary-features\u003e\n\nAlso, this version of `feature` provides two different kind of overloads:\n- the first expects a feature description, a feature-provider-lambda and an `assertionCreator`-lambda, in which you can define sub-expectations for the feature.\n  An `assertionCreator`-lambda has always the semantic of an [expectation-group](#define-single-expectations-or-an-expectation-group) or in other words, not-fail fast.\n  It has also the benefit, that Atrium can provide those sub-expectations in error reporting,\n  Moreover, the subject stays the same so that subsequent calls are still about the same subject.\n- the second overload expects all the parameters except the `assertionCreator`-lambda and changes the subject to the feature, \n  meaning a subsequent call in the fluent chain is about the feature and not the previous subject.\n  \nAs you can see, Atrium provides a generic way to postulate expectations about features. \nYet, if you extract the same feature over and over again or it gets more complicated, \nthen it might be worth to [write an own expectation function](#write-own-expectation-functions) where we recommend to \nuse `feature` over `its`.\n\n### Within Expectation Functions / Feature Extractors\n\nIn case you write an own expectation function, then we discourage two things: \n- using `its` because the reporting reads less nice and it is also less efficient than `feature`\n- using `feature` with a `MetaFeature`-provider-lambda (as shown in [Property and Methods](#property-and-methods))\n\nInstead, we encourage you to pass a [class references](https://kotlinlang.org/docs/reference/reflection.html#class-references)\nto `feature`.\nThis has the benefit, that we can always show the feature name, also in case a previous feature extraction or subject\ntransformation failed.\nFollowing an example: \n\n\u003cex-within-expectation-functions\u003e\n\n```kotlin\nfun \u003cF : Any, T : Pair\u003cF, *\u003e\u003e Expect\u003cT\u003e.firstToBeDoneWrong(expected: F) =\n    feature({ f(it::first) }) { toEqual(expected) }\n\nfun \u003cF : Any, T : Pair\u003cF, *\u003e\u003e Expect\u003cT\u003e.firstToBe(expected: F) =\n    feature(Pair\u003cF, *\u003e::first) { toEqual(expected) }\n\nexpect(listOf(1 to \"a\", 2 to \"b\")).get(10) {\n    firstToBeDoneWrong(1)\n    firstToBe(1)\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/FeatureExtractorExamples.kt#L116)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-within-expectation-functions)\u003c/sub\u003e\n\u003ca name=\"ex-within-expectation-functions\"\u003e\u003c/a\u003e\n```text\nI expected subject: [(1, a), (2, b)]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ ▶ get(10): ❗❗ Index out of bounds\n      » ▶ CANNOT show description as it is based on subject which is not defined: \n          ◾ to equal: 1        (kotlin.Int \u003c1234789\u003e)\n      » ▶ first: \n          ◾ to equal: 1        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-within-expectation-functions\u003e\n\nAlso, this version of `feature` provides two kind of overloads, one without and one with `assertionCreator`-lambda.\n(see for instance [Arbitrary Features](#arbitrary-features) for more information).\n\n### Ambiguity Problems\nUnfortunately there are several Kotlin bugs when it comes to overloading, especially in conjunction with `KFunction`\n(see [Kotlin Bugs](https://github.com/robstoll/atrium/wiki/Kotlin-Bugs-and-missing-features) and upvote in case you run into one).\nIt might happen that you run into such issues using `feature` in conjunction with a `MetaFeature`-provider-lambda (as shown in [Property and Methods](#property-and-methods)).\nHowever, Atrium provides alternative functions next to `f` within the `MetaFeature`-provider-lambda to disambiguate the situation.\nUse `p` for properties and `f0` to `f5` for methods. \nLikely you need to specify the type parameters manually as Kotlin is not able to infer them correctly.\n\n\u003ccode-ambiguity-problems\u003e\n\n```kotlin\nclass WorstCase {\n    val propAndFun: Int = 1\n    fun propAndFun(): Int = 1\n\n    fun overloaded(): Int = 1\n    fun overloaded(b: Boolean): Int = 1\n}\n\nexpect(WorstCase()) {\n    feature { p\u003cInt\u003e(it::propAndFun) }\n    feature { f0\u003cInt\u003e(it::propAndFun) }\n    feature { f0\u003cInt\u003e(it::overloaded) }\n    feature { f1\u003cBoolean, Int\u003e(it::overloaded, true) }.toEqual(1)\n}\n```\n\u003c/code-ambiguity-problems\u003e\n\nNotice, that you might run into the situation that Intellij is happy but the compiler is not.\nFor instance, Intellij will suggest that you can remove the type parameters in the above example. \nYet, if you do so, then the compiler will fail, mentioning ambiguous overloads. \nMost of the time this problem stems from the reason that Intellij is using a newer Kotlin version to analyse\nthan the one you compile your project with.\n\nNext to using the alternative functions, you could also use `its` or the overload of `feauture` which expects\na `String` as description (as shown in [arbitrary features](#arbitrary-features).\n\n### Property does not exist\n\nIn case you deal with Java code and are using `feature`, then you might run into the problem that a property does not exist. \nThis is due to the fact that Kotlin only provides syntactic sugar to access a getter via property syntax. \nIn such a case, use the `get...` method instead. For instance:\n```java\n// java\nclass A { \n    public String getFoo() { return \"bar\"; } \n}\n```\n```kotlin\n// kotlin\nval a = A()\na.foo // syntactic sugar, accesses getFoo via property\nexpect(a)\n    // feature{ f(it::foo) }    // would result in a compile error\n    .feature { f(it::getFoo) }  // works\n    .startsWith(...)\n``` \n\n## Subject Extraction\n\n`extractSubject` allows to get hold on the subject of the current `Expect` in case it is defined and reports an error\notherwise. There is rarely a good reason to use it as there are better options:\n- [feature extractor](#feature-extractors) (`feature`, `its` or _logic.extractFeature...)\n- toHoldThirdPartyExpectation (see [Integrate other Assertion/Expectation Libraries](#integrate-other-assertionexpectation-libraries)).\n- subject changer (_logic.changeSubject...)\n\nThe only case where it makes sense (which we are aware of so far) is, if your method under test generates random results\n(e.g. a data generator) and you want to state expectations which depend on the random generated data. \nFor instance:\n\n\u003ccode-extractSubject\u003e\n\n```kotlin\nval persons = dataGenerator.getRandomPersonsWithChildren()\nexpect(persons).toHaveElementsAndAll {\n    extractSubject { person -\u003e\n        feature { f(it::children) }.notToHaveElementsOrAll {\n            because(\"person should at least be 16 years older than its children\") {\n                feature { f(it::age) }.toBeLessThan(person.age - 16)\n            }\n        }\n    }\n}\n```\n\u003c/code-extractSubject\u003e\n\n\n## Type Expectations\nSee also [AnyExpectationSamples -\u003e toBeAnInstanceOf and co.](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/AnyExpectationSamples.kt#L128)\nfor further examples.\n\n\u003cex-type-expectations-1\u003e\n\n```kotlin\ninterface SuperType\n\ndata class SubType1(val number: Int) : SuperType\ndata class SubType2(val word: String, val flag: Boolean) : SuperType\n\nval x: SuperType = SubType2(\"hello\", flag = true)\nexpect(x).toBeAnInstanceOf\u003cSubType2\u003e {\n    feature { f(it::word) }.toEqual(\"goodbye\")\n    feature { f(it::flag) }.toEqual(false)\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L59)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-type-expectations-1)\u003c/sub\u003e\n\u003ca name=\"ex-type-expectations-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: SubType2(word=hello, flag=true)        (readme.examples.SubType2 \u003c1234789\u003e)\n◆ ▶ word: \"hello\"        \u003c1234789\u003e\n    ◾ to equal: \"goodbye\"        \u003c1234789\u003e\n◆ ▶ flag: true\n    ◾ to equal: false\n```\n\u003c/ex-type-expectations-1\u003e\n\nYou can narrow the type of the subject with the `toBeAnInstanceOf` function. \nOn one hand it checks that the subject of the current expectation (`x` in the above example) is actually the expected type \nand on the other hand it turns the subject into this type. \nThis way you can make specific expectations which are only possible for the corresponding type\n-- for instance, considering the above example, `number` is not available on `SuperType` but only on `SubType1`.\n\n\u003cex-type-expectations-2\u003e\n\n```kotlin\nexpect(x).toBeAnInstanceOf\u003cSubType1\u003e()\n    .feature { f(it::number) }\n    .toEqual(2)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L67)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-type-expectations-2)\u003c/sub\u003e\n\u003ca name=\"ex-type-expectations-2\"\u003e\u003c/a\u003e\n```text\nI expected subject: SubType2(word=hello, flag=true)        (readme.examples.SubType2 \u003c1234789\u003e)\n◆ to be an instance of type: SubType1 (readme.examples.SubType1)\n```\n\u003c/ex-type-expectations-2\u003e\n\nThere are two `toBeAnInstanceOf` overloads: \n- the first (shown in the first example) expects an `assertionCreator`-lambda in which you can define sub-expectations.\n    An `assertionCreator`-lambda has always the semantic of an [expectation-group](#define-single-expectations-or-an-expectation-group)\n    -- as a recapitulation, expectations in an expectation-group are all evaluated and failures are reported at the end of the block.\n    It has also the benefit, that Atrium can provide those sub-expectations in error reporting,\n    showing some additional context in case of a failure.\n- the second overload (shown in the second example) is parameterless and turns only the subject into the expected type; \n  failing to do so cannot include additional information in error reporting though.\n\n## Nullable Types\nSee also [AnyExpectationSamples -\u003e notToEqualNullFeature and co.](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/AnyExpectationSamples.kt#L81)\nfor further examples.\n\n\nLet us look at the case where the subject of the expectation has a [nullable type](https://kotlinlang.org/docs/reference/null-safety.html).\n\n\u003cex-nullable-1\u003e\n\n```kotlin\nval slogan1: String? = \"postulating expectations made easy\"\nexpect(slogan1).toEqual(null)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L74)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-nullable-1)\u003c/sub\u003e\n\u003ca name=\"ex-nullable-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: \"postulating expectations made easy\"        \u003c1234789\u003e\n◆ to equal: null\n```\n\u003c/ex-nullable-1\u003e\n\n\u003cex-nullable-2\u003e\n\n```kotlin\nval slogan2: String? = null\nexpect(slogan2).toEqual(\"postulating expectations made easy\")\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L79)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-nullable-2)\u003c/sub\u003e\n\u003ca name=\"ex-nullable-2\"\u003e\u003c/a\u003e\n```text\nI expected subject: null\n◆ to equal: \"postulating expectations made easy\"        \u003c1234789\u003e\n```\n\u003c/ex-nullable-2\u003e\n\nOn one hand, you can use `toEqual` and pass the same type -- \n`String?` in the above example, so in other words either `null` as in the first example or a `String` as in the second example.\nOn the other hand, you can use `notToEqualNull` to turn the subject into its non-null version.\n\nFollowing an example:\n\n\u003cex-nullable-3\u003e\n\n```kotlin\nexpect(slogan2)        // subject has type String?\n    .notToEqualNull()  // subject is narrowed to String\n    .toStartWith(\"atrium\")\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L85)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-nullable-3)\u003c/sub\u003e\n\u003ca name=\"ex-nullable-3\"\u003e\u003c/a\u003e\n```text\nI expected subject: null\n◆ not to equal: null but to be an instance of: String (kotlin.String) -- Class: java.lang.String\n```\n\u003c/ex-nullable-3\u003e\n\n`notToEqualNull` provides two overloads: \none without (example above) and one with `assertionCreator`-lambda (example below); see \n[Type Expectations](#type-expectations) for more information on the difference of the overloads.\n\n\u003cex-nullable-4\u003e\n\n```kotlin\nexpect(slogan2).notToEqualNull { toStartWith(\"atrium\") }\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L91)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-nullable-4)\u003c/sub\u003e\n\u003ca name=\"ex-nullable-4\"\u003e\u003c/a\u003e\n```text\nI expected subject: null\n◆ not to equal: null but to be an instance of: String (kotlin.String) -- Class: java.lang.String\n  » to start with: \"atrium\"        \u003c1234789\u003e\n```\n\u003c/ex-nullable-4\u003e\n\nAtrium provides one additional function which is intended for [data driven testing](#data-driven-testing) \ninvolving nullable types and is explained in the corresponding section.\n\n👓 _\u0026lt;- this icon signifies additional information, worth reading in our opinion but if you are only after code examples,\nthen you can skip now to the next section (otherwise click on the arrow to expand the section)._\u003cbr/\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e👓 dealing a lot with nullable types from Java...\u003c/summary\u003e\n\n... in this case we recommend having a look at the [Java Interoperability](#java-interoperability) section.\n\n\u003c/details\u003e\n\n## Collection Expectations\n\nAtrium provides expectation builders which allow to state sophisticated `toContain` expectations about `Iterable\u003cT\u003e`.\nSuch a building process allows you to define very specific expectations, where the process is guided by a fluent builder pattern.\nYou can either use such an \n[Expectation Builder](#sophisticated-expectation-builders)\nto create a specific expectation or use one of the \n[Shortcut Functions](#shortcut-functions) in case you have kind of a common case.\nThe following sub sections show both use cases by examples.\n\n### Shortcut Functions\nSee also\n[IterableExpectationSamples](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/IterableExpectationSamples.kt)\nfor further examples.\n\n\u003cex-collection-short-1\u003e\n\n```kotlin\nexpect(listOf(1, 2, 2, 4)).toContain(2, 3)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/CollectionExamples.kt#L13)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-collection-short-1)\u003c/sub\u003e\n\u003ca name=\"ex-collection-short-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 2, 4]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ to contain, in any order: \n  ⚬ an element which equals: 3        (kotlin.Int \u003c1234789\u003e)\n      » but no such element was found\n```\n\u003c/ex-collection-short-1\u003e\n \nThe expectation function `toContain(2, 3)` is a shortcut for using a \n[Sophisticated Expectation Builder](#sophisticated-expectation-builders) -- it actually calls `toContain.inAnyOrder.atLeast(1).values(2, 3)`. \nThis is reflected in the output.\n\n\u003cdetails\u003e\n\u003csummary\u003e👓 and what about expected value 2?\u003c/summary\u003e\n\nExactly, what about the expected value `2`, why do we not see anything about it in the output?\nThe output does not show anything about the expected value `2` because the default reporter reports only failing expectations.\n\nBack to the shortcut functions.\n\u003chr/\u003e\n\u003c/details\u003e\n \nNext to expecting that certain values are contained in or rather returned by an `Iterable`, \nAtrium allows us to use an `assertionCreator`-lambda to identify an element\n(an `assertionCreator`-lambda can also be thought of as a matcher / predicate in this context).\nAn element is considered as identified, if it holds all specified expectations the `assertionCreator` creates.\nFollowing an example:\n\n\u003cex-collection-short-2\u003e\n\n```kotlin\nexpect(listOf(1, 2, 2, 4)).toContain(\n    { toBeLessThan(0) },\n    { toBeGreaterThan(2).toBeLessThan(4) }\n)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/CollectionExamples.kt#L18)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-collection-short-2)\u003c/sub\u003e\n\u003ca name=\"ex-collection-short-2\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 2, 4]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ to contain, in any order: \n  ⚬ an element which needs: \n      » to be less than: 0        (kotlin.Int \u003c1234789\u003e)\n      » but no such element was found\n  ⚬ an element which needs: \n      » to be greater than: 2        (kotlin.Int \u003c1234789\u003e)\n      » to be less than: 4        (kotlin.Int \u003c1234789\u003e)\n      » but no such element was found\n```\n\u003c/ex-collection-short-2\u003e\n\nIn the above example, neither of the two lambdas matched any elements and thus both are reported as failing (sub) expectations.\n\nAnother `toContain` shortcut function which Atrium provides for `Iterable\u003cT\u003e` is kind of \nthe opposite of `inAnyOrder.atLeast(1)` and is named `toContainExactly`.\nAgain, Atrium provides two overloads for it, one for values,\ne.g. `toContainExactly(1, 2)` which calls `toContain.inOrder.only.values(1, 2)` \nand a second one which expects one or more `assertionCreator`-lambda, \ne.g. `toContainExactly( { toBeGreaterThan(5) }, { toBeLessThan(10) })` \nwhich calls `toContain.inOrder.only.elements({ toBeGreaterThan(5) }, { toBeLessThan(10) })`.\nWe will spare the examples here and show them in the following sections.\nNotice that you can pass `null` to `toContainExactly` instead of an `assertionCreator`-lambda to match `null`.\nThis makes of course only sense if your `Iterable` contains nullable elements.\n\nAtrium provides also a `notToContain` shortcut function. \nFurthermore, it provides aliases for `toContain` and `notToContain` named `toHaveElementsAndAny` and \n`toHaveElementsAndNone`, which might be a better choice if you think in terms of: I expect a predicate holds. \nThese two are completed with an `toHaveElementsAndAll` expectation function.\n\nFollowing each in action:\n\n\u003cex-collection-any\u003e\n\n```kotlin\nexpect(listOf(1, 2, 3, 4)).toHaveElementsAndAny {\n    toBeLessThan(0)\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/CollectionExamples.kt#L26)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-collection-any)\u003c/sub\u003e\n\u003ca name=\"ex-collection-any\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 3, 4]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ to contain, in any order: \n  ⚬ an element which needs: \n      » to be less than: 0        (kotlin.Int \u003c1234789\u003e)\n      » but no such element was found\n```\n\u003c/ex-collection-any\u003e\n\u003chr/\u003e\n\u003cex-collection-none\u003e\n\n```kotlin\nexpect(listOf(1, 2, 3, 4)).toHaveElementsAndNone {\n    toBeGreaterThan(2)\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/CollectionExamples.kt#L33)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-collection-none)\u003c/sub\u003e\n\u003ca name=\"ex-collection-none\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 3, 4]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ not to contain: \n  ⚬ an element which needs: \n      » to be greater than: 2        (kotlin.Int \u003c1234789\u003e)\n      ❗❗ following elements were mismatched: \n         ⚬ index 2: 3        (kotlin.Int \u003c1234789\u003e)\n         ⚬ index 3: 4        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-collection-none\u003e\n\u003chr/\u003e\n\u003cex-collection-all\u003e\n\n```kotlin\nexpect(listOf(1, 2, 3, 4)).toHaveElementsAndAll {\n    toBeGreaterThan(2)\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/CollectionExamples.kt#L40)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-collection-all)\u003c/sub\u003e\n\u003ca name=\"ex-collection-all\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 3, 4]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ elements need all: \n    » to be greater than: 2        (kotlin.Int \u003c1234789\u003e)\n    ❗❗ following elements were mismatched: \n       ⚬ index 0: 1        (kotlin.Int \u003c1234789\u003e)\n       ⚬ index 1: 2        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-collection-all\u003e\n\n\n### Sophisticated Expectation Builders\n\nSophisticated expectation builders implement a fluent builder pattern.\nTo use the expectation builder for sophisticated `Iterable\u003cT\u003e`-toContain-expectations, you can type `toContain` \n-- as you would when using the [Shortcut Functions](#shortcut-functions) `toContain` -- \nbut type `.` as next step (so that you are using the property `toContain` instead of one of the shortcut functions). \nCurrently, the builder provides two options, either `inAnyOrder` or `inOrder`. \nIn case you are using an IDE, you do not really have to think too much -- use code completion; \nthe fluent builders will guide you through your decision-making 😊\n\nFollowing on the last section we will start with an `inOrder` example:\n\n\u003cex-collection-builder-1\u003e\n\n```kotlin\nexpect(listOf(1, 2, 2, 4)).toContain.inOrder.only.entries({ toBeLessThan(3) }, { toBeLessThan(2) })\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/CollectionExamples.kt#L47)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-collection-builder-1)\u003c/sub\u003e\n\u003ca name=\"ex-collection-builder-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 2, 4]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ ▶ size: 4        (kotlin.Int \u003c1234789\u003e)\n    ◾ to equal: 2        (kotlin.Int \u003c1234789\u003e)\n◆ to contain only, in order: \n  ✔ ▶ element 0: 1        (kotlin.Int \u003c1234789\u003e)\n      ◾ to be less than: 3        (kotlin.Int \u003c1234789\u003e)\n  ✘ ▶ element 1: 2        (kotlin.Int \u003c1234789\u003e)\n      ◾ to be less than: 2        (kotlin.Int \u003c1234789\u003e)\n    ❗❗ additional elements detected: \n       ⚬ element 2: 2        (kotlin.Int \u003c1234789\u003e)\n       ⚬ element 3: 4        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-collection-builder-1\u003e\n\nSince we have chosen the `only` option, Atrium shows us a summary\u003csup\u003e\u003ca href=\"#in-order-only-summary\"\u003e1\u003c/a\u003e\u003c/sup\u003e where we see three things:\n- Whether a specified `assertionCreator`-lambda matched (signified by `✔` or `✘`) \n  the corresponding element or not (e.g. `✘ ▶ element 1:` was `2` and we expected, it `to be less than 2`)\n- Whether the expected size was correct or not (`✘ ▶ size:` was `4`, we expected it, `to equal: 2`\n- and last but not least, mismatches or additional elements as further clue (`❗❗ additional elements detected`).\n\n😍 We are pretty sure you are going to love this feature as well. \nPlease star Atrium if you like using it.\n\n\u003ca name=\"in-order-only-summary\"\u003e\u003c/a\u003e\n\u003csup\u003e1\u003c/sup\u003e Atrium shows a summary if we expect up to 10 elements, if we expect more elements, \nthen only failing expectations are shown.\n\n\u003cdetails\u003e\n\u003csummary\u003e💬 Show only failing expectations/elements earlier than 10 expected elements?\u003c/summary\u003e\n\nYou can use the `report` option to specify when Atrium shall start to show only failing expectations.\nFollowing an example changing the limit to 3 elements by using `showOnlyFailingIfMoreExpectedElementsThan` :\n\n\u003cex-collection-reportOptions-1\u003e\n\n```kotlin\nexpect(listOf(1, 2, 2, 4)).toContainExactly(\n    { toBeLessThan(3) },\n    { toBeLessThan(2) },\n    { toBeGreaterThan(1) },\n    report = { showOnlyFailingIfMoreExpectedElementsThan(2) }\n)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/CollectionExamples.kt#L52)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-collection-reportOptions-1)\u003c/sub\u003e\n\u003ca name=\"ex-collection-reportOptions-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 2, 4]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ ▶ size: 4        (kotlin.Int \u003c1234789\u003e)\n    ◾ to equal: 3        (kotlin.Int \u003c1234789\u003e)\n◆ to contain only, in order: \n  ⚬ ▶ element 1: 2        (kotlin.Int \u003c1234789\u003e)\n      ◾ to be less than: 2        (kotlin.Int \u003c1234789\u003e)\n    ❗❗ additional elements detected: \n       ⚬ element 3: 4        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-collection-reportOptions-1\u003e\n\nLikewise, you can use `showOnlyFailing()` to set the limit to 0 and `showAlwaysSummary()` to set the limit to Int.MAX_VALUE\n\n\u003chr/\u003e\n\u003c/details\u003e\n\nFollowing one more example for `inOrder` as well as a few examples for `inAnyOrder`. \nWe think explanations are no longer required at this stage.\nIn case you have a question (no matter about which section), then please turn up in the \n[atrium Slack channel](https://kotlinlang.slack.com/messages/C887ZKGCQ) \n([Invite yourself](https://slack.kotlinlang.org/) in case you do not have an account yet) \nand we happily answer your question there. \n\n\u003cex-collection-builder-2\u003e\n\n```kotlin\nexpect(listOf(1, 2, 2, 4)).toContain.inOrder.only.values(1, 2, 2, 3, 4)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/CollectionExamples.kt#L62)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-collection-builder-2)\u003c/sub\u003e\n\u003ca name=\"ex-collection-builder-2\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 2, 4]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ ▶ size: 4        (kotlin.Int \u003c1234789\u003e)\n    ◾ to equal: 5        (kotlin.Int \u003c1234789\u003e)\n◆ to contain only, in order: \n  ✔ ▶ element 0: 1        (kotlin.Int \u003c1234789\u003e)\n      ◾ to equal: 1        (kotlin.Int \u003c1234789\u003e)\n  ✔ ▶ element 1: 2        (kotlin.Int \u003c1234789\u003e)\n      ◾ to equal: 2        (kotlin.Int \u003c1234789\u003e)\n  ✔ ▶ element 2: 2        (kotlin.Int \u003c1234789\u003e)\n      ◾ to equal: 2        (kotlin.Int \u003c1234789\u003e)\n  ✘ ▶ element 3: 4        (kotlin.Int \u003c1234789\u003e)\n      ◾ to equal: 3        (kotlin.Int \u003c1234789\u003e)\n  ✘ ▶ element 4: ❗❗ hasNext() returned false\n        » to equal: 4        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-collection-builder-2\u003e\n\u003chr/\u003e\n\u003cex-collection-builder-3\u003e\n\n```kotlin\nexpect(listOf(1, 2, 2, 4)).toContain.inAnyOrder.atLeast(1).butAtMost(2).entries({ toBeLessThan(3) })\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/CollectionExamples.kt#L67)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-collection-builder-3)\u003c/sub\u003e\n\u003ca name=\"ex-collection-builder-3\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 2, 4]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ to contain, in any order: \n  ⚬ an element which needs: \n      » to be less than: 3        (kotlin.Int \u003c1234789\u003e)\n    ⚬ ▶ number of such elements: 3\n        ◾ is at most: 2\n```\n\u003c/ex-collection-builder-3\u003e\n\u003chr/\u003e\n\u003cex-collection-builder-4\u003e\n\n```kotlin\nexpect(listOf(1, 2, 2, 4)).toContain.inAnyOrder.only.values(1, 2, 3, 4)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/CollectionExamples.kt#L72)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-collection-builder-4)\u003c/sub\u003e\n\u003ca name=\"ex-collection-builder-4\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 2, 4]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ to contain only, in any order: \n  ✔ an element which equals: 1        (kotlin.Int \u003c1234789\u003e)\n  ✔ an element which equals: 2        (kotlin.Int \u003c1234789\u003e)\n  ✘ an element which equals: 3        (kotlin.Int \u003c1234789\u003e)\n  ✔ an element which equals: 4        (kotlin.Int \u003c1234789\u003e)\n  ❗❗ following elements were mismatched: \n     ⚬ 2        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-collection-builder-4\u003e\n\u003chr/\u003e\n\u003cex-collection-builder-5\u003e\n\n```kotlin\nexpect(listOf(1, 2, 2, 4)).toContain.inAnyOrder.only.values(4, 3, 2, 2, 1)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/CollectionExamples.kt#L77)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-collection-builder-5)\u003c/sub\u003e\n\u003ca name=\"ex-collection-builder-5\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 2, 4]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ ▶ size: 4        (kotlin.Int \u003c1234789\u003e)\n    ◾ to equal: 5        (kotlin.Int \u003c1234789\u003e)\n◆ to contain only, in any order: \n  ✔ an element which equals: 4        (kotlin.Int \u003c1234789\u003e)\n  ✘ an element which equals: 3        (kotlin.Int \u003c1234789\u003e)\n  ✔ an element which equals: 2        (kotlin.Int \u003c1234789\u003e)\n  ✔ an element which equals: 2        (kotlin.Int \u003c1234789\u003e)\n  ✔ an element which equals: 1        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-collection-builder-5\u003e\n\n\n## Map Expectations\n\nMap expectations are kind of very similar to [Collection Expectations](#collection-expectations), also regarding reporting.\nThat is the reason why we are not going into too much detail here because we assume you are already familiar with it.\n\nWe provide again [Shortcut Functions](#shortcut-functions-1) for the most common scenarios\nand more [Sophisticated Expectation Builder](#sophisticated-expectation-builders-1) for the other cases.\n\n### Shortcut Functions\nSee also\n[MapExpectationSamples](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/MapExpectationSamples.kt)\nfor further examples.\n\n\u003cex-map-1\u003e\n\n```kotlin\nexpect(mapOf(\"a\" to 1, \"b\" to 2)).toContain(\"c\" to 2, \"a\" to 1, \"b\" to 1)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MapExamples.kt#L15)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-map-1)\u003c/sub\u003e\n\u003ca name=\"ex-map-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: {a=1, b=2}        (java.util.LinkedHashMap \u003c1234789\u003e)\n◆ to contain, in any order: \n  ⚬ ▶ entry \"c\": ❗❗ key does not exist\n        » to equal: 2        (kotlin.Int \u003c1234789\u003e)\n  ⚬ ▶ entry \"b\": 2        (kotlin.Int \u003c1234789\u003e)\n      ◾ to equal: 1        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-map-1\u003e\n\nNext to postulate expectations based on key-value `Pair`s one can also define sub expectations for the value of \nan entry with the help of the parameter object `KeyValue`:\n\n\u003cex-map-2\u003e\n\n```kotlin\nexpect(mapOf(\"a\" to 1, \"b\" to 2)).toContain(\n    KeyValue(\"c\") { toEqual(2) },\n    KeyValue(\"a\") { toBeGreaterThan(2) },\n    KeyValue(\"b\") { toBeLessThan(2) }\n)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MapExamples.kt#L20)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-map-2)\u003c/sub\u003e\n\u003ca name=\"ex-map-2\"\u003e\u003c/a\u003e\n```text\nI expected subject: {a=1, b=2}        (java.util.LinkedHashMap \u003c1234789\u003e)\n◆ to contain, in any order: \n  ⚬ ▶ entry \"c\": ❗❗ key does not exist\n        » to equal: 2        (kotlin.Int \u003c1234789\u003e)\n  ⚬ ▶ entry \"a\": 1        (kotlin.Int \u003c1234789\u003e)\n      ◾ to be greater than: 2        (kotlin.Int \u003c1234789\u003e)\n  ⚬ ▶ entry \"b\": 2        (kotlin.Int \u003c1234789\u003e)\n      ◾ to be less than: 2        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-map-2\u003e\n\nIn case you expect that a map only contains certain entries, then you can use the shortcut `toContainOnly`.\nAgain both overloads are provided, one for key-value `Pair`s:\n\n\u003cex-map-only-1\u003e\n\n```kotlin\nexpect(mapOf(\"a\" to 1, \"b\" to 2)).toContainOnly(\"b\" to 2)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MapExamples.kt#L29)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-map-only-1)\u003c/sub\u003e\n\u003ca name=\"ex-map-only-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: {a=1, b=2}        (java.util.LinkedHashMap \u003c1234789\u003e)\n◆ ▶ size: 2        (kotlin.Int \u003c1234789\u003e)\n    ◾ to equal: 1        (kotlin.Int \u003c1234789\u003e)\n◆ to contain only, in any order: \n  ✔ ▶ entry \"b\": 2        (kotlin.Int \u003c1234789\u003e)\n      ◾ to equal: 2        (kotlin.Int \u003c1234789\u003e)\n    ❗❗ additional entries detected: \n       ⚬ entry \"a\": 1        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-map-only-1\u003e\n\nAnd the other overload which expects a `KeyValue` and allows defining sub expectations for the value:\n\n\u003cex-map-only-2\u003e\n\n```kotlin\nexpect(mapOf(\"a\" to 1, \"b\" to 2)).toContainOnly(\n    KeyValue(\"c\") { toEqual(2) },\n    KeyValue(\"a\") { toBeLessThan(2) },\n    KeyValue(\"b\") { toBeLessThan(2) }\n)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MapExamples.kt#L34)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-map-only-2)\u003c/sub\u003e\n\u003ca name=\"ex-map-only-2\"\u003e\u003c/a\u003e\n```text\nI expected subject: {a=1, b=2}        (java.util.LinkedHashMap \u003c1234789\u003e)\n◆ ▶ size: 2        (kotlin.Int \u003c1234789\u003e)\n    ◾ to equal: 3        (kotlin.Int \u003c1234789\u003e)\n◆ to contain only, in any order: \n  ✘ ▶ entry \"c\": ❗❗ key does not exist\n        » to equal: 2        (kotlin.Int \u003c1234789\u003e)\n  ✔ ▶ entry \"a\": 1        (kotlin.Int \u003c1234789\u003e)\n      ◾ to be less than: 2        (kotlin.Int \u003c1234789\u003e)\n  ✘ ▶ entry \"b\": 2        (kotlin.Int \u003c1234789\u003e)\n      ◾ to be less than: 2        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-map-only-2\u003e\n\n### Sophisticated Expectation Builders\n\nMost functionality for `Map.toContain` are provided as shortcut functions but there is a handy one \nin case you deal with ordered Maps: `.toContain.inOrder.only`    \nThere are multiple methods finalising the building process : `entry`/`entries`/`entriesOf` where `entry` and `entries`\nagain provide two overloads, one expecting key-value `Pair`s:\n\n\u003cex-map-builder-1\u003e\n\n```kotlin\nexpect(mapOf(\"a\" to 1, \"b\" to 2)).toContain.inOrder.only.entries(\"b\" to 2, \"a\" to 1)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MapExamples.kt#L43)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-map-builder-1)\u003c/sub\u003e\n\u003ca name=\"ex-map-builder-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: {a=1, b=2}        (java.util.LinkedHashMap \u003c1234789\u003e)\n◆ to contain only, in order: \n  ✘ ▶ element 0: a=1        (java.util.LinkedHashMap.Entry \u003c1234789\u003e)\n      ◾ ▶ key: \"a\"        \u003c1234789\u003e\n          ◾ to equal: \"b\"        \u003c1234789\u003e\n      ◾ ▶ value: 1        (kotlin.Int \u003c1234789\u003e)\n          ◾ to equal: 2        (kotlin.Int \u003c1234789\u003e)\n  ✘ ▶ element 1: b=2        (java.util.LinkedHashMap.Entry \u003c1234789\u003e)\n      ◾ ▶ key: \"b\"        \u003c1234789\u003e\n          ◾ to equal: \"a\"        \u003c1234789\u003e\n      ◾ ▶ value: 2        (kotlin.Int \u003c1234789\u003e)\n          ◾ to equal: 1        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-map-builder-1\u003e\n\nAnd the other expecting `KeyValue`s which allow specifying sub expectations for the value\n\n\u003cex-map-builder-2\u003e\n\n```kotlin\nexpect(mapOf(\"a\" to 1, \"b\" to 2)).toContain.inOrder.only.entries(\n    KeyValue(\"a\") { toBeLessThan(2) },\n    KeyValue(\"b\") { toBeLessThan(2) })\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MapExamples.kt#L48)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-map-builder-2)\u003c/sub\u003e\n\u003ca name=\"ex-map-builder-2\"\u003e\u003c/a\u003e\n```text\nI expected subject: {a=1, b=2}        (java.util.LinkedHashMap \u003c1234789\u003e)\n◆ to contain only, in order: \n  ✔ ▶ element 0: a=1        (java.util.LinkedHashMap.Entry \u003c1234789\u003e)\n      ◾ ▶ key: \"a\"        \u003c1234789\u003e\n          ◾ to equal: \"a\"        \u003c1234789\u003e\n      ◾ ▶ value: 1        (kotlin.Int \u003c1234789\u003e)\n          ◾ to be less than: 2        (kotlin.Int \u003c1234789\u003e)\n  ✘ ▶ element 1: b=2        (java.util.LinkedHashMap.Entry \u003c1234789\u003e)\n      ◾ ▶ key: \"b\"        \u003c1234789\u003e\n          ◾ to equal: \"b\"        \u003c1234789\u003e\n      ◾ ▶ value: 2        (kotlin.Int \u003c1234789\u003e)\n          ◾ to be less than: 2        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-map-builder-2\u003e\n\n### Others\n\nIn case you want to postulate an expectation about a value of one particular key, then you can use `getExisting`.\nFor instance:\n\n\u003cex-map-3\u003e\n\n```kotlin\ndata class Person(val firstName: String, val lastName: String, val age: Int)\nval bernstein = Person(\"Leonard\", \"Bernstein\", 50)\nexpect(mapOf(\"bernstein\" to bernstein))\n    .getExisting(\"bernstein\") {\n        feature { f(it::firstName) }.toEqual(\"Leonard\")\n        feature { f(it::age) }.toEqual(60)\n    }\n    .getExisting(\"einstein\") {\n        feature { f(it::firstName) }.toEqual(\"Albert\")\n    }\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MapExamples.kt#L59)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-map-3)\u003c/sub\u003e\n\u003ca name=\"ex-map-3\"\u003e\u003c/a\u003e\n```text\nI expected subject: {bernstein=Person(firstName=Leonard, lastName=Bernstein, age=50)}        (java.util.Collections.SingletonMap \u003c1234789\u003e)\n◆ ▶ get(\"bernstein\"): Person(firstName=Leonard, lastName=Bernstein, age=50)        (readme.examples.MapExamples.Person \u003c1234789\u003e)\n    ◾ ▶ age: 50        (kotlin.Int \u003c1234789\u003e)\n        ◾ to equal: 60        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-map-3\u003e\n\nIn case you have only expectations about the keys or values of the `Map` then you can use `keys` or `values`:\n\n\u003cex-map-4\u003e\n\n```kotlin\nexpect(mapOf(\"a\" to 1, \"b\" to 2)) {\n    keys { toHaveElementsAndAll { toStartWith(\"a\") } }\n    values { toHaveElementsAndNone { toBeGreaterThan(1) } }\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MapExamples.kt#L73)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-map-4)\u003c/sub\u003e\n\u003ca name=\"ex-map-4\"\u003e\u003c/a\u003e\n```text\nI expected subject: {a=1, b=2}        (java.util.LinkedHashMap \u003c1234789\u003e)\n◆ ▶ keys: [a, b]        (java.util.LinkedHashMap.LinkedKeySet \u003c1234789\u003e)\n    ◾ elements need all: \n        » to start with: \"a\"        \u003c1234789\u003e\n        ❗❗ following elements were mismatched: \n           ⚬ index 1: \"b\"        \u003c1234789\u003e\n◆ ▶ values: [1, 2]        (java.util.LinkedHashMap.LinkedValues \u003c1234789\u003e)\n    ◾ not to contain: \n      ⚬ an element which needs: \n          » to be greater than: 1        (kotlin.Int \u003c1234789\u003e)\n          ❗❗ following elements were mismatched: \n             ⚬ index 1: 2        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-map-4\u003e\n\nLast but not least, you can use the non-reporting `asEntries()` function which\nturns `Expect\u003cMap\u003cK, V\u003e\u003e` into an `Expect\u003cSet\u003cMap.Entry\u003cK, V\u003e\u003e` and thus allows that you can use all the expectation \nfunctions and sophisticated builders shown in [Collection Expectations](#collection-expectations).\n\nThere should seldom be a need for it but in case you want to make also sub expectations for the key, \nthen it will come in handy:\n\n\u003cex-map-5\u003e\n\n```kotlin\nexpect(linkedMapOf(\"a\" to 1, \"b\" to 2)).asEntries().toContain.inOrder.only.entries(\n    { toEqualKeyValue(\"a\", 1) },\n    {\n        key.toStartWith(\"a\")\n        value.toBeGreaterThan(2)\n    }\n)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MapExamples.kt#L81)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-map-5)\u003c/sub\u003e\n\u003ca name=\"ex-map-5\"\u003e\u003c/a\u003e\n```text\nI expected subject: {a=1, b=2}        (java.util.LinkedHashMap \u003c1234789\u003e)\n◆ to contain only, in order: \n  ✔ ▶ element 0: a=1        (java.util.LinkedHashMap.Entry \u003c1234789\u003e)\n      ◾ ▶ key: \"a\"        \u003c1234789\u003e\n          ◾ to equal: \"a\"        \u003c1234789\u003e\n      ◾ ▶ value: 1        (kotlin.Int \u003c1234789\u003e)\n          ◾ to equal: 1        (kotlin.Int \u003c1234789\u003e)\n  ✘ ▶ element 1: b=2        (java.util.LinkedHashMap.Entry \u003c1234789\u003e)\n      ◾ ▶ key: \"b\"        \u003c1234789\u003e\n          ◾ to start with: \"a\"        \u003c1234789\u003e\n      ◾ ▶ value: 2        (kotlin.Int \u003c1234789\u003e)\n          ◾ to be greater than: 2        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-map-5\u003e\n\n`toEqualKeyValue` as well as `key` and `value` are expectation functions defined for `Map.Entry\u003cK, V\u003e`.\n\nThere are more expectation functions, a full list can be found in \n[KDoc of atrium-api-fluent](https://docs.atriumlib.org/latest#/kdoc/atrium-api-fluent).\n\n## Path Expectations\nSee also\n[PathExpectationSamples](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/PathExpectationSamples.kt)\nand\n[PathFeatureExtractorSamples](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/PathFeatureExtractorSamples.kt)\nfor further examples.\n\nAtrium’s expectation functions for paths give detailed failure hints explaining what happened on the file system.\nFor example, `toExist` will explain which entry was the first one missing:\n\n\u003cex-path-exists\u003e\n\n```kotlin\nexpect(Paths.get(\"/usr/bin/noprogram\")).toExist()\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/PathExamples.kt#L31)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-path-exists)\u003c/sub\u003e\n\u003ca name=\"ex-path-exists\"\u003e\u003c/a\u003e\n```text\nI expected subject: /usr/bin/noprogram        (sun.nio.fs.UnixPath \u003c1234789\u003e)\n◆ to: exist\n    » the closest existing parent directory is /usr/bin\n```\n\u003c/ex-path-exists\u003e\n\nAtrium will give details about why something cannot be accessed, for example when checking whether a file is writable:\n\n\u003cex-path-writable\u003e\n\n```kotlin\nexpect(Paths.get(\"/root/.ssh/config\")).toBeWritable()\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/PathExamples.kt#L36)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-path-writable)\u003c/sub\u003e\n\u003ca name=\"ex-path-writable\"\u003e\u003c/a\u003e\n```text\nI expected subject: /root/.ssh/config        (sun.nio.fs.UnixPath \u003c1234789\u003e)\n◆ to be: writable\n    » failure at parent path: /root        (sun.nio.fs.UnixPath \u003c1234789\u003e)\n      » access was denied\n      » the owner is root, the group is root\n      » the permissions are u=rwx g= o=\n```\n\u003c/ex-path-writable\u003e\n\nEven in more complicated scenarios, Atrium explains step by step what happened:\n\n\u003cex-path-symlink-and-parent-not-folder\u003e\n\n```kotlin\nval directory = Files.createDirectory(tmpDir)\nval file = Files.createFile(directory.resolve(\"file\"))\nval filePointer = Files.createSymbolicLink(directory.resolve(\"directory\"), file)\n\nexpect(filePointer.resolve(\"subfolder/file\")).toBeARegularFile()\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/PathExamples.kt#L41)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-path-symlink-and-parent-not-folder)\u003c/sub\u003e\n\u003ca name=\"ex-path-symlink-and-parent-not-folder\"\u003e\u003c/a\u003e\n```text\nI expected subject: /tmp/atrium-path/directory/subfolder/file        (sun.nio.fs.UnixPath \u003c1234789\u003e)\n◆ to be: a file\n    » followed the symbolic link /tmp/atrium-path/directory to /tmp/atrium-path/file\n    » failure at parent path: /tmp/atrium-path/file        (sun.nio.fs.UnixPath \u003c1234789\u003e)\n      » was a file instead of a directory\n```\n\u003c/ex-path-symlink-and-parent-not-folder\u003e\n\n## Attaching a Reason\n\nIn case you want to add further information to an expectation, e.g. state the reason why you expect it to hold, you can\nuse `because`:\n\n\u003cex-because-1\u003e\n\n```kotlin\nexpect(\"filename?\")\n    .because(\"? is not allowed in file names on Windows\") {\n        notToContain(\"?\")\n    }\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L96)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-because-1)\u003c/sub\u003e\n\u003ca name=\"ex-because-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: \"filename?\"        \u003c1234789\u003e\n◆ not to contain: \n  ⚬ value: \"?\"        \u003c1234789\u003e\n    ⚬ ▶ number of matches: 1\n        ◾ to equal: 0        (kotlin.Int \u003c1234789\u003e)\nℹ because: ? is not allowed in file names on Windows\n```\n\u003c/ex-because-1\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e💬 Use \u003ccode\u003ebecause\u003c/code\u003e only to give reasons for non-obvious expectations\u003c/summary\u003e\n\n`because` can be a useful tool for explaining why there is a certain expectation. \nSometimes it is not directly obvious why one should expect something. \nIn such cases, using `because` can make your code, and your error messages, easier to\nunderstand for other developers (including yourself in three months).\n\nHaving said that, you should not use `because` if you are missing a specific predefined expectation function. \nYou can use a [feature extractor](#feature-extractors), [write your own expectation function](#write-own-expectation-functions)\nor [propose an addition to Atrium](https://github.com/robstoll/atrium/issues/new?template=feature_request.md\u0026title=Missing%20Expectation%20Function)\nin such cases.\n\nFor instance, instead of the following (which can easily be out of sync):\n```kotlin\nexpect(person.name).because(\"name should be Alexander\") {\n    toEqual(\"Alex\")\n}\n```\nIt is better to use a feature extractor as follows:\n```kotlin\nexpect(person).feature(Person::name).toEqual(\"Alex\")\n```\n\nJust like code comments, `because` can be valuable, but should not be overused.\n\n\u003c/details\u003e\n\n## Integrate other Assertion/Expectation Libraries\n\nIf you are in the situation where you either want to migrate a large number of own assertion functions written for a \nthird party assertion library (e.g. AssertJ) to Atrium or where you want to integrate an assertion library into\nthe reporting of Atrium, the expectation function `toHoldThirdPartyExpectation` comes in handy.\n\nIt basically allows you to carry out any (expectation) functionality and give it a description and representation in \nreporting. The third party expectation is considered to hold if no exception is thrown and to fail otherwise.\n\nFollowing an example:\n\n\u003cex-third-party-1\u003e\n\n```kotlin\nexpect(listOf(1, 2, 3, -1)).toHaveElementsAndAll {\n    toHoldThirdPartyExpectation(\"not to be\", Text(\"negative\")) { subject -\u003e\n        // in the following we use assertJ\n        assertThat(subject).isNotNegative()\n    }\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/ThirdPartyExamples.kt#L24)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-third-party-1)\u003c/sub\u003e\n\u003ca name=\"ex-third-party-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1, 2, 3, -1]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ elements need all: \n    » not to be: negative\n    ❗❗ following elements were mismatched: \n       ⚬ index 3: -1        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-third-party-1\u003e\n\nPlease, [open a feature request](https://github.com/robstoll/atrium/issues/new?template=feature_request.md\u0026title=[Feature])\nfor features you miss in Atrium. We happily add further functionality as long as it is used by someone.\nIf you should use this third party expectation often, then it makes sense to write an own expectation function:\n\n\u003cex-third-party-2\u003e\n\n```kotlin\nfun \u003cT : Number\u003e Expect\u003cT\u003e.notToBeNegative() =\n    toHoldThirdPartyExpectation(\"not to be\", Text(\"negative\")) { subject -\u003e\n        when (subject) {\n            is Int -\u003e assertThat(subject).isNotNegative()\n            is Long -\u003e assertThat(subject).isNotNegative()\n            is Float -\u003e assertThat(subject).isNotNegative()\n            is Double -\u003e assertThat(subject).isNotNegative()\n            is BigDecimal -\u003e assertThat(subject).isNotNegative()\n            // we might lose precision with toDouble but in most cases it should be OK\n            else -\u003e assertThat(subject.toDouble()).isNotNegative()\n        }\n    }\n\nexpect(-10).notToBeNegative()\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/ThirdPartyExamples.kt#L34)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-third-party-2)\u003c/sub\u003e\n\u003ca name=\"ex-third-party-2\"\u003e\u003c/a\u003e\n```text\nI expected subject: -10        (kotlin.Int \u003c1234789\u003e)\n◆ not to be: negative\n  ℹ Properties of the unexpected AssertionError\n    » message: \"\nExpecting actual:\n  -10\nto be greater than or equal to:\n  0\n\"        \u003c1234789\u003e\n    » stacktrace: \n      ⚬ readme.examples.ThirdPartyExamples$ex-third-party-2$notToBeNegative$1.invoke(ThirdPartyExamples.kt:38)\n      ⚬ readme.examples.ThirdPartyExamples$ex-third-party-2$notToBeNegative$1.invoke(ThirdPartyExamples.kt:36)\n      ⚬ readme.examples.ThirdPartyExamples.ex_third_party_2$notToBeNegative(ThirdPartyExamples.kt:36)\n      ⚬ readme.examples.ThirdPartyExamples.ex-third-party-2(ThirdPartyExamples.kt:48)\n      ⚬ java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n      ⚬ java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n      ⚬ java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n      ⚬ java.base/java.lang.reflect.Method.invoke(Method.java:566)\n```\n\u003c/ex-third-party-2\u003e\n\nAs you can see, in case of failure we see our defined description and representation as well as properties of the \nthrown `Exception`. Of course, if we start writing that much code, it might also be easier to just migrate it to \nAtrium (and create a PR so that others benefit as well 😉):\n\n\u003cex-third-party-3\u003e\n\n```kotlin\nimport ch.tutteli.atrium.logic._logic\n\nfun \u003cT : Number\u003e Expect\u003cT\u003e.notToBeNegative() =\n    _logic.createAndAppend(\"not to be\", Text(\"negative\")) { subject -\u003e\n        when (subject) {\n            is Int -\u003e subject.sign \u003e= 0\n            is Long -\u003e subject.sign \u003e= 0\n            is Float -\u003e subject.sign \u003e= 0\n            is Double -\u003e subject.sign \u003e= 0\n            is BigDecimal -\u003e subject.signum() \u003e= 0\n            //  we might lose precision with toDouble but in most cases it should be OK\n            else -\u003e sign(subject.toDouble()) \u003e= 0\n        }\n    }\n\nexpect(-10).notToBeNegative()\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/ThirdPartyExamples.kt#L52)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-third-party-3)\u003c/sub\u003e\n\u003ca name=\"ex-third-party-3\"\u003e\u003c/a\u003e\n```text\nI expected subject: -10        (kotlin.Int \u003c1234789\u003e)\n◆ not to be: negative\n```\n\u003c/ex-third-party-3\u003e\n\n## Data Driven Testing\nSee also\n[GroupingSamples](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/GroupingSamples.kt)\nfor further examples.\n\nAtrium is not intended for data driven testing in the narrowed sense in terms that it cannot produce multiple tests.\nThis is the responsibility of your test runner.\nHowever, Atrium let you define multiple expectations within one test and reports them all if you want.\nIn this sense it can be used for data driven testing.\nThis is especially helpful in case your test runner does not support data driven testing (or other mechanisms like hierarchical or dynamic tests).\nAs an example, Atrium can help you to write data driven tests in a common module of a multiplatform-project.\n\nUse `expectGrouped` (a pre-defined expectation verb which ships along with `expect`) instead and then define multiple \n`expect` in it. Following an example:\n\n\u003cex-data-driven-1\u003e\n\n```kotlin\nfun myFun(i: Int) = (i + 97).toChar()\n\nexpectGrouped {\n    mapOf(\n        1 to 'a',\n        2 to 'c',\n        3 to 'e'\n    ).forEach { (arg, result) -\u003e\n        group(\"calling myFun with $arg\") {\n            expect(myFun(arg)).toEqual(result)\n        }\n    }\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/DataDrivenExamples.kt#L20)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-data-driven-1)\u003c/sub\u003e\n\u003ca name=\"ex-data-driven-1\"\u003e\u003c/a\u003e\n```text\nmy expectations: \n# calling myFun with 1: \n  ◆ ▶ I expected subject: 'b'\n      ◾ to equal: 'a'\n# calling myFun with 3: \n  ◆ ▶ I expected subject: 'd'\n      ◾ to equal: 'e'\n```\n\u003c/ex-data-driven-1\u003e\n\nPer default, only failing expectations are shown.\nThis is also the reason why the call of `myFun(2)` is not listed (as the result is `c` as expected).\n\n`expectGrouped` creates an ExpectGrouping-Block which is very similar to an expectation-group block \n(see [Define an expectation-group](#define-single-expectations-or-an-expectation-group)) just that you have not yet \ndefined a subject. It also specifies that all expectations specified in it are evaluated and reported together \nand this is also the reason why we see `calling myFun with 3` in the above [Output](#ex-data-driven-1) even though \ncalling it with `1` failed.\n\nPlease [create a feature request](https://github.com/robstoll/atrium/issues/new?template=feature_request.md\u0026title=[Feature])\nif you want to see a summary, meaning also successful expectations -- we happily add more functionality if it is of use for someone.\n\nFollowing another example which involves an `assertionCreator`-lambda and not only a simple `toEqual` check. \nWe are going to reuse the `myFun` from above:\n\n\u003cex-data-driven-2\u003e\n\n```kotlin\nexpectGrouped {\n    mapOf\u003cInt, ExpectationCreator\u003cChar\u003e\u003e(\n        1 to { toBeLessThan('f') },\n        2 to { toEqual('c') },\n        3 to { toBeGreaterThan('e') }\n    ).forEach { (arg, assertionCreator) -\u003e\n        group(\"calling myFun with $arg\") {\n            expect(myFun(arg), assertionCreator)\n        }\n    }\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/DataDrivenExamples.kt#L37)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-data-driven-2)\u003c/sub\u003e\n\u003ca name=\"ex-data-driven-2\"\u003e\u003c/a\u003e\n```text\nmy expectations: \n# calling myFun with 3: \n  ◆ ▶ I expected subject: 'd'\n      ◾ to be greater than: 'e'\n```\n\u003c/ex-data-driven-2\u003e\n\nThe example should be self-explanatory.\nOne detail to note though is the usage of `ExpectationCreator`.\nIt's a `typealias` for `Expect\u003cT\u003e.() -\u003e Unit` and reduces some verbosity. Its usage is of course optional.\nIn case you should run into type inference issues, then prepend your lambda with `expectLambda` \n(for instance `expectLambda { toBeLessThan('f') }`), it's a helper function which gives Kotlin an additional hint.\n\nSo far we have not shown it but you can also nest groups and even use groups within `expect`. For instance:\n\n\u003cex-data-driven-nesting\u003e\n\n```kotlin\nval x1 = 1\nval x2 = 3\nval y = 6\n\nexpectGrouped {\n    group(\"first group\") {\n        expect(x1).toEqual(2)\n        group(\"sub-group\") {\n            expect(x2).toBeGreaterThan(5)\n        }\n    }\n    group(\"second group\") {\n        expect(y) {\n            group(\"sub-group 1\") {\n                toBeGreaterThan(0)\n                toBeLessThan(5)\n            }\n            group(\"sub-group 2\") {\n                notToEqual(6)\n            }\n        }\n    }\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/DataDrivenExamples.kt#L76)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-data-driven-nesting)\u003c/sub\u003e\n\u003ca name=\"ex-data-driven-nesting\"\u003e\u003c/a\u003e\n```text\nmy expectations: \n# first group: \n  ◆ ▶ I expected subject: 1        (kotlin.Int \u003c1234789\u003e)\n      ◾ to equal: 2        (kotlin.Int \u003c1234789\u003e)\n  # sub-group: \n    ◆ ▶ I expected subject: 3        (kotlin.Int \u003c1234789\u003e)\n        ◾ to be greater than: 5        (kotlin.Int \u003c1234789\u003e)\n# second group: \n  ◆ ▶ I expected subject: 6        (kotlin.Int \u003c1234789\u003e)\n      # sub-group 1: \n        ◆ to be less than: 5        (kotlin.Int \u003c1234789\u003e)\n      # sub-group 2: \n        ◆ not to equal: 6        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-data-driven-nesting\u003e\n\nThere is another function worth mentioning here which comes in handy in data-driven testing in case the subject has a \n[nullable type]((https://kotlinlang.org/docs/reference/null-safety.html).)\n\nIf you wish to make sub expectations on the non-nullable type of the subject, then you can use\n`toEqualNullIfNullGivenElse` which accepts an `assertionCreator`-lambda or `null`.\nIt is short for `if (assertionCreatorOrNull == null) toEqual(null) else notToEqual(assertionCreatorOrNull)`. \nFollowing another fictional example which illustrates `toEqualNullIfNullGivenElse` (we are reusing `myFun` from above):\n\n\u003cex-data-driven-3\u003e\n\n```kotlin\nfun myNullableFun(i: Int) = if (i \u003e 0) i.toString() else null\n\nexpectGrouped {\n    mapOf\u003cInt, ExpectationCreator\u003cString\u003e?\u003e(\n        Int.MIN_VALUE to { toContain(\"min\") },\n        -1 to null,\n        0 to null,\n        1 to { toEqual(\"1\") },\n        2 to { toEndWith(\"2\") },\n        Int.MAX_VALUE to { toEqual(\"max\") }\n    ).forEach { (arg, assertionCreatorOrNull) -\u003e\n        group(\"calling myFun with $arg\") {\n            expect(myNullableFun(arg)).toEqualNullIfNullGivenElse(assertionCreatorOrNull)\n        }\n    }\n}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/DataDrivenExamples.kt#L56)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-data-driven-3)\u003c/sub\u003e\n\u003ca name=\"ex-data-driven-3\"\u003e\u003c/a\u003e\n```text\nmy expectations: \n# calling myFun with -2147483648: \n  ◆ ▶ I expected subject: null\n        » to contain: \n          ⚬ value: \"min\"        \u003c1234789\u003e\n              » but no match was found\n# calling myFun with 2147483647: \n  ◆ ▶ I expected subject: \"2147483647\"        \u003c1234789\u003e\n      ◾ to equal: \"max\"        \u003c1234789\u003e\n```\n\u003c/ex-data-driven-3\u003e\n\n## Further Examples\n\nAtrium supports further expectation builders (e.g, for `CharSequence`) \nas well as expectation functions which have not been shown in the examples above.\n\nTake a look at the sample files which are used i.a. in the KDOC of the corresponding expectation functions:\n\n- [Samples api-fluent common](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/)\n- [Samples api-fluent jvm](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/)\n\n+ [Samples api-infix common](https://github.com/robstoll/atrium/tree/main/apis/infix/atrium-api-infix/src/commonTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/samples/)\n+ [Samples api-infix jvm](https://github.com/robstoll/atrium/tree/main/apis/infix/atrium-api-infix/src/jvmTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/samples/)\n\n# How is Atrium different from other Expectation/Assertion Libraries\n\nThe following subsections shall give you a quick overview how Atrium differs from other assertion libraries. \n\n- [Ready to Help](#ready-to-help)\n  - [Fluent API with Code Documentation](#1-fluent-api-with-code-documentation)\n  - [Additional Information in Failure Reporting](#2-additional-information-in-failure-reporting)\n  - [Prevents you from Pitfalls](#3-prevents-you-from-pitfalls)\n- [Flexibility](#flexibility)\n- [Migration of Deprecated Functionality](#migration-of-deprecated-functionality)\n\n## Ready to Help\nAtrium is designed to help you whenever possible.\nWe think this is the biggest difference to other expectation libraries and a very handy one indeed.\n\n### 1. Fluent API with Code Documentation\nAtrium provides a fluent API where the design focus was put on the interoperability (of the API) \nwith the code completion functionality of your IDE. \nOr in other words, you can always use code completion to get direct help from your IDE.\nThis experience is improved by providing up-to-date [code documentation](#kdoc) (in form of KDoc) \nfor all expectation functions, including samples, so that you get the extra help needed.\n\n### 2. Additional Information in Failure Reporting\nAtrium adds extra information to error messages so that you get quickly a better idea of what went wrong. \nFor instance, for the following expectation (which fails):\n\n\u003cexs-add-info-1\u003e\n\n```kotlin\nexpect(listOf(1, 2, 3)).toContain.inOrder.only.values(1, 3)\n```\n\u003c/exs-add-info-1\u003e\n\nAtrium points out which `values` were found, makes an implicit expectation about the size and \nalso states which entries were additionally contained in the list:\n\n\u003cexs-add-info-1-output\u003e\n\n```text\nI expected subject: [1, 2, 3]        (java.util.Arrays.ArrayList \u003c1234789\u003e)\n◆ ▶ size: 3        (kotlin.Int \u003c1234789\u003e)\n    ◾ to equal: 2        (kotlin.Int \u003c1234789\u003e)\n◆ to contain only, in order: \n  ✔ ▶ element 0: 1        (kotlin.Int \u003c1234789\u003e)\n      ◾ to equal: 1        (kotlin.Int \u003c1234789\u003e)\n  ✘ ▶ element 1: 2        (kotlin.Int \u003c1234789\u003e)\n      ◾ to equal: 3        (kotlin.Int \u003c1234789\u003e)\n    ❗❗ additional elements detected: \n       ⚬ element 2: 3        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/exs-add-info-1-output\u003e\n\n\nLet us have a look at another example.\n\n\u003cexs-add-info-2\u003e\n\n```kotlin\nexpect(9.99f).toEqualWithErrorTolerance(10.0f, 0.01f)\n```\n\u003c/exs-add-info-2\u003e\n\nThe above expectation looks good at first sight but actually fails (at least on @robstoll's machine). \nAnd without some extra information in the output we would believe that there is actually a bug in the expectation library itself.\nBut Atrium shows where it goes wrong and even gives a possible hint:\n\n\u003cexs-add-info-2-output\u003e\n\n```text\nI expected subject: 9.99        (kotlin.Float \u003c1234789\u003e)\n◆ to equal (error ± 0.01): 10.0        (kotlin.Float \u003c1234789\u003e)\n    » failure might be due to using kotlin.Float, see exact check on the next line\n    » exact check was |9.989999771118164 - 10.0| = 0.010000228881835938 ≤ 0.009999999776482582\n```\n\u003c/exs-add-info-2-output\u003e\n\nOne last example. This time about formulating an expectation that a certain `Throwable` is thrown but\nthe expectation fails because it was the wrong one. \nAtrium comes with a very useful hint, it shows the actual exception:\n\n\u003cex-add-info-3\u003e\n\n```kotlin\nexpect {\n    try {\n        throw UnsupportedOperationException(\"not supported\")\n    } catch (t: Throwable) {\n        throw IllegalArgumentException(\"no no no...\", t)\n    }\n}.toThrow\u003cIllegalStateException\u003e { messageToContain(\"no no no\") }\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L112)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-add-info-3)\u003c/sub\u003e\n\u003ca name=\"ex-add-info-3\"\u003e\u003c/a\u003e\n```text\nI expected subject: () -\u003e kotlin.Nothing        (readme.examples.MostExamples$ex-add-info-3$1 \u003c1234789\u003e)\n◆ ▶ thrown exception when called: java.lang.IllegalArgumentException\n    ◾ to be an instance of type: IllegalStateException (java.lang.IllegalStateException)\n      » ▶ message: \n          ◾ not to equal: null but to be an instance of: String (kotlin.String) -- Class: java.lang.String\n          ◾ to contain: \n            ⚬ value: \"no no no\"        \u003c1234789\u003e\n                » but no match was found\n    ℹ Properties of the unexpected IllegalArgumentException\n      » message: \"no no no...\"        \u003c1234789\u003e\n      » stacktrace: \n        ⚬ readme.examples.MostExamples$ex-add-info-3$1.invoke(MostExamples.kt:117)\n        ⚬ readme.examples.MostExamples$ex-add-info-3$1.invoke(MostExamples.kt:113)\n        ⚬ readme.examples.MostExamples.ex-add-info-3(MostExamples.kt:148)\n        ⚬ java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n        ⚬ java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n        ⚬ java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n        ⚬ java.base/java.lang.reflect.Method.invoke(Method.java:566)\n      » cause: java.lang.UnsupportedOperationException\n          » message: \"not supported\"        \u003c1234789\u003e\n          » stacktrace: \n            ⚬ readme.examples.MostExamples$ex-add-info-3$1.invoke(MostExamples.kt:115)\n```\n\u003c/ex-add-info-3\u003e\n\n\n### 3. Prevents you from Pitfalls\nBut not enough. There are certain pitfalls when it comes to using an expectation library and Atrium tries to prevent you from those.\n\nFor instance, an overload of `toEqual` and of `notToEqual` for `BigDecimal` was introduced which are both deprecated and throw a `PleaseUseReplacementException`. \nThe reason behind it?\nIt is very likely that a user actually wants to compare that a certain `BigDecimal` is numerically (not) equal to another `BigDecimal` \nrather than including `BigDecimal.scale` in the comparison.\nAccordingly, the deprecation message of `toEqual` (`notToEqual` alike) explains the problem and suggests to either use `toEqualNumerically` or `toEqualIncludingScale`.\nAnd if the user should decide to use `toEqualIncludingScale` and at some point an expectation fails only due to the comparison of `BigDecimal.scale`\nthen Atrium reminds us of the possible pitfall. For instance:\n\n\u003cex-pitfall-1\u003e\n\n```kotlin\nexpect(BigDecimal.TEN).toEqualIncludingScale(BigDecimal(\"10.0\"))\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L123)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-pitfall-1)\u003c/sub\u003e\n\u003ca name=\"ex-pitfall-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: 10        (java.math.BigDecimal \u003c1234789\u003e)\n◆ is equal (including scale): 10.0        (java.math.BigDecimal \u003c1234789\u003e)\n    💡 notice, if you used toEqualNumerically then the expectation would have been met.\n```\n\u003c/ex-pitfall-1\u003e\n\nAnother example are empty `assertionCreator`-lambdas. \nGetting distracted by a working colleague and taking up the work at the wrong position might sound familiar to you. \nFor instance:\n\n\u003cex-pitfall-2\u003e\n\n```kotlin\nexpect(listOf(1)).get(0) {}\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/MostExamples.kt#L127)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-pitfall-2)\u003c/sub\u003e\n\u003ca name=\"ex-pitfall-2\"\u003e\u003c/a\u003e\n```text\nI expected subject: [1]        (java.util.Collections.SingletonList \u003c1234789\u003e)\n◆ ▶ get(0): 1        (kotlin.Int \u003c1234789\u003e)\n    ◾ at least one expectation defined: false\n        » You forgot to define expectations in the assertionCreator-lambda\n        » Sometimes you can use an alternative to `{ }` For instance, instead of `toThrow\u003c..\u003e { }` you should use `toThrow\u003c..\u003e()`\n```\n\u003c/ex-pitfall-2\u003e\n\n## Flexibility\nAnother design goal of Atrium was to give you the flexibility needed but still adhere to a concise design. \nFirst and most importantly, Atrium does not enforce a certain style on your code base. \nQuite the contrary, it gives you the flexibility to [choose a desired name for the expectation verb](#use-own-expectation-verb), \nit continues by providing the possibility to configure the [reporting style](#use-own-components), \ngoes on that you can choose from different [API Styles](#api-styles) \nand ends that you can [replace almost all components](#use-own-components) by other implementations and hook into existing.\n\nSo for instance, if you like to use an `infix` API, then use the bundle `atrium-infix`. \nYou prefer pure fluent and do not even want to see infix style in your code, \nthen use `atrium-fluent` which provides a pure fluent style API. \n\nYou are free to choose what fits best without introducing ambiguity etc.\nYou could even mix up different API-styles if needed (but not without losing conciseness -- but hey, it is your decision 😉). \n\n## Migration of Deprecated Functionality\nAtrium follows [Semantic Versioning](https://semver.org/) and tries to be binary backward compatible within a major version (since 0.6.0).\nUntil 2.0.0 this is only true for the API level, we reserve the right to break things on the logic and core level until then.\nMoreover, we follow the principle that a user of Atrium has enough time to migrate its code to new functionality before a next major version.\nWe provide this in form of `@Deprecated` annotations with a corresponding `ReplaceWith` \nas well as migration guides in the [Release Notes](https://github.com/robstoll/atrium/releases).\nThis way we hope that we provide a pleasant way to stay up-to-date without the need to migrate everything from one day to the other.\n\n# Write own Expectation Functions\n\nAre you missing an expectation function for a specific type and the generic \n[Feature Extractors](#feature-extractors) are not good enough?\n\nThe following subsections will show how you can write your own expectation functions. \nA pull request of your new expectation function is very much appreciated.\n\n## Boolean based Expectation Functions\n\nThis is kind of the simplest way of defining expectation functions. Following an example:\n\n\u003ccode-own-boolean-1\u003e\n\n```kotlin\nimport ch.tutteli.atrium.logic._logic\n\nfun Expect\u003cInt\u003e.toBeAMultipleOf(base: Int) =\n    _logic.createAndAppend(\"is multiple of\", base) { it % base == 0 }\n```\n\u003c/code-own-boolean-1\u003e\n\nand its usage:\n\n\u003cex-own-boolean-1\u003e\n\n```kotlin\nexpect(12).toBeAMultipleOf(5)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/OwnExpectationFunctions.kt#L33)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-own-boolean-1)\u003c/sub\u003e\n\u003ca name=\"ex-own-boolean-1\"\u003e\u003c/a\u003e\n```text\nI expected subject: 12        (kotlin.Int \u003c1234789\u003e)\n◆ is multiple of: 5        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-own-boolean-1\u003e\n\nLet us see how we actually defined `toBeAMultipleOf`. \n1. *Choose the extension point*: in our example we want to provide the expectation function for `Int`s. \n    Hence, we define `toBeAMultipleOf` as [extension function](https://kotlinlang.org/docs/reference/extensions.html) of `Expect\u003cInt\u003e`.\n\n2. *Use the method `_logic.createAndAppend`* which creates and appends \n    the expectation to itself (creating alone is not enough, it needs to be appended in order that it is evaluated). \n    The method `createAndAppend` returns an `Expect` for the current subject, making it easy for you to provide a fluent API as well.\n \n    The method [createAndAppend](https://docs.atriumlib.org/latest#/kdoc/atrium-core/ch.tutteli.atrium.creating/-assertion-container/create-and-append.html)\n    expects:\n    - a `String` as description of your expectation.\n    - the representation of the expected value.\n    - and the actual check as lambda where you typically use `it` which refers to the subject of the expectation.\n     \nIn most cases you probably use the expected value itself as its representation -- so you pass it as second argument.\nAnd finally you specify the test as such in the lambda passed as third argument.\n\nBut not all expectation functions require a value which is somehow compared against the subject \n-- some state an expectation about a characteristic of the subject without comparing it against an expected value.\nConsider the following expectation function:\n\n\u003ccode-own-boolean-2\u003e\n\n```kotlin\nimport ch.tutteli.atrium.logic._logic\n\nfun Expect\u003cInt\u003e.toBeEven() =\n    _logic.createAndAppend(\"is\", Text(\"an even number\")) { it % 2 == 0 }\n```\n\u003c/code-own-boolean-2\u003e\n\nWe are using a [Text](https://docs.atriumlib.org/latest#/kdoc/atrium-core/ch.tutteli.atrium.reporting/-text/index.html) as \nrepresentation so that `\"an even number\"` is not treated as a `String` in reporting.\nIts usage looks then as follows:\n\n\u003cex-own-boolean-2\u003e\n\n```kotlin\nexpect(13).toBeEven()\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/OwnExpectationFunctions.kt#L48)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-own-boolean-2)\u003c/sub\u003e\n\u003ca name=\"ex-own-boolean-2\"\u003e\u003c/a\u003e\n```text\nI expected subject: 13        (kotlin.Int \u003c1234789\u003e)\n◆ is: an even number\n```\n\u003c/ex-own-boolean-2\u003e\n\n## Throwable based expectation functions\n\nYou might already implement functions (e.g. in your business code) which check/validate certain things and throw if \nthe expectations are not met. If those functions are well tested by itself, then they can very well also act as \nexpectation functions in other tests. To integrate those functions into the reporting of Atrium you can use\n`toHoldThirdPartyExpectation`. Take a look at [Integrate other Assertion/Expectation Libraries](#integrate-other-assertionexpectation-libraries)\nfor a first explanation. Assuming, that are you going to use your existing functionality more than once in tests, \nan expectation function could look as follows:\n\n\u003cex-third-party-10\u003e\n\n```kotlin\nfun Expect\u003cMyDomainModel\u003e.toComplyValidation() =\n    toHoldThirdPartyExpectation(\"to comply\", Text(\"validation\")) { subject -\u003e\n        subject.validateMinThreshold()\n        subject.validateMaxThreshold()\n        //...\n    }\n\nexpect(MyDomainModel(alpha1 = 1204)).toComplyValidation()\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/ThirdPartyExamples.kt#L79)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-third-party-10)\u003c/sub\u003e\n\u003ca name=\"ex-third-party-10\"\u003e\u003c/a\u003e\n```text\nI expected subject: MyDomainModel(alpha1=1204)        (readme.examples.ThirdPartyExamples.MyDomainModel \u003c1234789\u003e)\n◆ to comply: validation\n  ℹ Properties of the unexpected IllegalStateException\n    » message: \"threshold value for alpha1 exceeded, expected \u003c= 1000, was 1204\"        \u003c1234789\u003e\n    » stacktrace: \n      ⚬ readme.examples.ThirdPartyExamples.validateMaxThreshold(ThirdPartyExamples.kt:75)\n      ⚬ readme.examples.ThirdPartyExamples$ex-third-party-10$toComplyValidation$1.invoke(ThirdPartyExamples.kt:83)\n      ⚬ readme.examples.ThirdPartyExamples$ex-third-party-10$toComplyValidation$1.invoke(ThirdPartyExamples.kt:81)\n      ⚬ readme.examples.ThirdPartyExamples.ex_third_party_10$toComplyValidation(ThirdPartyExamples.kt:81)\n      ⚬ readme.examples.ThirdPartyExamples.ex-third-party-10(ThirdPartyExamples.kt:87)\n      ⚬ java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n      ⚬ java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n      ⚬ java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n      ⚬ java.base/java.lang.reflect.Method.invoke(Method.java:566)\n```\n\u003c/ex-third-party-10\u003e\n\n## Compose Expectation Functions\n\nSo far, we core contributors ran quickly into the situation where we wanted to compose functions or\nreuse existing functions but with different arguments. \nWe will show both use cases here, starting off by composing functions. \n\nSay you want to build a `toBeBetween` expectation function for `java.util.Date`, you could write it as follows:\n\n\u003ccode-own-compose-1\u003e\n\n```kotlin\nfun \u003cT : Date\u003e Expect\u003cT\u003e.toBeBetween(lowerBoundInclusive: T, upperBoundExclusive: T) =\n    and {\n        toBeGreaterThanOrEqualTo(lowerBoundInclusive)\n        toBeLessThan(upperBoundExclusive)\n    }\n```\n\u003c/code-own-compose-1\u003e\n\nPretty simple, isn't it?\nNote, using `and {...}` creates an [expectation group-block](#define-single-expectations-or-an-expectation-group) \nand therefore both `toBeGreaterThanOrEqualTo` and `toBeLessThan` are evaluated and reported.\nIf wou prefer a fail-fast behaviour then you could write it as follows but from our experience more context in error\nmessages ways more than a tiny bit faster test execution stop: \n```kotlin\ntoBeGreaterThanOrEqualTo(lowerBoundInclusive).and.toBeLessThan(upperBoundExclusive)\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e💬 Why is a type parameter used in the above examples?\u003c/summary\u003e\n\nThat is right, we used a type parameter `T: Date` and not `Expect\u003cDate\u003e` directly. \nYou should always do this unless your type is final (not `open`) and does not have type parameters itself - but to have a simple rule, just do it. \nThis way the expectation function is also available for subtypes. This is because `Expect` is [invariant](https://kotlinlang.org/docs/reference/generics.html#variance). \nFollowing an example:\n```kotlin\ninterface A { val foo get() = 1 }\nclass B: A\nval Expect\u003cA\u003e.foo get() = feature(A::foo)\n\nexpect(B()).foo // does not compile as foo is only available for `Expect\u003cA\u003e`\n```\n    \n\u003c/details\u003e\n\n\nSo let's move on to an example which is a bit more complicated. Assume the following data class `Person`\n\n\u003ccode-own-compose-3a\u003e\n\n```kotlin\ndata class Person(\n    val firstName: String,\n    val lastName: String,\n    val age: Int,\n    val children: Collection\u003cPerson\u003e\n    // ...  and others\n)\n```\n\u003c/code-own-compose-3a\u003e\n\nSay you want to postulate an expectation about the number of children a person has:\n\n\u003ccode-own-compose-3b\u003e\n\n```kotlin\nfun Expect\u003cPerson\u003e.toHaveNumberOfChildren(number: Int): Expect\u003cPerson\u003e =\n    feature(Person::children) { toHaveSize(number) }\n\n```\n\u003c/code-own-compose-3b\u003e\n\nThree things to notice here: \n1. we make use of a [feature extractor with class reference](#within-expectation-functions--feature-extractors).\n2. We use the overload which expects an `assertionCreator`-lambda. This way subsequent expectations are still made on `Person` and not on `children`.\n3. We have not used a type parameter in contrast to the previous example, because Person is final and doesn't have type\n   parameters by its own. If it were open, we would again use `fun \u003cT: Person\u003e Expect\u003cT\u003e.toHaveNumberOfChildren` so\n   that this expectation function is also available on subtypes of Person.\n \nIts usage is then as follows:\n\n\u003cex-own-compose-3\u003e\n\n```kotlin\nexpect(Person(\"Susanne\", \"Whitley\", 43, emptyList()))\n    .toHaveNumberOfChildren(2)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/OwnExpectationFunctions.kt#L67)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-own-compose-3)\u003c/sub\u003e\n\u003ca name=\"ex-own-compose-3\"\u003e\u003c/a\u003e\n```text\nI expected subject: Person(firstName=Susanne, lastName=Whitley, age=43, children=[])        (readme.examples.Person \u003c1234789\u003e)\n◆ ▶ children: []        (kotlin.collections.EmptyList \u003c1234789\u003e)\n    ◾ ▶ size: 0        (kotlin.Int \u003c1234789\u003e)\n        ◾ to equal: 2        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-own-compose-3\u003e\n\nAnother example: expect the person to have children which are all adults (assuming 18 is the age of majority).\n\n\u003ccode-own-compose-4\u003e\n\n```kotlin\nfun Expect\u003cPerson\u003e.toHaveAdultChildren(): Expect\u003cPerson\u003e =\n    feature(Person::children) {\n        toHaveElementsAndAll {\n            feature(Person::age).toBeGreaterThanOrEqualTo(18)\n        }\n    }\n\n```\n\u003c/code-own-compose-4\u003e\n\nWe once again use `feature` with an [expectation-group](#define-single-expectations-or-an-expectation-group) \nfor the same reason as above.\nNote how `toHaveElementsAndAll` already checks that there is at least one element. \nI.e. it fails for a `Person` with 0 children, because such a person does not have adult children. \n\n\u003cex-own-compose-4\u003e\n\n```kotlin\nexpect(Person(\"Susanne\", \"Whitley\", 43, emptyList()))\n    .toHaveAdultChildren()\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/OwnExpectationFunctions.kt#L86)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-own-compose-4)\u003c/sub\u003e\n\u003ca name=\"ex-own-compose-4\"\u003e\u003c/a\u003e\n```text\nI expected subject: Person(firstName=Susanne, lastName=Whitley, age=43, children=[])        (readme.examples.Person \u003c1234789\u003e)\n◆ ▶ children: []        (kotlin.collections.EmptyList \u003c1234789\u003e)\n    ◾ to have: a next element\n      » elements need all: \n          » ▶ age: \n              ◾ to be greater than or equal to: 18        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-own-compose-4\u003e\n\nIf we keep adding expectation functions involving `children` it might be best to provide a shortcut property and function.\n\n\u003ccode-own-compose-5\u003e\n\n```kotlin\nval Expect\u003cPerson\u003e.children: Expect\u003cCollection\u003cPerson\u003e\u003e get() = feature(Person::children)\n\nfun Expect\u003cPerson\u003e.children(assertionCreator: Expect\u003cCollection\u003cPerson\u003e\u003e.() -\u003e Unit): Expect\u003cPerson\u003e =\n    feature(Person::children, assertionCreator)\n```\n\u003c/code-own-compose-5\u003e\n\nNotice, that we have used a class-reference and not a bounded-reference to refer to `children` which is best practice \n(see [feature extractor within expectation functions]( #within-expectation-functions--feature-extractors)).\nWith this, we can write things like:\n\n\u003cex-own-compose-5\u003e\n\n```kotlin\nexpect(Person(\"Susanne\", \"Whitley\", 43, listOf(Person(\"Petra\", \"Whitley\", 12, emptyList()))))\n    .children { // using the fun -\u003e expectation-group, ergo sub expectations don't fail fast\n        toHaveElementsAndNone {\n            feature { f(it::firstName) }.toStartWith(\"Ro\")\n        }\n        toHaveElementsAndAll {\n            feature { f(it::lastName) }.toEqual(\"Whitley\")\n        }\n    } // subject is still Person here\n    .children  // using the val -\u003e subsequent expectations are about children and fail fast\n        .toHaveSize(2)\n        .toHaveElementsAndAny {\n            feature { f(it::age) }.toBeGreaterThan(18)\n        }\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/OwnExpectationFunctions.kt#L97)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-own-compose-5)\u003c/sub\u003e\n\u003ca name=\"ex-own-compose-5\"\u003e\u003c/a\u003e\n```text\nI expected subject: Person(firstName=Susanne, lastName=Whitley, age=43, children=[Person(firstName=Petra, lastName=Whitley, age=12, children=[])])        (readme.examples.Person \u003c1234789\u003e)\n◆ ▶ children: [Person(firstName=Petra, lastName=Whitley, age=12, children=[])]        (java.util.Collections.SingletonList \u003c1234789\u003e)\n    ◾ ▶ size: 1        (kotlin.Int \u003c1234789\u003e)\n        ◾ to equal: 2        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-own-compose-5\u003e\n\n\u003chr/\u003e\n\nEnough of expectation functions for features. Let's move on to an example where we want to reuse an existing function \nbut with different arguments. Say we have a function which returns a list of first name / last name `Pair`s. \nWe want to assert that the pairs contain only the first name / last name pairs of certain `Person`s in any order.\n[Collection Expectations](#collection-expectations) will help us with this. \nHowever, `toContain.inAnyOrder.values` expects `Pair`s.\nSo we have to map from `Person` to `Pair` upfront.\nAs we have a variable length argument list and want to pass it to a variable length argument list, this cannot be done with a simple `map` from Kotlin. \nAnd it gets worse if we want to use `toContain.inAnyOrder.entries` which expects at least one `assertionCreator`-lambda (`Expect\u003cT\u003e.() -\u003e Unit`)\nbecause Kotlin cannot infer the types automatically.\n\n`mapArguments` to the rescue, you can write the expectation function as follows:\n\n\u003ccode-own-compose-6\u003e\n\n```kotlin\nimport ch.tutteli.atrium.logic.utils.mapArguments\n\nfun \u003cT : List\u003cPair\u003cString, String\u003e\u003e\u003e Expect\u003cT\u003e.areNamesOf(\n    person: Person, vararg otherPersons: Person\n): Expect\u003cT\u003e {\n    val (pair, otherPairs) = mapArguments(person, otherPersons) { it.firstName to it.lastName }\n    return toContain.inAnyOrder.only.values(pair, *otherPairs)\n}\n```\n\u003c/code-own-compose-6\u003e\n\nAs you can see we moved the mapping inside the function so that the consumer of our API can happily use it as follows:\n```kotlin\nexpect(get...WhichReturnsPairs()).areNamesOf(fKafka, eBloch, kTucholsky)\n```\n\nAnother fictional example, say we expect that the pairs have the same initials as the given persons and in the given order.\nWhich means, this time we need to use `assertionCreator`-lambdas. This can be written as follows:\n\n\u003ccode-own-compose-7\u003e\n\n```kotlin\nfun \u003cT : List\u003cPair\u003cString, String\u003e\u003e\u003e Expect\u003cT\u003e.sameInitialsAs(\n    person: Person, vararg otherPersons: Person\n): Expect\u003cT\u003e {\n    val (first, others) = mapArguments(person, otherPersons).toExpect\u003cPair\u003cString, String\u003e\u003e {\n        first.toStartWith(it.firstName[0].toString())\n        second.toStartWith(it.lastName[0].toString())\n    }\n    return toContain.inOrder.only.entries(first, *others)\n}\n```\n\u003c/code-own-compose-7\u003e\n\nThere are a few additional methods which you can call after `mapArguments`.\nSee [KDoc of ArgumentMapperBuilder](https://docs.atriumlib.org/latest#/kdoc/atrium-logic/ch.tutteli.atrium.logic.utils/-argument-mapper-builder/index.html).\nIn case you want to provide your own implementation, \nit suffices to create an extension function for `ArgumentMapperBuilder`. \n\n## Enhanced Reporting\n\n[Composing expectation functions](#compose-expectation-functions) gives already quite a bit of power to an expectation function writer.\nYet, sometimes we would like to create functions which have a better error reporting than the one we get \nwhen we compose expectation functions.\n\n[`_logic`](https://github.com/robstoll/atrium/tree/main/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/logic.kt#L23) \nis the entry point to `AssertionContainer` which is the equivalent of `Expect` but on a lower level.\n\nFollowing a quick overview what extension methods could be useful:\n- all expectation functions on the logic level (what you have seen in [Compose expectation functions](#compose-expectation-functions) \nwas the API level) so that you can reuse and compose them in other ways.\n- `changeSubject` which allows to change the subject either:\n   - `unreported`; meaning it does not show up in reporting (e.g. `Expect\u003cArray\u003cout T\u003e\u003e.asList()` uses it, see [arrayAssertions](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/arraySubjectChangers.kt#L20))\n   - reported, using `reportBuilder`; meaning a subject transformation which is shown in reporting as it incorporates a transformation (e.g. `toBeAnInstanceOf` uses it, see [AnyAssertions](https://github.com/robstoll/atrium/tree/main/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt#L70))\n- `collect` which allows to collect expectations - especially helpful in composing expectations (see [mapEntryAssertions -\u003e isKeyValue](https://github.com/robstoll/atrium/tree/main/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/impl/DefaultMapEntryAssertions.kt#L11))\n- `extractFeature` for feature extraction where it is not always save to extract (see [`List.get`](https://github.com/robstoll/atrium/tree/main/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/impl/DefaultListAssertions.kt#L13))   \n\nBesides, the `assertionBuilder` allows to create different kinds of Assertions \n(see [AssertionBuilder](https://docs.atriumlib.org/latest#/kdoc/atrium-core/ch.tutteli.atrium.assertions.builders/-assertion-builder/index.html) for more information)\nwhich can be used to create very specific expectation functions. \n\nYou can find an example in [floatingPointAssertions](https://github.com/robstoll/atrium/tree/main/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultFloatingPointAssertions.kt#L72)\nwhich makes use of explanatory assertions as well as providing a failure hint.\n\nUnfortunately we do not have the time to cover all cases, so let us know if you want to know more\n-- either by opening an issue or via the [atrium Slack channel](https://kotlinlang.slack.com/messages/C887ZKGCQ)\n([Invite yourself](https://slack.kotlinlang.org/)).\n\n## Own Sophisticated Expectation Builders\n\nDo you want to write an own sophisticated expectation builder (or extend a current one with more options) instead of an expectation function?\nGreat, we do not provide documentation yet (had only one question about it since 2017). \n\nWe are willing to provide more documentation if you need it (please open an issue). \nIn the meantime we might help you via slack, please post your questions in the [atrium Slack channel](https://kotlinlang.slack.com/messages/C887ZKGCQ)\n([Invite yourself](https://slack.kotlinlang.org/) in case you do not have an account yet).\n\n# Use own Expectation Verb\n\nAtrium offers the expectation verbs `expect` and `expectGrouped` out of the box. \n\nYou can also define your own expectation verb if the pre-defined verbs do not suite you or \nin case you want to change some default implementation.\nIn order to create an own expectation verb it is sufficient to:\n 1. Copy the file content of [atriumVerbs.kt](https://github.com/robstoll/atrium/tree/main/misc/atrium-verbs-internal/src/commonMain/kotlin/ch.tutteli.atrium.api.verbs.internal/atriumVerbs.kt)\n 2. Create your own atriumVerbs.kt and paste the previously copied content\n 3. Adjust package name and `import`s and rename `expect`/`expectGrouped` as desired (you can also leave it that way of course).\n 4. exclude `atrium-verbs` from your dependencies. \n    Taking the setup shown in the [Installation](#installation) section for the JVM platform, you would replace the `dependencies` block as follows:\n    ```kotlin\n    dependencies {\n        testImplementation(\"ch.tutteli.atrium:atrium-fluent:1.2.0\") {\n            exclude(group=\"ch.tutteli.atrium\", module=\"atrium-verbs\")\n        }\n    }\n    ```\n\nWhat are the benefits of creating an own expectation verb:\n- you can encapsulate the reporting style. \u003cbr/\u003e\n  This is especially useful if you have multiple projects and want to have a consistent reporting style.  \n  For instance, you could change from same-line to multi-line reporting or report not only failing but also successful expectations etc.\n  \n    \u003cdetails\u003e\n    \u003csummary\u003e💬 where should I put the atriumVerbs.kt?\u003c/summary\u003e\n    \n    We suggest you create an adapter project for Atrium where you specify the expectation verb. \n    And most likely you will accumulate them with expectation functions which are so common,\n    that they appear in multiple of your projects -- please share them with us \n    (get in touch with us via issue/discussion/slack if you need help) if they are not of an internal nature 😉\n    \n    \u003chr/\u003e\n    \u003c/details\u003e\n\n \nWhat are the drawbacks:\n- you have to maintain your expectation verb. That should not be a big deal though\n  -- you might have to replace deprecated options by their replacement when you upgrade to a newer Atrium version but that's about it.\n\n## Use own Components\n\nReplacing existing components with your own (or third-party) components can be done when specifying an own expectation verb\nvia `withOptions`. See for instance [atriumVerbs.kt](https://github.com/robstoll/atrium/tree/main/misc/atrium-verbs-internal/src/commonMain/kotlin/ch.tutteli.atrium.api.verbs.internal/atriumVerbs.kt#L29)\nwhich is used internally of Atrium in tests and uses a different `AtriumErrorAdjuster`.\n\nAnother example, say you prefer multi-line reporting over single-line reporting,\nthen you can use `withOptions` as follows:\n\n\u003ccode-own-expectation-verb\u003e\n\n```kotlin\nimport ch.tutteli.atrium.core.ExperimentalNewExpectTypes\nimport ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer\nimport ch.tutteli.atrium.creating.build\n\n@OptIn(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)\nfun \u003cT\u003e expect(subject: T): RootExpect\u003cT\u003e =\n    RootExpectBuilder.forSubject(subject)\n        .withVerb(\"expected the subject\")\n        .withOptions {\n            withComponent(TextAssertionPairFormatter::class) { c -\u003e\n                TextAssertionPairFormatter.newNextLine(c.build(), c.build())\n            }\n        }\n        .build()\n```\n\u003c/code-own-expectation-verb\u003e\n\nFollowing an example using the expectation verb\n\n\u003cex-own-expectation-verb\u003e\n\n```kotlin\nexpect(10).toEqual(9)\n```\n↑ \u003csub\u003e[Example](https://github.com/robstoll/atrium/tree/main/misc/tools/readme-examples/src/test/kotlin/readme/examples/OwnExpectationVerb.kt#L40)\u003c/sub\u003e ↓ \u003csub\u003e[Output](#ex-own-expectation-verb)\u003c/sub\u003e\n\u003ca name=\"ex-own-expectation-verb\"\u003e\u003c/a\u003e\n```text\nexpected the subject:\n  10        (kotlin.Int \u003c1234789\u003e)\n◆ to equal:\n  9        (kotlin.Int \u003c1234789\u003e)\n```\n\u003c/ex-own-expectation-verb\u003e\n\nCompare the above output with what we would get per default:\n```\nexpected the subject: 10        (kotlin.Int \u003c1234789\u003e)\n◆ to be: 9        (kotlin.Int \u003c1234789\u003e)\n```\n\nYou prefer another reporting style but Atrium does not yet support it? \nPlease let us know it by [writing a feature request](https://github.com/robstoll/atrium/issues/new?template=feature_request.md\u0026title=[Feature]).\n\nThere are more options to choose from. Take a look at the \n[DefaultComponentFactoryContainer](https://github.com/robstoll/atrium/tree/main/atrium-core/src/commonMain/kotlin/ch/tutteli/atrium/creating/impl/ComponentFactoryContainerImpl.kt#L118)\nto see the default configuration.\n\n\n\n\u003ca name=\"apis\"\u003e\u003c/a\u003e\n# API Styles\nAtrium supports currently two API styles: pure `fluent` and `infix`.\nBoth have their design focus on interoperability with code completion functionality of your IDE \n-- so that you can let your IDE do some of the work.\n\nAtrium is \n[built up by different modules](https://docs.atriumlib.org/latest#/kdoc/) \nand it is your choice which implementation you want to use. \nHowever, this is more intended for advanced user with special requirements.\nAtrium provides bundle modules which bundle API, logic, core, translation as well as predefined expectation verbs,\nso that you just have to have a dependency on one of those bundles:\n\n- [atrium-fluent](https://github.com/robstoll/atrium/tree/main/bundles/fluent/atrium-fluent/build.gradle.kts)\n- [atrium-infix](https://github.com/robstoll/atrium/tree/main/bundles/infix/atrium-infix/build.gradle.kts)\n\nHave a look at \n[apis/differences.md](https://github.com/robstoll/atrium/tree/main/apis/differences.md)\nfor more information and to see how the API styles differ.\n\n# Java Interoperability\nAtrium provides some helper functions in case you have to deal with Java Code where not all types are non-nullable. \n[Platform types](https://kotlinlang.org/docs/java-interop.html#null-safety-and-platform-types)\nare turned into a non-nullable version per default (if possible). \n\nYet, that might not be what you want, especially if you know that certain functions return potentially `null` \nor in other words, the return type of those functions should be treated as nullable in Kotlin. \nTherefore, you want to turn the platform type into the nullable version. \n\nYou need to use a cast to do this. But depending on your return type this might be cumbersome especially if you deal with type parameters. \nThus, Atrium provides the following functions to ease dealing with Java Code at least for some standard cases:\n- [`nullable`](https://github.com/robstoll/atrium/tree/main/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/utils/nullable.kt#L189)\n  turns a type into a nullable type and a return type of a KFunction into a nullable type.\n- [`nullableContainer`](https://github.com/robstoll/atrium/tree/main/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/utils/nullable.kt#L30)\n  turns an `Iterable` into an iterable with nullable element type, likewise it does the same for `Array`.\n- [`nullableKeyMap`](https://github.com/robstoll/atrium/tree/main/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/utils/nullable.kt#L56)\n  turns a `Map` into a map with a nullable key type.\n- [`nullableValueMap`](https://github.com/robstoll/atrium/tree/main/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/utils/nullable.kt#L69)\n  turns a `Map` into a map with a nullable value type.\n- [`nullableKeyValueMap`](https://github.com/robstoll/atrium/tree/main/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/utils/nullable.kt#L82)\n  turns a `Map` into a map with a nullable key and nullable value type. \n    \n \n# KDoc - Code Documentation\nThe code documentation is generated with dokka and is hosted on github-pages:\n[KDoc of atrium](https://docs.atriumlib.org/)\n\n# FAQ\nYou find frequently asked questions below.\nIf your question is not answered below, then please do not hesitate to open a new discussion in \n[Q\u0026A](https://github.com/robstoll/atrium/discussions/new?category=q-a) or in the [atrium Slack channel](https://kotlinlang.slack.com/messages/C887ZKGCQ).\nIn case you do not have an account for kotlinlang.slack.com yet, then please [Invite yourself](https://slack.kotlinlang.org/). \n\n## My expectation function is not available for subtypes\n\nSay you have defined the following\n```kotlin\ninterface Foo\nclass Bar: Foo\nfun Expect\u003cFoo\u003e.toHaveWings() = ...\nfun test() = expect(Bar()).toHaveWings()\n//                          | compile error wrong receiver: Foo expected Bar given\n```\n`Expect` is [invariant](https://kotlinlang.org/docs/reference/generics.html#variance) which means, if you define\n`Expect\u003cFoo\u003e` then it is only available for `Foo` and not for subtypes. You need to use a type parameter instead and\nuse an upper bound to restrict the subject type:\n```kotlin\nfun \u003cT: Foo\u003e Expect\u003cT\u003e.toHaveWings() = ...\n```\nNow, you can use `toHaveWings` also on subtypes of `Foo`.\n\nIn general, you should always use the type parameter approach, the only exception is if you deal with final classes\n(e.g. data classes) which don't have a type parameter itself. In such a case there is no benefit to have a type\nparameter but on the other hand, it also doesn't hurt -- less to think about 😉 (your IDE might warn you that it is not necessary though).\n\n## I have problems in conjunction with `feature`\n\nSee [Ambiguity Problems](#ambiguity-problems) and [Property does not exist](#property-does-not-exist).\n\n\n## Does Atrium provide something like AssertJ's soft assertion?\nOf course and even more powerful yet less cumbersome to write in our opinion.\nCheck out the [comparison of expectation-groups with AssertJ's soft assertions](#expecation-groups-are-better-soft-assertions).\n\n## Are there toContain/toHaveElementsAndAll/None/Any expectation functions for `Sequence`/`Array`?\n\nAtrium does not provide extension functions applicable to `Expect\u003cSequence\u003cE\u003e\u003e` (or `Array`) directly,\nbecause they would basically duplicate the functions available for `Expect\u003cIterable\u003cE\u003e\u003e`.  \nHowever, Atrium provides subject changer functions: `asIterable` and `asList` so that you can turn an `Expect\u003cSequence\u003cE\u003e\u003e` \ninto an `Expect\u003cIterable\u003cE\u003e\u003e`, `Expect\u003cList\u003cE\u003e\u003e` respectively. An example:\n\n\u003ccode-faq-1\u003e\n\n```kotlin\nexpect(sequenceOf(1, 2, 3)).asIterable().toContain(2)\n```\n\u003c/code-faq-1\u003e\n\nLikewise, you can turn an `Expect\u003cArray\u003cE\u003e\u003e`, `Expect\u003cDoubleArray\u003e` etc. into an `Expect\u003cList\u003cE\u003e\u003e` with `asList`.\n\nSee [ArraySubjectChangerSamples](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/ArraySubjectChangerSamples.kt)\nand [SequenceSubjectChangerSamples](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SequenceSubjectChangerSamples.kt).\n\nFeel free vote for [first class support for Array and Sequence in api-fluent](https://github.com/robstoll/atrium/issues/459).\n\n\u003cdetails\u003e\n\u003csummary\u003e💬 why do I not see anything about the transformation in reporting?\u003c/summary\u003e\n\n`asIterable` uses `_logic.changeSubject.unreported` internally which is intended for not showing up in reporting.\nIf you would like that the transformation is reflected in reporting then you can use a regular feature extractor \nas follows:\n\n\u003ccode-faq-2\u003e\n\n```kotlin\nexpect(sequenceOf(1, 2, 3)).feature { f(it::asIterable) }.toContain(2)\n```\n\u003c/code-faq-2\u003e\n\n\u003c/details\u003e\n\n\n## Where are the expectation functions for java.io.File?\n\nAtrium does not provide extension functions applicable to `Expect\u003cFile\u003e` directly,\nbecause they would basically duplicate the functions available for `Expect\u003cPath\u003e`.\nHowever, Atrium provides the subject changer `asPath` so that you can turn an `Expect\u003cFile\u003e`\ninto an `Expect\u003cPath\u003e`. \nSee [FileSubjectChangerSamples](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/FileSubjectChangerSamples.kt).\n\n## Where are the expectation functions for java.util.Date?\n\nAtrium does not provide extension functions applicable to `Expect\u003cDate\u003e`/`Expect\u003cDateTime\u003e` directly,\nbecause they would basically duplicate the functions available for `Expect\u003cLocalDate\u003e`/`Expect\u003cLocalDateTime\u003e`.\nHowever, Atrium provides the subject changer functions: `asLocalDate` and `asLocalDateTime` so that you can turn an `Expect\u003cDate\u003e`\neither into an `Expect\u003cLocalDate\u003e` or `Expect\u003cLocalDateTime\u003e`.\n\nSee [DateSubjectChangerSamples](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/DateSubjectChangerSamples.kt).\n\n## Where do I find a list of all available functions?\n\nAtrium provides KDoc for all APIs - have a look at their KDoc:\n- [atrium-api-fluent](https://docs.atriumlib.org/latest#/kdoc/atrium-api-fluent)\n- [atrium-api-infix](https://docs.atriumlib.org/latest#/kdoc/atrium-api-infix)\n\nA good alternative is to have a look at the sample files:\n- [Samples api-fluent common](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/)\n- [Samples api-fluent jvm](https://github.com/robstoll/atrium/tree/main/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/)\n\n+ [Samples api-infix common](https://github.com/robstoll/atrium/tree/main/apis/infix/atrium-api-infix/src/commonTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/samples/)\n+ [Samples api-infix jvm](https://github.com/robstoll/atrium/tree/main/apis/infix/atrium-api-infix/src/jvmTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/samples/)\n\n# Roadmap\n\nThe roadmap is maintained at [atrium-roadmap](https://github.com/robstoll/atrium-roadmap).\nThe milestones give you an overview of the planned (breaking) changes\n-- e.g. the changes for the next major version [2.0.0](https://github.com/robstoll/atrium-roadmap/issues?utf8=%E2%9C%93\u0026q=is%3Aissue+milestone%3A2.0.0)\n\nYou are invited to take part in the discussions related to design decisions, upcoming features and more.\nBring in your own wishes and ideas into this process.\n  \nIn case you are missing a particular expectation function in Atrium, then please open a \n[Feature Request](https://github.com/robstoll/atrium/issues/new?template=feature_request.md\u0026title=[Feature]) \nin this repository.\n\n# Contributors and contribute\n\nOur thanks go to [code contributors](https://github.com/robstoll/atrium/graphs/contributors) \nas well as other contributors (see acknowledgements in the [release notes](https://github.com/robstoll/atrium/releases)).\n\nYou are more than welcome to contribute as well:\n- star Atrium if you like it\n- [open a bug](https://github.com/robstoll/atrium/issues/new?template=bug_report.md) or [create a feature request](https://github.com/robstoll/atrium/issues/new?template=feature_request.md\u0026title=[Feature])\n- share your ideas via [issue](https://github.com/robstoll/atrium/issues/new) or [slack](https://kotlinlang.slack.com/messages/C887ZKGCQ)\n- [ask a question](https://kotlinlang.slack.com/messages/C887ZKGCQ)\n  so that we better understand where Atrium needs to improve.\n- write a blog post about Atrium (e.g. about a feature you like) or a tutorial (let us know we happily link to your page)\n- share your expectation functions with the rest of us by creating a pull request (no need for i18n support or the like, we can augment your pull request).\n- have a look at the [help wanted issues](https://github.com/robstoll/atrium/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)\n  if you would like to code (ping us on [Slack](https://kotlinlang.slack.com/messages/C887ZKGCQ) if there are not any).  \n\nPlease have a look at \n[CONTRIBUTING.md](https://github.com/robstoll/atrium/tree/main/.github/CONTRIBUTING.md)\nfor further suggestions and guidelines.\n\n# Sponsors\nWe would like to thank the following sponsors for their support:\n- [Tegonal Cooperative](https://tegonal.com) for sponsoring Support and PR-Review time.\n\nDo you want to become a sponsor as well? Great, have a look at the following GitHub sponsor profiles:\n- [robstoll](https://github.com/sponsors/robstoll) (Author and main contributor)\n\nor ping @robstoll in the Slack-Channel if you would like to support the project in another way.\n\n# License\nAtrium is licensed under [EUPL 1.2](https://joinup.ec.europa.eu/collection/eupl/eupl-text-11-12).\n\nAtrium is using:\n- [KBox](https://github.com/robstoll/kbox) licensed under [Apache 2.0](https://opensource.org/licenses/Apache2.0)\n- [Niok](https://github.com/robstoll/niok) licensed under [Apache 2.0](https://opensource.org/licenses/Apache2.0)\n","funding_links":["https://github.com/sponsors/robstoll"],"categories":["Libraries","Kotlin","测试","Testing"],"sub_categories":["Test","Misc","SQL"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobstoll%2Fatrium","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobstoll%2Fatrium","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobstoll%2Fatrium/lists"}