All Versions
18
Latest Version
Avg Release Cycle
45 days
Latest Release
71 days ago

Changelog History
Page 1

  • v0.12.0

    May 26, 2020

    API Maturity: Stable
    Implementation Maturity: Almost Stable

    πŸ— There won't be any breaking changes in the API (assertion functions/builders) until v1.0.0 besides parameter name renaming and experimental features. But we want to progress as well and deprecate functionality in each version (e.g quite a lot with 0.7.0; please replace deprecated functionality until v1.0.0 where we will remove it).
    βœ… However, we do not provide yet a stable API for the domain and core modules of Atrium -- it is almost stable, but there might be slight breaking changes which we want to introduce before v1.0.0. That is also the reason why we do not have yet established backward compatibility tests for domain/core. This might affect you if you write your own assertion functions. And it also affects you if you provide your own implementation for parts of Atrium.

    Table of Content

    πŸ†• New Features

    api-fluent-en_GB

    • #375 Path.hasSameTextualContentAs => @thanks to tkech17

    πŸ†• new api-infix-en_GB

    Thanks to the following contributors to support the migration from cc-infix-en_GB to infix-en_GB:

    kotlin 1.3 extensions

    Domain / Core

    • none this time

    πŸ›  Fixes

    • none this time

    πŸ‘Œ Improvements

    • 🏁 #235 setup code coverage for windows=> thanks to @ivanmiklec
    • πŸ— #245 Build samples as composite build => thanks to @assaflei
      #/167 compile android class files into dex bytecode => thanks to @assaflei
    • πŸ”€ #493 merge jdk8 extension into jvm module => thanks to @assaflei
    • #470 create maven sample project => thanks to @binkley
    • #466 fail if a TranslatableWithArgs has not exact arguments => thanks to @tkech17
    • #495 cache gradle and dependencies in github actions => thanks to @assaflei
    • 🚚 #506 remove android jars -> they are no longer required => thanks to @ultraon for checking if the module-info.class bug was really resolved in d8

    πŸ’₯ Breaking Changes

    Planned (previously deprecated or announced)

    • none this time (yet we no longer publish a dedicated -android jar you can use the regular jvm artifact instead)

    Unplanned

    • none this time

    πŸ—„ Deprecation

    🚚 The following was deprecated and will be removed with 1.0.0:

    • API cc-infix-en_GB => use infix-en_GB, fluent-en_GB respectively => a big thank you to @Miftahunajat for the many ReplaceWith he placed all over api-cc-infix to ease the migration πŸ‘
    • the jdk8 extension was merged into the normal module => search import ch.tutteli.atrium.api.fluent.en_GB.jdk8 replace with import ch.tutteli.atrium.api.fluent.en_GB

    πŸ’₯ Breaking Changes with 1.0.0

    πŸ‘€ See atrium-roadmap -> Milestone 1.0.0

    πŸ—„ Migrating deprecated functionality

    API fluent

    ⚑️ There aren't any deprecations in api-fluent-en_GB in this version and thus also no migration required if you update from 0.9.x, 0.10.0 or 0.11.0

    πŸ”€ In case you used the jdk8 extension: it was merged into the main artifact:

    • perform a search for import ch.tutteli.atrium.api.fluent.en_GB.jdk8 and replace with import ch.tutteli.atrium.api.fluent.en_GB
    • βœ‚ remove the corresponding dependency

    API infix

    You should migrate from api-cc-infix-en_GB to the new api-infix-en_GB (api-cc-infix-en_GB will be removed with 1.0.0)
    πŸš€ In case you migrate from a version < 0.7.0 to this version, then please have a look at the migration guide given in the Release notes of v0.7.0 and v0.8.0 first.

    Otherwise you can use the suggested replacements (ALT + Enter -> Replace with ...) or the search/replace patterns shown below (recommended, since faster).

    Notice, that you don't have to migrate everything at once where asExpect and asAssert allow to switch between the old Assert and the new Expect world.
    Ping us in the Atrium slack channel if you need help.

    The following command is carrying out the points 1 to 14 as described below. The script for 15a or 15b follows afterwards (don't forget to do the manual points 16, 17, ...), run it from the root of your project, no guarantees that your system is capable of carrying it out. If not, you can use the manual steps described below.

    find ./ -path "*/test/*" -name "*.kt" | xargs perl -0777 -i \
    -pe 's/AssertImpl([\n\r\s]*)\.changeSubject\(([^\)\n]+)\)[\n\r\s]*\{[\n\r\s]*subject/ExpectImpl$1.changeSubject\($2\)$1.unreported \{ it/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.changeSubject\(([^\)]+)\)/ExpectImpl$1.changeSubject\($2\).unreported/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)plant.subject/AssertImpl$1.builder$2.descriptive$3.withTest\(plant\)$4\{$5it/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)subject/AssertImpl$1.builder$2.descriptive$3.withTest\(this\)$4\{$5it/g;' \
    -pe 's/(\.| )((?:toThrow|isA)<.*>)\s*\{\s*\}/$1$2()/g;' \
    -pe 's/notToBeNull\s*\{\s*\}/notToBe null/g;' \
    -pe 's/fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlant\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter\)/fun <T> $1\(subject: T\): Expect<T> = \n ExpectBuilder.forSubject\(subject\)\n .withVerb\($2\)\n .withoutOptions\(\)\n .build\(\)/g;' \
    -pe 's/fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\s*,[\n\r\s]*assertionCreator: Assert<T>.\(\)\s*->\s*Unit\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantAndAddAssertionsCreatedBy\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter,[\n\r\s]*assertionCreator\)/fun <T> $1\(subject: T, assertionCreator: Expect<T>.\(\) -> Unit\): Expect<T> = \n $1(subject).addAssertionsCreatedBy(assertionCreator)/g;' \
    -pe 's/(?:internal )?fun <T(?:\s*:\s*Any\?)?> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantNullable\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[^\)]+\)//g;' \
    -pe 's/import ch.tutteli.atrium.verbs\.(expect|assert|assertThat)/import ch.tutteli.atrium.api.verbs.$1/g;' \
    -pe 's/AssertImpl/ExpectImpl/g;' \
    -pe 's/fun Assert(?:ionPlant(?:Nullable)?)?<(.*)>\./fun <T: $1> Expect<T>\./g;' \
    -pe 's/Assert(ionPlant(Nullable)?)?</Expect</g;' \
    -pe 's/import ch\.tutteli\.atrium\.creating\.Assert(ionPlant(Nullable)?)?/import ch.tutteli.atrium.creating.Expect/g;' \
    -pe 's/import ch.tutteli\.atrium\.api\.cc\.infix\.en_GB/import ch.tutteli.atrium.api.infix.en_GB/g;' \
    -pe 's/is(Less|Greater)OrEquals/is$1ThanOrEqual/g;' \
    -pe 's/\.asIterable\(\)/ asList o/g;' \
    -pe 's/asIterable(\s*\{)/asList$1/g;' \
    -pe 's/\.asEntries\(\)/ asEntries o/g;' \
    -pe 's/ o / it /g;' \
    -pe 's/get\s*Index\(([^\)]+)\)\s*assertIt\s*\{([^\}]+)\}/get index($1) {$2}/g;' \
    -pe 's/import ch.tutteli.atrium.api.infix.en_GB.Index/import ch.tutteli.atrium.api.infix.en_GB.index/g;' \
    -pe 's/getExisting\s*Key\(([^\)]+)\)\s*assertIt\s*\{([^\}]+)\}/getExisting key($1) {$2}/g;' \
    -pe 's/import ch.tutteli.atrium.api.infix.en_GB.Key/import ch.tutteli.atrium.api.infix.en_GB.key/g;' 
    

    In case you use Kotlin 1.4 or have enabled the new type inference, then you can use the following script to carry out point 13a:

    find ./ -path "*/test/*" -name "*.kt" | xargs perl -0777 -i \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)/$1its feature of(\{ f(it::$2) \})$3/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)/feature of(\{ f(it::$1) \})$2/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)/$1its feature \{ f(it::$2) \}/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)/feature \{ f(it::$1) \}/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)/$1its feature of($2::$3)$4/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)/feature of($1::$2)$3/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)/$1 its feature($2::$3)/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)/feature($1::$2)/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)/$1its feature of($2::$3)/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)/feature of($1::$2)/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)/$1its feature of("$2.$3") { $2.$3 }/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)/feature("$1.$2", { $1.$2 })/g;' \
    -pe 's/(import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)/$1.feature/g;' \
    

    Otherwise you need to carry out the point 13b which can be done with the following script`

    find ./ -path "*/test/*" -name "*.kt" | xargs perl -0777 -i \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)/$1its feature \{ f(it::$2) \} it$3/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)/feature \{ f(it::$1) \} it$2/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)/$1its feature \{ f(it::$2) \}/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)/feature \{ f(it::$1) \}/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)/$1its feature of($2::$3)$4/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)/feature of($1::$2)$3/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)/$1 its feature($2::$3)/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)/feature($1::$2)/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)/$1its feature of($2::$3)/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)/feature of($1::$2)/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)/$1its feature of("$2.$3") { $2.$3 }/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)/feature("$1.$2", { $1.$2 })/g;' \
    -pe 's/(import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)/$1.feature/g;'
    

    β†ͺ But you still need to add manually import ch.tutteli.atrium.api.infix.en_GB.workaround.it where it is not known


    In case you cannot carry out the commands above, then read on to perform the manual steps:

    The following list helps you to migrate faster by using a few regex search replace commands (in Intellij). Make sure you have checked Regex as well as Match Case in the search options. Notice, that the code will certainly not compile after a single replace, you need to carry out all search&replace commands.
    It is not perfect, maybe you need to do a few adjustments in addition, let us now and we improve the search/replace commands here.

    Switch to ExpectImpl.changeSubject instead of using AssertImpl.changeSubject:
    Search: AssertImpl([\n\r\s]*)\.changeSubject\(([^\)\n]+)\)[\n\r\s]*\{[\n\r\s]*subject
    Replace: ExpectImpl$1.changeSubject\($2\)$1.unreported { it

    Search: AssertImpl([\n\r\s]*)\.changeSubject\(([^\)]+)\)
    Replace: ExpectImpl$1.changeSubject\($2\).unreported

    πŸ— builder.descriptive, safe withTest

    πŸ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject
    πŸ— Replace: AssertImpl$1.builder$2.createDescriptive\(plant, $3it

    πŸ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)plant.subject
    πŸ— Replace: AssertImpl$1.builder$2.descriptive$3.withTest\(plant\)$4{$5it

    πŸ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)subject
    πŸ— Replace: AssertImpl$1.builder$2.descriptive$3.withTest\(this\)$4{$5it

    toThrow and isA with empty assertionCreator lambda
    Search: (\.| )((?:toThrow|wirft|isA|istEin)<.*>)\s*\{\s*\}
    Replace $1$2()

    notToBeNull with empty assertionCreator lambda
    Search: notToBeNull\s*\{\s*\}
    Replace: notToBe null

    migrate custom assertion verbs:

    Search: fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlant\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter\)
    Replace:
    πŸ— fun <T> $1\(subject: T\): Expect<T> = \n ExpectBuilder.forSubject\(subject\)\n .withVerb\($2\)\n .withoutOptions\(\)\n .build\(\)

    Search:
    fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\s*,[\n\r\s]*assertionCreator: Assert<T>.\(\)\s*->\s*Unit\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantAndAddAssertionsCreatedBy\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter,[\n\r\s]*assertionCreator\)
    Replace:
    fun <T> $1\(subject: T, assertionCreator: Expect<T>.\(\) -> Unit\): Expect<T> = \n $1(subject).addAssertionsCreatedBy(assertionCreator)

    Search:
    (?:internal )?fun <T(?:\s*:\s*Any\?)?> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantNullable\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[^\)]+\)
    Replace: (empty string)

    In case the above search&replace did not find anything (because your code is different):
    ♻️ Switch from AssertImpl.coreFactory.newReportingPlant to ExpectBuilder
    πŸ— => see atriumVerbs.kt for an example of how own assertion verbs are defined now; or use the suggested replacements but please add import ch.tutteli.atrium.domain.builders.reporting.ExpectBuilder first as it will not work correctly otherwise due to an Intellij bug
    => Note that you don't need a verb for nullable types any more. Thus:

    • remove the upper bound T: Any
    • remove the verb which uses `newReportingPlantNullable
    • remove the verb which expected act: () -> Unit

    Switch to new built-in assertion verbs which use Expect

    Search: import ch.tutteli.atrium.verbs.(expect|assert|assertThat)
    Replace: import ch.tutteli.atrium.api.verbs.$1

    Switch from AssertImpl to ExpectImpl

    Search: AssertImpl
    Replace: ExpectImpl

    Switch all your assertion functions to use Expect and no longer Assert:

    Search: import ch\.tutteli\.atrium\.creating\.Assert(ionPlant(Nullable)?)?
    Replace: import ch.tutteli.atrium.creating.Expect

    Search: fun Assert(?:ionPlant(?:Nullable)?)?<(.*)>\.
    Replace: fun <T: $1> Expect<T>\.

    Search: Assert(ionPlant(Nullable)?)?<
    Replace: Expect<

    Switch the API

    Search: import ch.tutteli\.atrium\.api\.cc\.infix\.en_GB Replace:import ch.tutteli.atrium.api.infix`

    isLessOr/isGreaterOrEquals

    Search: is(Less|Greater)OrEquals
    Replace: is$1ThanOrEqual

    11 use asList instead of asIterable and replace asEntries()

    Search: \.asIterable\(\)
    Replace: asList o

    Search: asIterable(\s*\{)
    Replace: asList$1

    Search: asEntries\(\)
    Replace: asEntries o

    12 List get Index assertIt
    This one has to be done with care as there could be nested assertion group blocks

    Search: get\s*Index\(([^\)]+)\)\s*assertIt\s*\{([^\}]+)\}
    Replace: get index($1) {$2}

    Search: import ch.tutteli.atrium.api.infix.en_GB.Index
    Replace: import ch.tutteli.atrium.api.infix.en_GB.index

    13 Map getExisting Key assertIt
    This one has to be done with care as there could be nested assertion group blocks

    Search: getExisting\s*Key\(([^\)]+)\)\s*assertIt\s*\{([^\}]+)\}
    Replace: getExisting key($1) {$2}

    Search: import ch.tutteli.atrium.api.infix.en_GB.Key
    Replace: import ch.tutteli.atrium.api.infix.en_GB.key

    14 use it instead of o inside assertion groups:
    Search: o
    Replace: it (you can also replace by its)

    15a. use new feature mechanism - replacements for Kotlin 1.4 or the new inference (skip to 13b in case you use Kotlin < 1.4)

    This one needs extra care as arguments could be function calls. Verify the replacements

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    Replace: $1its feature of(\{ f(it::$2) \})$3

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    Replace: feature of(\{ f(it::$1) \})$2

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)
    Replace: $1its feature \{ f(it::$2) \}

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)
    Replace: feature \{ f(it::$1) \}

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)
    Replace: $1its feature of($2::$3)$4

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)
    Replace: feature of($1::$2)$3

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)
    Replace: $1 its feature($2::$3)

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)
    Replace: feature($1::$2)

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)
    Replace: $1its feature of($2::$3)

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)
    Replace: feature of($1::$2)

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)
    Replace: $1its feature of("$2.$3") { $2.$3 }

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)
    Replace: feature("$1.$2", { $1.$2 })

    Search: (import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)
    Replace: $1.feature

    15b use new feature mechanism - replacements for Kotlin < 1.4 (skip if you already applied 11a)

    This one needs extra care as arguments could be function calls. Verify the replacements

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    βž• add import ch.tutteli.atrium.api.infix.en_GB.workaround.it to those files

    Search again for: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    Replace: $1its feature \{ f(it::$2) \} it$3

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    Replace: feature \{ f(it::$1) \} it$2

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)
    Replace: $1its feature \{ f(it::$2) \}

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)
    Replace: feature \{ f(it::$1) \}

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)
    Replace: $1its feature of($2::$3)$4

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)
    Replace: feature of($1::$2)$3

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)
    Replace: $1 its feature($2::$3)

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)
    Replace: feature($1::$2)

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)
    Replace: $1its feature of($2::$3)

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)
    Replace: feature of($1::$2)

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)
    Replace: $1its feature of("$2.$3") { $2.$3 }

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)
    Replace: feature("$1.$2", { $1.$2 })

    Search: (import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)
    Replace: $1.feature

    β†ͺ But you still need to add manually import ch.tutteli.atrium.api.infix.en_GB.workaround.it where it is not known

    16 In case you have custom assertion verbs
    Dealing with thrown exceptions is now handled by Expect as well.
    πŸ‘» However, in case you have named the assertion verb differently for expecting an Exception then you have to decide:

    • 🚚 use the same name => rename the corresponding function which expects act: () -> Unit to the same name and remove it afterwards
    • πŸ‘‰ use a different name => delegate the function which expects act: () -> Unit to the other verb

    πŸ— Check if you need to add import ch.tutteli.atrium.domain.builders.reporting.ExpectBuilder

    Try to reduce duplicated Expect imports
    Repeat until you don't have duplicate imports any more
    Search: import ch\.tutteli\.atrium\.creating\.Expect\n\s*import ch\.tutteli\.atrium\.creating\.Expect
    Replace: import ch.tutteli.atrium.creating.Expect

    ⚠ Try to compile your project and watch out for the following warnings:

    • 'MyClass' is a final type, and thus a value of the type parameter is predetermined
      => you can suppress this warning by adding @file:Suppress("FINAL_UPPER_BOUND") to your file, this is actually a Kotlin bug (https://youtrack.jetbrains.com/issue/KT-34257)
  • v0.11.1

    April 30, 2020

    πŸš€ See #485 for more details. All other features introduce in v0.11.0 are shown in release notes of v0.11.0

  • v0.11.0

    April 19, 2020

    API Maturity: Stable
    Implementation Maturity: Almost Stable

    πŸ— There won't be any breaking changes in the API (assertion functions/builders) until v1.0.0 besides parameter name renaming and experimental features. But we want to progress as well and deprecate functionality in each version (e.g quite a lot with 0.7.0 and with 0.9.0; please replace deprecated functionality until v1.0.0 where we will remove it).
    βœ… However, we do not provide yet a stable API for the domain and core modules of Atrium -- it is almost stable, but there might be slight breaking changes which we want to introduce before v1.0.0. That is also the reason why we do not have yet established backward compatibility tests for domain/core. This might affect you if you write your own assertion functions. And it also affects you if you provide your own implementation for parts of Atrium.

    Table of Content

    πŸ†• New Features

    API fluent-en_GB

    • #131 shortcut Iterable.containsExactlyElementsOf => thanks to @tkech17
    • #425 shortcut Iterable.containsElementsOf => thanks to @tkech17
    • #422 add elementsOf to contains.ignoreCase => thanks to @Miftahunajat

    Domain / Core

    • none this time

    πŸ›  Fixes

    • none this time

    πŸ‘Œ Improvements

    • #339 Avoid Duplicated Messages
    • #116 Migrate Spek1 specs to Spek2 => thanks to @assaflei
    • 🚚 #416 move non-jvm specific specs to common => thanks to @assaflei

    πŸ’₯ Breaking Changes

    Planned (previously deprecated or announced)

    • none this time

    Unplanned

    • none this time

    πŸ—„ Deprecation

    • none this time

    πŸ’₯ Breaking Changes with 1.0.0

    πŸ‘€ See atrium-roadmap -> Milestone 1.0.0

    πŸ—„ Migrating deprecated functionality

    ⚑️ There aren't any deprecations in this version and thus also no migration required if you update from 0.9.x or 0.10.0.

    πŸš€ In case you migrate from 0.8.0 to this version, have a look at the migration guide given in the release notes of v0.9.0

  • v0.10.0

    March 12, 2020

    API Maturity: Stable
    Implementation Maturity: Almost Stable

    πŸ— There won't be any breaking changes in the API (assertion functions/builders) until v1.0.0 besides parameter name renaming and experimental features. But we want to progress as well and deprecate functionality in each version (e.g quite a lot with 0.7.0; please replace deprecated functionality until v1.0.0 where we will remove it).
    βœ… However, we do not provide yet a stable API for the domain and core modules of Atrium -- it is almost stable, but there might be slight breaking changes which we want to introduce before v1.0.0. That is also the reason why we do not have yet established backward compatibility tests for domain/core. This might affect you if you write your own assertion functions. And it also affects you if you provide your own implementation for parts of Atrium.

    Table of Content

    πŸ†• New Features

    API fluent-en_GB

    • #31 shortcut for Throwable.cause => thanks to @tfesenko for the implementation

    jdk8 extensions

    • #216 shortcut for Path.resolve => thanks to @slalu for the implementation

    Domain / Core

    • none this time

    πŸ›  Fixes

    • none this time

    πŸ‘Œ Improvements

    πŸ’₯ Breaking Changes

    Planned (previously deprecated or announced)

    • none this time

    Unplanned

    • none this time

    πŸ—„ Deprecation

    • none this time

    πŸ’₯ Breaking Changes with 1.0.0

    πŸ‘€ See atrium-roadmap -> Milestone 1.0.0

    πŸ—„ Migrating deprecated functionality

    ⚑️ There aren't any deprecations in this version and thus also no migration required if you update from 0.9.0.

    πŸš€ In case you migrate from 0.8.0 to this version, have a look at the migration guide given in the release notes of v0.9.0

  • v0.9.2

    February 08, 2020

    πŸ›  Fixes

    πŸ‘€ it was planned that withOptions ships with 0.9.0 (see #153). However, this was done only for the assertion verb which is used internally in Atrium but was not revealed for the predefined assertion verbs, hence this patch-version (thanks to @matejdro for the report - #362)

    Most users won't use withOptions and thus we are going to present only withRepresentation for now (introduced in 0.9.2 #365).
    ❗❗Note though that withOptions and withRepresentation are both experimental and might be changed in a future version without previous notice nor migration path.

    withRepresentation can be used as follows :

    expect(listOf(1,2,2,3 /* imagine a lot more numbers */))
      .withRepresentation("xy numbers")
      .all { isLessThan(10) }
    

    The error report looks then as follows in case of a failure:

    expected that subject: xy numbers
    ◆ all entries: 
        Β» is less than: 10 (kotlin.Int <1234789>)
        ❗❗ following entries were mismatched: 
           ⚬ index 101: 12 (kotlin.Int <8933389>)
           ⚬ index 223: 10 (kotlin.Int <4523459>)
    

    πŸ‘€ To use it, you have to add the following annotation to your test method/class @UseExperimental(ExperimentalWithOptions::class)

    Further Improvements

    • #81 create sample MPP project => thanks to @bsemexan for most of the work

    πŸš€ all other features as well a migration guide is given in the release notes of v0.9.0

  • v0.9.1

    February 04, 2020

    πŸš€ all other features as well a migration guide is given in the release notes of v0.9.0

  • v0.9.0

    February 02, 2020

    API Maturity: Stable
    Implementation Maturity: Almost Stable

    πŸ— There won't be any breaking changes in the API (assertion functions/builders) until v1.0.0 besides parameter name renaming and experimental features. But we want to progress as well and deprecate functionality in each version (e.g quite a lot with 0.7.0; please replace deprecated functionality until v1.0.0 where we will remove it).
    βœ… However, we do not provide yet a stable API for the domain and core modules of Atrium -- it is almost stable, but there might be slight breaking changes which we want to introduce before v1.0.0. That is also the reason why we do not have yet established backward compatibility tests for domain/core. This might affect you if you write your own assertion functions. And it also affects you if you provide your own implementation for parts of Atrium.

    Table of Content

    πŸ†• New Features

    πŸ†• new API fluent-en_GB

    • #26 Introduced Expect<T> as replacement for Assert<out T> => things like expect(1).toBe("not an int") result now in a compile error
    • #66 allow to pass single Char to starts(Not)With/ends(Not)With => thanks to @kssc0112 for the implementation
    • #128 Iterable.contains.inOrder.only.elementsOf => thanks to @npswedberg for the implementation
    • #129 Iterable.contains.inAnyOrder.elementsOf => thanks to @sanatik for the implementation
    • #127 Iterable.contains.inAnyOrder.only.elementsOf => thanks to @johnGachihi for the implementation
    • #158 Iterable.hasNext/hasNotNext => thanks to @sanatik for the implementation
    • #163 shortcut for Iterable.min() => thanks to @piyushmor for the implementation
    • #163 shortcut for Iterable.min() => thanks to @Megamiun for the implementation
    • #130 CharSequence.contains.elementsOf => thanks to @mattyway for the implementation
    • #165 CharSequence.matches => thanks to @mikemolenda for the implementation
    • #165 CharSequence.mismatches => thanks to @shardulsonar for the implementation
    • #183 Array.asList => thanks to @piraces for the implementaiton

    jdk8 extensions

    • #108 Path.exists and Path.existsNot with excellent failure hints => big thanks to @jGleitz for the implementation
    • #111 shortcut for Paht.isDirectory and Paht.isRegularFile => thanks to @jGleitz for the implementation
    • #112 shortcut for Paht.isReadable and Paht.isWritable => thanks to @jGleitz for the implementation
    • #187 Path.startsWith => thanks to @arjank for the implementation
    • #189 Path.startsNotWith => thanks to @arjank for the implementation
    • #188 Path.endsWith => thanks to @gaconkzk for the implementation
    • #190 Path.endsNotWith => thanks to @segunfamisa for the implementation
    • #109 shortcut for Path.fileName => thanks to @Tregz for the implementation
    • #110 shortcut for Path.getParent => thanks to @lpicanco for the implementation
    • #169 shortcut for Path.extension => thanks to @lelloman for the implementation
    • #170 shortcut for Path.fileNameWithoutExtension => thanks to @aljacinto for the implementation
    • #166 File.asPath => thanks to @lelloman for the implementation
    • #261 LocalDate(Time) and ZonedDatetime.isBefore => thanks to @sandjelkovic for the start and @name213 for the finishing
    • #261 LocalDate(Time) and ZonedDatetime.isBeforeOrEqual => thanks to @name213 for the implementation
    • #175 LocalDate(Time) and ZonedDatetime.isAfter => thanks to @name213 for the implementation
    • #262 LocalDate(Time) and ZonedDatetime.isAfterOrEqual => thanks to @lukebiddell for the implementation
    • #290 LocalDate(Time) and ZonedDatetime.isEqual => thanks to @name213 for the implementation
    • #174 shortcut for LocalDate(Time) and ZonedDatetime.year => thanks to @lpicanco for the implementation
    • #175 shortcut for LocalDate(Time) and ZonedDatetime.month => thanks to @ShradhaSangtani for the implementation
    • #176 shortcut for LocalDate(Time) and ZonedDatetime.day => thanks to @ShradhaSangtani for the implementation
    • #175 shortcut for LocalDate(Time) and ZonedDatetime.dayOfWeek => thanks to @sanatik for the implementation
    • #47 Optional.isEmpty => thanks to @arjank for the implementation
    • #113 shortcut for Optional.get named isPresent => thanks to @slalu for the implementation

    kotlin 1.3 extensions

    Domain / Core

    πŸ”‹ Features for assertion-function-writers:

    • introduced ExtractedFeaturePostStep and ChangedSubjectPostStep in order to have only one function on the domain level which covers both, narrowing features and features which expect an assertionCreator-lambda
    • ExpectImpl.feature.extractor
    • πŸ— ExpectImpl.builder.representationOnly => assertion without description but only a representation

    Others

    • #80 sample project for Atrium + junit5 => thanks to @bsemexan for the implementation
    • #207 sample project for Atrium + jasmine => thanks to @bsemexan for the implementation

    πŸ›  Fixes

    • #143 domain-api-js was not included in bundle => thanks go to Darren Bell for reporting the issue

    πŸ‘Œ Improvements

    • #160 CharSequence.containsRegex accepts now also a Regex => thanks to @neelkamath for the implementation
    • πŸ— #85 switch to implement instead of compile in build.gradle where possible => thanks to @kssc0112 for the implementation
      due to this cc-en_GB should no longer show up in cc-infix-en_GB
    • ⚑️ #193 update to spek 2.0.8 and use Spek's include => thanks to @Anubhav007 for the implementation
    • #202 use spek-js instead of dummy impl => thanks to @wudmer for the implementation
    • #239 spec for elementsOf empty iterable => thanks to @Megamiun for the implementation
    • ⚑️ #218 update tutteli-spek-extensions to 1.0.1 => thanks to @ShradhaSangtani for the implementation
    • #86 switch to native emoij => thanks to @Frandall
    • #156 improve specs for iterable, use one time consumable iterables => thanks to @aljacinto for the analysis and implementation
    • πŸ›  #296 git guide for newcomers as well as other fixes in CONTRIBUTING.md => thanks to @johnGachihi for the implementation
    • #285 do not report value type for map contains values
    • #297 rename ...OrEquals to ...OrEqual
    • 🚚 #298 remove isA check in toBe for nullable types
    • #300 show only java's qualified name if different from kotlin => thanks to @Hubisco for the implementation
    • β†ͺ #303 workaround KT-35882, set cause of AtriumError explicit to null
    • #306 reword number of occurrences => thanks to @Jak-Sidious for the implementation
    • #307 change assertion verb in reporting => thanks to @Jak-Sidious for the implementation
    • #308 change toBe in reporting to equals => thanks to @Hubisco for the implementation

    πŸ’₯ Breaking Changes

    Planned (previously deprecated or announced)

    • none this time

    Unplanned

    • none this time

    πŸ—„ Deprecation

    🚚 The following was deprecated and will be removed with 1.0.0:

    • API cc-en_GB => use fluent-en_GB, infix-en_GB respectively
    • API cc-de_CH => there won't be a replacement, vote for #137 if you used it, we recommend you switch to fluent-en_GB
    • everything involving Assert/AssertionPlant and the like => switch to Expect and the like
    • πŸ—„ Assert.subject, which means Expect.subject is deprecated as well. Subject is passed as argument to lambdas which have to deal with it.
      For instance, instead of writing createAndAddAssertion(TO_BE, expected) { subject == expected } one has to write createAndAddAssertion(TO_BE, expected) { it == expected }
    • ThrowableBuider including the assertion verb which dealt with exceptions (accepted a lambda) => can now be done with the regular expect function
    • 🚚 SubjectProvider and AssertionHolder, both introduced in this version and it might well be we remove it with 1.0.0

    πŸ—„ The following deprecations are planned for a future version

    • this release does not yet include the new infix API. However, we are going to deprecate cc-infix-en_GB in favour of the new infix API infix-en_GB which is based on Expect as soon as the new infix API is ready.
    • πŸ—„ AssertImpl will be deprecated in favour of ExpectImpl

    πŸ’₯ Breaking Changes with 1.0.0

    πŸ‘€ See atrium-roadmap -> Milestone 1.0.0

    πŸ—„ Migrating deprecated functionality

    πŸš€ In case you migrate from a version < 0.7.0 then please have a look at the migration guide given in the Release Notes of 0.7.0 and 0.8.0.
    Otherwise you can use the suggested replacements (ALT + Enter -> Replace with ...) or the search/replace patterns shown below.

    Notice, that you don't have to migrate everything at once where asExpect and asAssert allow to switch between the old Assert and the new Expect world.
    Ping us in the Atrium slack channel if you need help.

    The following command is carrying out the points 1 to 11 described below (don't forget the points 12, 13, ...), run it from the root of your project, no guarantees that your system is capable of carrying it out. If not, you can use the manual steps described below.

    find ./ -name "*.kt" | xargs perl -0777 -i \
    -pe 's/AssertImpl([\n\r\s]*)\.changeSubject\(([^\)\n]+)\)[\n\r\s]*\{[\n\r\s]*subject/ExpectImpl$1.changeSubject\($2\)$1.unreported \{ it/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.changeSubject\(([^\)]+)\)/ExpectImpl$1.changeSubject\($2\).unreported/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)plant.subject/AssertImpl$1.builder$2.descriptive$3.withTest\(plant\)$4\{$5it/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)subject/AssertImpl$1.builder$2.descriptive$3.withTest\(this\)$4\{$5it/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)/feature(\{ f(it::$1) \})$2/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)/feature \{ f(it::$1) \}/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)`/feature(\{ f(it::$1) \})$2/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)/feature($1::$2)/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)/feature("$1.$2", { $1.$2 })/g;' \
    -pe 's/(import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)/$1.feature/g;' \
    -pe 's/(\.| )((?:toThrow|wirft|isA|istEin)<.*>)\s*\{\s*\}/$1$2()/g;' \
    -pe 's/notToBeNull\s*\{\s*\}/notToBe(null)/g;' \
    -pe 's/fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlant\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter\)/fun <T> $1\(subject: T\): Expect<T> = \n ExpectBuilder.forSubject\(subject\)\n .withVerb\($2\)\n .withoutOptions\(\)\n .build\(\)/g;' \
    -pe 's/fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\s*,[\n\r\s]*assertionCreator: Assert<T>.\(\)\s*->\s*Unit\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantAndAddAssertionsCreatedBy\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter,[\n\r\s]*assertionCreator\)/fun <T> $1\(subject: T, assertionCreator: Expect<T>.\(\) -> Unit\): Expect<T> = \n $1(subject).addAssertionsCreatedBy(assertionCreator)/g;' \
    -pe 's/(?:internal )?fun <T(?:\s*:\s*Any\?)?> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantNullable\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[^\)]+\)//g;' \
    -pe 's/import ch.tutteli.atrium.verbs\.(expect|assert|assertThat)/import ch.tutteli.atrium.api.verbs.$1/g;' \
    -pe 's/AssertImpl/ExpectImpl/g;' \
    -pe 's/fun Assert(?:ionPlant(?:Nullable)?)?<(.*)>\./fun <T: $1> Expect<T>\./g;' \
    -pe 's/Assert(ionPlant(Nullable)?)?</Expect</g;' \
    -pe 's/import ch\.tutteli\.atrium\.creating\.Assert(ionPlant(Nullable)?)?/import ch.tutteli.atrium.creating.Expect/g;' \
    -pe 's/import ch.tutteli\.atrium\.api\.cc\.(en_GB|de_CH)/import ch.tutteli.atrium.api.fluent.$1/g;' \
    -pe 's/is(Less|Greater)OrEquals/is$1ThanOrEqual/g;'
    

    The following list helps you to migrate faster by using a few regex search replace commands (in Intellij). Make sure you have checked Regex as well as Match Case in the search options. Notice, that the good will certainly not compile after a single replace, you need to carry out all search&replace commands.
    It is not perfect, maybe you need to do a few adjustments in addition, let us now and we improve the search/replace commands here.

    Switch to ExpectImpl.changeSubject instead of using AssertImpl.changeSubject:
    Search: AssertImpl([\n\r\s]*)\.changeSubject\(([^\)\n]+)\)[\n\r\s]*\{[\n\r\s]*subject
    Replace: ExpectImpl$1.changeSubject\($2\)$1.unreported { it

    Search: AssertImpl([\n\r\s]*)\.changeSubject\(([^\)]+)\)
    Replace: ExpectImpl$1.changeSubject\($2\).unreported

    πŸ— builder.descriptive, safe withTest

    πŸ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject
    πŸ— Replace: AssertImpl$1.builder$2.createDescriptive\(plant, $3it

    πŸ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)plant.subject
    πŸ— Replace: AssertImpl$1.builder$2.descriptive$3.withTest\(plant\)$4{$5it

    πŸ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)subject
    πŸ— Replace: AssertImpl$1.builder$2.descriptive$3.withTest\(this\)$4{$5it

    πŸ‘‰ use new feature mechanism

    This one needs extra care as arguments could be function calls. Verify the replacements

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    Replace: feature(\{ f(it::$1) \})$2

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)
    Replace: feature \{ f(it::$1) \}

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)
    Replace: feature($1::$2)

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)
    Replace: feature("$1.$2", { $1.$2 })

    Search: (import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)
    Replace: $1.feature

    toThrow and isA with empty assertionCreator lambda
    Search: (\.| )((?:toThrow|wirft|isA|istEin)<.*>)\s*\{\s*\}
    Replace $1$2()

    notToBeNull with empty assertionCreator lambda
    Search: notToBeNull\s*\{\s*\}
    Replace: notToBe(null)

    migrate custom assertion verbs:

    Search: fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlant\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter\)
    Replace:
    πŸ— fun <T> $1\(subject: T\): Expect<T> = \n ExpectBuilder.forSubject\(subject\)\n .withVerb\($2\)\n .withoutOptions\(\)\n .build\(\)

    Search:
    fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\s*,[\n\r\s]*assertionCreator: Assert<T>.\(\)\s*->\s*Unit\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantAndAddAssertionsCreatedBy\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter,[\n\r\s]*assertionCreator\)
    Replace:
    fun <T> $1\(subject: T, assertionCreator: Expect<T>.\(\) -> Unit\): Expect<T> = \n $1(subject).addAssertionsCreatedBy(assertionCreator)

    Search:
    (?:internal )?fun <T(?:\s*:\s*Any\?)?> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantNullable\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[^\)]+\)
    Replace: (empty string)

    In case the above search&replace did not find anything (because your code is different):
    ♻️ Switch from AssertImpl.coreFactory.newReportingPlant to ExpectBuilder
    πŸ— => see atriumVerbs.kt for an example of how own assertion verbs are defined now; or use the suggested replacements but please add import ch.tutteli.atrium.domain.builders.reporting.ExpectBuilder first as it will not work correctly otherwise due to an Intellij bug
    => Note that you don't need a verb for nullable types any more. Thus:

    • remove the upper bound T: Any
    • remove the verb which uses `newReportingPlantNullable
    • remove the verb which expected act: () -> Unit

    Switch to new built-in assertion verbs which use Expect

    Search: import ch.tutteli.atrium.verbs.(expect|assert|assertThat)
    Replace: import ch.tutteli.atrium.api.verbs.$1

    Switch from AssertImpl to ExpectImpl

    Search: AssertImpl
    Replace: ExpectImpl

    Switch all your assertion functions to use Expect and no longer Assert:

    Search: import ch\.tutteli\.atrium\.creating\.Assert(ionPlant(Nullable)?)?
    Replace: import ch.tutteli.atrium.creating.Expect

    Search: fun Assert(?:ionPlant(?:Nullable)?)?<(.*)>\.
    Replace: fun <T: $1> Expect<T>\.

    Search: Assert(ionPlant(Nullable)?)?<
    Replace: Expect<

    Switch the API

    Search: import ch.tutteli\.atrium\.api\.cc\.(en_GB|de_CH)
    Replace: import ch.tutteli.atrium.api.fluent.$1

    isLessOr/isGreaterOrEquals

    Search: is(Less|Greater)OrEquals
    Replace: is$1ThanOrEqual

    In case you have custom assertion verbs
    Dealing with thrown exceptions is now handled by Expect as well.
    πŸ‘» However, in case you have named the assertion verb differently for expecting an Exception then you have to decide:

    • 🚚 use the same name => rename the corresponding function which expects act: () -> Unit to the same name and remove it afterwards
    • πŸ‘‰ use a different name => delegate the function which expects act: () -> Unit to the other verb

    πŸ— Check if you need to add import ch.tutteli.atrium.domain.builders.reporting.ExpectBuilder

    Try to reduce duplicated Expect imports
    Repeat until you don't have duplicate imports anymore
    Search: import ch\.tutteli\.atrium\.creating\.Expect\n\s*import ch\.tutteli\.atrium\.creating\.Expect
    Replace: import ch.tutteli.atrium.creating.Expect

    ⚠ Try to compile your project and watch out for the following warnings:

    • 'MyClass' is a final type, and thus a value of the type parameter is predetermined
      => you can suppress this warning by adding @file:Suppress("FINAL_UPPER_BOUND") to your file, this is actually a Kotlin bug (https://youtrack.jetbrains.com/issue/KT-34257)
  • v0.9.0-alpha2

    January 11, 2020

    This is a pre-release for early adopters using currently cc.en_GB and want to switch to fluent.en_GB which makes use of Expect instead of Assert

    Following the setup instructions:

    dependencies {
        testImplementation "ch.tutteli.atrium:atrium-fluent-en_GB:0.9.0-alpha2"
    }
    

    πŸš€ Proper release notes will follow with the final release of 0.9.0.
    Already many thanks to all the contributors

    Following already the migration guide.

    πŸ—„ Migrating deprecated functionality

    πŸš€ In case you migrate from a version < 0.7.0 then please have a look at the migration guide given in the Release Notes of 0.7.0.
    Otherwise you can use the suggested replacements (ALT + Enter -> Replace with ...) or the search/replace patterns shown below.

    Notice, that you don't have to migrate everything at once where asExpect and asAssert allow to switch between the old Assert and the new Expect world.
    Ping us in the Atrium slack channel if you need help.

    The following command is carrying out the points 1 to 10 described below (don't forget the points 11, 12, ...), run it from the root of your project, no guarantees that your system is capable of carrying it out. If not, you can use the manual steps described below.

    find ./ -name "*.kt" | xargs perl -0777 -i \
    -pe 's/AssertImpl([\n\r\s]*)\.changeSubject\(([^\)\n]+)\)[\n\r\s]*\{[\n\r\s]*subject/ExpectImpl$1.changeSubject\($2\)$1.unreported \{ it/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.changeSubject\(([^\)]+)\)/ExpectImpl$1.changeSubject\($2\).unreported/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)plant.subject/AssertImpl$1.builder$2.descriptive$3.withTest\(plant\)$4\{$5it/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)subject/AssertImpl$1.builder$2.descriptive$3.withTest\(this\)$4\{$5it/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)/feature(\{ f(it::$1) \})$2/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)/feature \{ f(it::$1) \}/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)`/feature(\{ f(it::$1) \})$2/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)/feature($1::$2)/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)/feature("$1.$2", { $1.$2 })/g;' \
    -pe 's/(import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)/$1.feature/g;' \
    -pe 's/(\.| )((?:toThrow|wirft|isA|istEin)<.*>)\s*\{\s*\}/$1$2()/g;' \
    -pe 's/notToBeNull\s*\{\s*\}/notToBe(null)/g;' \
    -pe 's/fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlant\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter\)/fun <T> $1\(subject: T\): Expect<T> = \n ExpectBuilder.forSubject\(subject\)\n .withVerb\($2\)\n .withoutOptions\(\)\n .build\(\)/g;' \
    -pe 's/fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\s*,[\n\r\s]*assertionCreator: Assert<T>.\(\)\s*->\s*Unit\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantAndAddAssertionsCreatedBy\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter,[\n\r\s]*assertionCreator\)/fun <T> $1\(subject: T, assertionCreator: Expect<T>.\(\) -> Unit\): Expect<T> = \n $1(subject).addAssertionsCreatedBy(assertionCreator)/g;' \
    -pe 's/(?:internal )?fun <T(?:\s*:\s*Any\?)?> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantNullable\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[^\)]+\)//g;' \
    -pe 's/import ch.tutteli.atrium.verbs\.(expect|assert|assertThat)/import ch.tutteli.atrium.api.verbs.$1/g;' \
    -pe 's/AssertImpl/ExpectImpl/g;' \
    -pe 's/fun Assert(?:ionPlant(?:Nullable)?)?<(.*)>\./fun <T: $1> Expect<T>\./g;' \
    -pe 's/Assert(ionPlant(Nullable)?)?</Expect</g;' \
    -pe 's/import ch\.tutteli\.atrium\.creating\.Assert(ionPlant(Nullable)?)?/import ch.tutteli.atrium.creating.Expect/g;' \
    -pe 's/import ch.tutteli\.atrium\.api\.cc\.(en_GB|de_CH)/import ch.tutteli.atrium.api.fluent.$1/g;' \
    -pe 's/import ch.tutteli\.atrium\.api\.cc\.infix\.en_GB/import ch.tutteli.atrium.api.infix.en_GB/g;'
    

    The following list helps you to migrate faster by using a few regex search replace commands (in Intellij). Make sure you have checked Regex as well as Match Case in the search options. Notice, that the good will certainly not compile after a single replace, you need to carry out all search&replace commands.
    It is not perfect, maybe you need to do a few adjustments in addition, let us now and we improve the search/replace commands here.

    Switch to ExpectImpl.changeSubject instead of using AssertImpl.changeSubject:
    Search: AssertImpl([\n\r\s]*)\.changeSubject\(([^\)\n]+)\)[\n\r\s]*\{[\n\r\s]*subject
    Replace: ExpectImpl$1.changeSubject\($2\)$1.unreported { it

    Search: AssertImpl([\n\r\s]*)\.changeSubject\(([^\)]+)\)
    Replace: ExpectImpl$1.changeSubject\($2\).unreported

    πŸ— builder.descriptive, safe withTest

    πŸ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject
    πŸ— Replace: AssertImpl$1.builder$2.createDescriptive\(plant, $3it

    πŸ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)plant.subject
    πŸ— Replace: AssertImpl$1.builder$2.descriptive$3.withTest\(plant\)$4{$5it

    πŸ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)subject
    πŸ— Replace: AssertImpl$1.builder$2.descriptive$3.withTest\(this\)$4{$5it

    πŸ‘‰ use new feature mechanism

    This one needs extra care as arguments could be function calls. Verify the replacements

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    Replace: feature(\{ f(it::$1) \})$2

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)
    Replace: feature \{ f(it::$1) \}

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)
    Replace: feature($1::$2)

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)
    Replace: feature("$1.$2", { $1.$2 })

    Search: (import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)
    Replace: $1.feature

    toThrow and isA with empty assertionCreator lambda
    Search: (\.| )((?:toThrow|wirft|isA|istEin)<.*>)\s*\{\s*\}
    Replace $1$2()

    notToBeNull with empty assertionCreator lambda
    Search: notToBeNull\s*\{\s*\}
    Replace: notToBe(null)

    migrate custom assertion verbs:

    Search: fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlant\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter\)
    Replace:
    πŸ— fun <T> $1\(subject: T\): Expect<T> = \n ExpectBuilder.forSubject\(subject\)\n .withVerb\($2\)\n .withoutOptions\(\)\n .build\(\)

    Search:
    fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\s*,[\n\r\s]*assertionCreator: Assert<T>.\(\)\s*->\s*Unit\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantAndAddAssertionsCreatedBy\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter,[\n\r\s]*assertionCreator\)
    Replace:
    fun <T> $1\(subject: T, assertionCreator: Expect<T>.\(\) -> Unit\): Expect<T> = \n $1(subject).addAssertionsCreatedBy(assertionCreator)

    Search:
    (?:internal )?fun <T(?:\s*:\s*Any\?)?> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantNullable\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[^\)]+\)
    Replace: (empty string)

    In case the above search&replace did not find anything (because your code is different):
    ♻️ Switch from AssertImpl.coreFactory.newReportingPlant to ExpectBuilder
    πŸ— => see atriumVerbs.kt for an example of how own assertion verbs are defined now; or use the suggested replacements but please add import ch.tutteli.atrium.domain.builders.reporting.ExpectBuilder first as it will not work correctly otherwise due to an Intellij bug
    => Note that you don't need a verb for nullable types any more. Thus:

    • remove the upper bound T: Any
    • remove the verb which uses `newReportingPlantNullable
    • remove the verb which expected act: () -> Unit

    Switch to new built-in assertion verbs which use Expect

    Search: import ch.tutteli.atrium.verbs.(expect|assert|assertThat)
    Replace: import ch.tutteli.atrium.api.verbs.$1

    Switch from AssertImpl to ExpectImpl

    Search: AssertImpl
    Replace: ExpectImpl

    Switch all your assertion functions to use Expect and no longer Assert:

    Search: import ch\.tutteli\.atrium\.creating\.Assert(ionPlant(Nullable)?)?
    Replace: import ch.tutteli.atrium.creating.Expect

    Search: fun Assert(?:ionPlant(?:Nullable)?)?<(.*)>\.
    Replace: fun <T: $1> Expect<T>\.

    Search: Assert(ionPlant(Nullable)?)?<
    Replace: Expect<

    Switch the API

    Search: import ch.tutteli\.atrium\.api\.cc\.(en_GB|de_CH)
    Replace: import ch.tutteli.atrium.api.fluent.$1

    Search: import ch.tutteli\.atrium\.api\.cc\.infix\.en_GB
    Replace: import ch.tutteli.atrium.api.infix.en_GB

    In case you have custom assertion verbs
    Dealing with thrown exceptions is now handled by Expect as well.
    πŸ‘» However, in case you have named the assertion verb differently for expecting an Exception then you have to decide:

    • 🚚 use the same name => rename the corresponding function which expects act: () -> Unit to the same name and remove it afterwards
    • πŸ‘‰ use a different name => delegate the function which expects act: () -> Unit to the other verb

    πŸ— Check if you need to add import ch.tutteli.atrium.domain.builders.reporting.ExpectBuilder

    Try to reduce duplicate Expect imports
    Repeat until you don't have duplicate imports anymore
    Search: import ch\.tutteli\.atrium\.creating\.Expect\n\s*import ch\.tutteli\.atrium\.creating\.Expect
    Replace: import ch.tutteli.atrium.creating.Expect

    ⚠ Try to compile your project and watch out for the following warnings:

    • 'MyClass' is a final type, and thus a value of the type parameter is predetermined
      => you can suppress this warning by adding @file:Suppress("FINAL_UPPER_BOUND") to your file, this is actually a Kotlin bug (https://youtrack.jetbrains.com/issue/KT-34257)
  • v0.9.0-alpha

    August 29, 2019

    This is a pre-release for early adopters using currently cc.en_GB and want to switch to fluent.en_GB which makes use of Expect instead of Assert

    Following the setup instructions:

    dependencies {
        testImplementation "ch.tutteli.atrium:atrium-fluent-en_GB:0.9.0-alpha"
    }
    

    πŸš€ Proper release notes will follow with the final release of 0.9.0, following already the migration guide

    πŸ—„ Migrating deprecated functionality

    πŸš€ In case you migrate from a version < 0.7.0 then please have a look at the migration guide given in the Release Notes of 0.7.0.
    Otherwise you can use the suggested replacements (ALT + Enter -> Replace with ...) or the search/replace patterns shown below.

    πŸ”” Notice, that you don't have to migrate everything at once where asExpect and asAssert allow to switch between the old Assert and the new Expect world.
    Ping us in the Atrium slack channel if you need help.

    Following a few hints how you can migrate faster by using a few regex search replace commands (make sure you have checked Regex in the search) -- it might be the search replace is not enough for your use case and require a few adjustements from your part) :

    Switch all your assertion functions to use Expect and no longer Assert:

    Search: import ch.tutteli.atrium.creating.Assert(ionPlant(Nullable)?)?
    Replace: import ch.tutteli.atrium.creating.Expect

    Repeat until you don't have duplicate imports anymore
    Search: import ch.tutteli.atrium.creating.Expect\n\s*ch.tutteli.atrium.creating.Expect
    Replace: import ch.tutteli.atrium.creating.Expect

    Search: Assert(ionPlant(Nullable)?)?<
    Replace: Expect<

    Switch to ExpectImpl.changeSubject instead of using AssertImpl.changeSubject:
    Search: AssertImpl([\n\r\s]).changeSubject(([)\n]+))[\n\r\s]{[\n\r\s]*subject
    Replace: ExpectImpl$1.changeSubject$1.unreported($2) { it

    Search: AssertImpl([\n\r\s]*).changeSubject(([)]+))
    Replace: ExpectImpl$1changeSubject.unreported($1)

    πŸ— builder.descriptive, safe withTest

    πŸ— Search: AssertImpl([\n\r\s]).builder([\n\r\s]).createDescriptive(([,\n]+,[)]+)[\n\r\s]{[\n\r\s])plant.subject
    πŸ— Replace: AssertImpl$1.builder$2.createDescriptive(plant, $3it

    πŸ— Search: AssertImpl([\n\r\s]).builder([\n\r\s]).descriptive([\n\r\s]).withTest((?[\n\r\s]){([\n\r\s]*)plant.subject
    πŸ— Replace: AssertImpl$1.builder$2.descriptive$3.withTest(plant)$4{$5it

    πŸ— Search: AssertImpl([\n\r\s]).builder([\n\r\s]).descriptive([\n\r\s]).withTest((?[\n\r\s]){([\n\r\s]*)subject
    πŸ— Replace: AssertImpl$1.builder$2.descriptive$3.withTest(this)$4{$5it

    πŸ‘‰ use new feature mechanism

    This one needs extra care as arguments could be function calls. Verify the replacements

    Search: (?:property|returnValueOf|rueckgabewertVon)(subject::([)]+)).
    Replace: feature { f(it::$1) }.

    Search: (?:property|returnValueOf|rueckgabewertVon)(subject::([)]+))(\s*{)
    Replace: feature({ f(it::$1) })$2$

    Search: (?:property|returnValueOf|rueckgabewertVon)(([:]+)::([)]+))
    Replace: feature($1::$2)

    toThrow with empty assertionCreator lambda
    Search: .((?:toThrow|wirft|isA|istEin)<[>]+>)\s*{\s*}
    Replace .$1()

    migrate custom assertion verbs
    ♻️ Switch from AssertImpl.coreFactory.newReportingPlant to ExpectImpl.assertionVerbBuilder
    πŸ‘€ see atriumVerbs.kt for an example

  • v0.8.0

    April 28, 2019

    Jar's can be found here: https://bintray.com/robstoll/tutteli-jars/atrium/0.8.0
    API Maturity : Stable
    Implementation Maturity : Almost Stable

    πŸš€ There won't be any breaking changes in the API (assertion functions/builders) until v1.0.0 besides parameter name renaming. But we want to progress as well and deprecate functionality in each version (e.g quite a lot with 0.7.0; please replace deprecated functionality until v1.0.0 where we will remove it.
    βœ… However, we do not provide yet a stable API for the domain and core modules of Atrium -- it is almost stable, but there might be slight breaking changes which we want to introduce before v1.0.0. That is also the reason why we do not have yet established backward compatibility tests for domain/core. This might affect you if you write your own assertion functions. And it also affects you if you provide your own implementation for parts of Atrium.

    Table of Content

    • πŸ†• New Features
      • API
      • Domain/Core aka write own assertion functions
      • Others
    • πŸ›  Fixes
    • Improvments
    • πŸ’₯ Breaking Changes
    • πŸ—„ Deprecation
    • πŸ—„ Migrating deprecated functionality

    πŸ†• New Features

    API

    • #27 containsExactly as replacement for containsStrictly, thanks to @msmoljan for the implementation and thanks to @christophsturm for the idea
    • #33 isNotBlank for CharSequence, thanks to @pt2121 for the implementation
    • #39 make atLeast optional for CharSequence.contains, thanks to @christophsturm for the idea
    • #51 keys and values for Map to postulate assertions about the keys or values of a Map
    • #37 asEntries for Map, thanks to @arjank for the implementation
    • #29/#75 getExisting for Map to postulate assertions about the value of a corresponding key
    • #28 containskey, #59 containsNotKey for Map, thanks to @uaArsen for the implementation
    • #61/#62 contains for Map
    • #30/#76 get for List
    • #53 notToThrow as counterpart of toThrow - thanks to @charleskorn for the idea
    • #65 shortcut property/fun for Pair.first and Pair.second
    • #25 isKeyValue as well as shortcut property/fun key/value for Map.Entry
    • #69 toBe was opened up for nullable subjects (accepts now Any?), thanks to @dave08 for the discussion
    • #70 toBeNullIfNullElse for nullable subjects, thanks to @dave08 for the idea
    • #71 shortcut property/fun Collection.size
    • #78 containsExactly with single assertion creator
    • #48 asIterable with assertionCreator block
    • #46 o as alternative to this in sub-assertions for the infix API

    Domain / Core

    πŸ”‹ Features for assertion-function-writers:

    • #67 AssertImpl.mapArguments -> to map a variable length argument lists of the form first: T, vararg rest: T (inside a function T, Array<out T>) to R, Array<out R>
    • #72 changeSubject to a nullable type
    • AssertImpl.feature.extractor -> in case you want to make an assertion about a feature which is not always safe to extract (e.g. List.get expects a suitable index)
    • ✨ AssertImpl.collector.collectOrExplain => collects assertions for later usage but only if it is safe to collect them, otherwise it wraps them into an explanatory assertion so that it can be used to enhance reporting
    • AssertImpl.collector.collectNullable which allows to collect assertions for a nullable subject (for AssertionPlantNullable instead of AssertionPlant)

    Others

    • turned Atrium into an multi-platform project; all dependencies are also available for:
      • the JS platform => use the -js suffix; you will have to migrate to bundle en_GB if you still use en_UK, see Migrating deprecated functionality below.
      • the Android platform => use the -android suffix => Thansk to @ultraon for reporting #52 regarding issues with module-info.class
    • πŸ—„ #41 deprecated notToBeNullBut for BigDecimal
    • πŸ—„ #55 infix API - deprecated calls to toBe if a keyword is passed inadvertently
    • βœ… stacktraces in error reporting should no longer contain stack frames of Atrium or test runners

    πŸ›  Fixes

    • none this time

    πŸ‘Œ Improvements

    • DetailedObjectFormatter shows now Kotlin types instead of Java types (e.g. kotlin.Int instead of java.lang.Integer).
    • an AtriumError is now thrown instead of an AssertionError (AtriumError is a subtype of AssertionError)

    πŸ’₯ Breaking Changes

    Planned (previously deprecated or announced)

    • none this time

    Unplaned

    • Made Group, GroupWithoutNullableEntries and GroupWithNullableEntries invariant. I doubt this will be a problem for someone, otherwise let me know
    • Made Value, Values, Entry, Entries invariant; in case you get problems, try to use user-site variance and specify out there
      The following breaking changes only bother you if you implemented an own core. Most have been necessary to turn Atrium into a multi-platform project:
    • core uses now an own implementation of Locale and no longer java.util.Locale
    • core uses now KClass instead of Class
    • TranslatableWithArgs takes a List instead of an Array as parameter
    • Reporter needs to provide an AtriumErrorAdjuster in addition
    • βœ‚ removed duplicate anyAssertions in package ch.tutteli.atrium.domain.creating.any.typetransformation.creators (use the one from package ch.tutteli.atrium.domain.creating)

    tl;dr the following is only of interest if you rely on binary compatibility
    I changed the JvmName of contains? in cc-en_UK and cc-infix-en_UK to containsDeprecated and enthaelt? in cc-de_CH to enthaeltDeprecated due to the DEX compiler for android which cannot handle ? in identifiers. This is a binary backward compatibility break for a method which I introduced in 0.7.0 to retain source backward compatibility. In case you use still use cc-en_UK or cc-infix-en_UK and rely on binary compatibility you will have to recompile when updating to 0.8.0.

    πŸ—„ Deprecation

    🚚 The following was deprecated and will be removed with 1.0.0:

    • Assert<Iterable>.containsStrictly use containsExactly instead.
    • 🌐 ReporterBuilder::withoutTranslations using java.util.Locale => use Atrium's Locale
    • 0️⃣ TranslatorOption::withDefaultTranslator using java.util.Locale => use Atrium's Locale
    • 0️⃣ TextAssertionFormatterOption::withDefaultTextCapabilities => use withTextCapabilities which uses KClass instead of Class
    • πŸ”§ AtriumErrorAdjusterOption::withOnlyFailureReporter and withCustomReporter => new step in configuration, use either withDefaultAtriumErrorAdjusters or choose one of the other options
    • πŸ‘€ all functions containing nullable in their name => their counterpart without nullable in their name where opened up to accept also nullable types (see #60 for details)

    Possible Breaking Changes with 0.9.0

    • I will prepare the transition to Assert<T> instead of Assert<out T>. I will turn Assert into an own type (currently only a type alias) - you should not notice something but it means that the binary code will change when you compile against 0.9.0
    • βœ… I might reuse opentest4j exceptions to improve error reporting in IDEs. For this to work I might have to make modifications to Assertion/AssertionGroup (would only affect core implementors).
    • πŸ‘» An exception will be thrown where one has to define an assertionCreator -- shall prevent kind of dead code/incomplete assertions; for instance assert(mapOf("a" to 1)).keys {}
      • same for addAssertionsCreatedBy/and {}; they will throw an exception if no sub-assertion is defined
    • toBe, contains etc. which expect T where <T: Any> might be restricted to input types, so that comparing apple with oranges is no longer possible without explicitly stating the type. E.g. assert(1).toBe("hello")would be a compile error

    Possible Breaking Changes with 1.0.0

    Please open an issue if you are not happy with one of the changes and state why or contact me via the Atrium slack channel.

    Assert<Throwable>.message{} will return Assert<Throwable> instead of Unit

    Assert<T>.isA{} will return Assert<T> instead of Unit

    All property and returnValueOf taking an assertionCreator will return the same type as the current subject.

    🚚 I will remove out of Assert<out T> in order that things like asssert(1).toBe("hello") is no longer possible, overloads can be simplified etc.

    returnValueOf functions might be renamed to returnValueOfX where X denotes the number of arguments. Too often it occurs that Kotlin is not able to infer the correct overload, the user does not get the appropriate help in code completion or the error message is too big. This should help.

    πŸ”‹ feature assertion functions might require a lambda in the future. This way error reporting does not blow up in the middle of the way because subject is not available. However, there is a bug concerning nullable-features in Kotlin which prevents me from doing it at the moment: https://youtrack.jetbrains.com/issue/KT-23768, please up-vote it.

    A type parameter might be added to AssertionGroup to restrict the AssertionGroupType.

    πŸ“¦ BulletPointIdentifier together with subtypes (AssertionGroupTypes) might be moved to another package: ch.tutteli.atrium.reporting.assertions

    πŸ‘€ AssertionPlant/Assert will switch roles => AssertionPlant will be the typealias of Assert, see #26; should only break binary compatibility

    πŸ‘€ I will introduce interface groups for RepoterBuilder as I did in other cases (e.g. see Descriptive); should only break binary compatibility

    πŸ—„ Migrating deprecated functionality

    πŸš€ In case you migrate from a version < 0.7.0 then please have a look at the migration guide given in the Release Notes of 0.7.0.
    Otherwise you can use the suggested replacements (ALT + Enter -> Replace with ...)

    Ping me in the Atrium slack channel if you need help.