add feature assertions to the new infix API

This commit is contained in:
Robert Stoll
2020-02-29 20:28:51 +01:00
parent a352716d1f
commit 382ce93bdb
11 changed files with 388 additions and 63 deletions

View File

@@ -42,7 +42,8 @@ fun <T, R> Expect<T>.feature(
* creates a new [Expect] for it and
* returns it so that subsequent calls are based on the feature.
*
* @return The newly created [Expect] for the return value of calling [f] on the current subject of the assertion.
* @return The newly created [Expect] for the return value of calling the method [f]
* on the current subject of the assertion.
*
* @since 0.9.0
*/
@@ -72,7 +73,8 @@ fun <T, R> Expect<T>.feature(
* creates a new [Expect] for it and
* returns it so that subsequent calls are based on the feature.
*
* @return The newly created [Expect] for the return value of calling [f] on the current subject of the assertion.
* @return The newly created [Expect] for the return value of calling the method [f]
* on the current subject of the assertion.
*
* @since 0.9.0
*/
@@ -106,7 +108,8 @@ fun <T, A1, R> Expect<T>.feature(
* creates a new [Expect] for it and
* returns it so that subsequent calls are based on the feature.
*
* @return The newly created [Expect] for the return value of calling [f] on the current subject of the assertion.
* @return The newly created [Expect] for the return value of calling the method [f]
* on the current subject of the assertion.
*
* @since 0.9.0
*/
@@ -140,7 +143,8 @@ fun <T, A1, A2, R> Expect<T>.feature(
* creates a new [Expect] for it and
* returns it so that subsequent calls are based on the feature.
*
* @return The newly created [Expect] for the return value of calling [f] on the current subject of the assertion.
* @return The newly created [Expect] for the return value of calling the method [f]
* on the current subject of the assertion.
*
* @since 0.9.0
*/
@@ -174,7 +178,8 @@ fun <T, A1, A2, A3, R> Expect<T>.feature(
* creates a new [Expect] for it and
* returns it so that subsequent calls are based on the feature.
*
* @return The newly created [Expect] for the return value of calling [f] on the current subject of the assertion.
* @return The newly created [Expect] for the return value of calling the method [f]
* on the current subject of the assertion.
*
* @since 0.9.0
*/

View File

@@ -171,10 +171,11 @@ inline infix fun <T> Expect<T>.and(@Suppress("UNUSED_PARAMETER") o: o): Expect<T
*
* @return An [Expect] for the current subject of the assertion.
*/
infix fun <T> Expect<T>.and(assertionCreator: Expect<T>.() -> Unit) = addAssertionsCreatedBy(assertionCreator)
infix fun <T> Expect<T>.and(assertionCreator: Expect<T>.() -> Unit): Expect<T> =
addAssertionsCreatedBy(assertionCreator)
/**
* Inline property referring actually to `this` and allows to write nicer sub-assertions.
* Inline property referring actually to `this` and allows to write infix assertions within an assertion group block
*
* For instance, instead of:
* ```

View File

@@ -23,7 +23,7 @@ interface ListGetStep<E, T : List<E>> {
/**
* Makes the assertion that the given [index] is within the bounds of [Expect.subject] and that
* the corresponding entry holds all assertions the given [assertionCreator] might create for it.
* the corresponding entry holds all assertions the given [assertionCreator] creates for it.
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if a created [Expect]s (by calling [assertionCreator])

View File

@@ -4,11 +4,11 @@ import ch.tutteli.atrium.api.infix.en_GB.creating.list.get.builders.ListGetStep
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl
internal class ListGetStepImpl<E, T: List<E>>(
internal class ListGetStepImpl<E, T : List<E>>(
override val expect: Expect<T>,
override val index: Int
) : ListGetStep<E, T> {
override infix fun assertIt(assertionCreator: Expect<E>.() -> Unit): Expect<T>
= expect.addAssertion(ExpectImpl.list.get(expect, index).collect(assertionCreator))
override infix fun assertIt(assertionCreator: Expect<E>.() -> Unit): Expect<T> =
ExpectImpl.list.get(expect, index).addToInitial(assertionCreator)
}

View File

@@ -0,0 +1,271 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.assertions.AssertionGroup
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.FeatureExpect
import ch.tutteli.atrium.domain.builders.ExpectImpl
import ch.tutteli.atrium.domain.builders.creating.MetaFeatureOption
import ch.tutteli.atrium.domain.creating.MetaFeature
import kotlin.reflect.*
/**
* Extracts the [property] out of the current subject of the assertion,
* creates a new [Expect] for it and
* returns it so that subsequent calls are based on the feature.
*
* @return The newly created [Expect] for the given [property].
*
* @since 0.10.0
*/
infix fun <T, R> Expect<T>.feature(property: KProperty1<T, R>): FeatureExpect<T, R> =
ExpectImpl.feature.property(this, property).getExpectOfFeature()
/**
* Extracts the value which is returned when calling the method [f] on the current subject of the assertion,
* creates a new [Expect] for it and
* returns it so that subsequent calls are based on the feature.
*
* Use `feature of(...)` in case the method requires parameters or in case you want to define
* an assertion group block for it.
*
* @return The newly created [Expect] for the return value of calling the method [f]
* on the current subject of the assertion.
*
* @since 0.10.0
*/
infix fun <T, R> Expect<T>.feature(f: KFunction1<T, R>): FeatureExpect<T, R> =
ExpectImpl.feature.f0(this, f).getExpectOfFeature()
/**
* Extracts a feature out of the current subject of the assertion using the given [Feature.extractor],
* creates a new [Expect] for it and
* returns it so that subsequent calls are based on the feature.
*
* Use `of(K..., ...)` to create this representation where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX` and potentially the required arguments for a `KFunctionX` where `X` > 1.
*
* @return The newly created [Expect] for the extracted feature.
*
* @since 0.10.0
*/
infix fun <T, R> Expect<T>.feature(of: Feature<T, R>): FeatureExpect<T, R> =
ExpectImpl.feature.manualFeature(this, of.description, of.extractor).getExpectOfFeature()
/**
* Extracts a feature out of the current subject of the assertion using the given [FeatureWithCreator.extractor],
* creates a new [Expect] for it,
* applies an assertion group based on the given [FeatureWithCreator.assertionCreator] for the feature and
* returns the initial [Expect] with the current subject.
*
* Use `of(K..., ...) { ... }` to create this representation where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX`, the last an `assertionCreator`-lambda and the remaining arguments in-between the
* required arguments in case of a `KFunctionX` where `X` > 1.
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] in case the created [AssertionGroup] does not hold.
*
* @since 0.10.0
*/
infix fun <T, R> Expect<T>.feature(of: FeatureWithCreator<T, R>): Expect<T> =
ExpectImpl.feature.manualFeature(this, of.description, of.extractor).addToInitial(of.assertionCreator)
/**
* Extracts a feature out of the current subject of the assertion,
* based on the given [provider],
* creates a new [Expect] for it and
* returns it so that subsequent calls are based on the feature.
*
* @param provider Creates a [MetaFeature] where the subject of the assertion is available via
* implicit parameter `it`. Usually you use [f][MetaFeatureOption.f] to create a [MetaFeature],
* e.g. `feature { f(it::size) }`
*
* @return The newly created [Expect] for the extracted feature.
*
* @since 0.10.0
*/
infix fun <T, R> Expect<T>.feature(provider: MetaFeatureOption<T>.(T) -> MetaFeature<R>): FeatureExpect<T, R> =
ExpectImpl.feature.genericSubjectBasedFeature(this) { MetaFeatureOption(this).provider(it) }.getExpectOfFeature()
/**
* Extracts a feature out of the current subject of the assertion,
* based on the given [MetaFeatureOptionWithCreator]
* creates a new [Expect] for it,
* applies an assertion group based on the given [MetaFeatureOptionWithCreator.assertionCreator] for the feature and
* returns the initial [Expect] with the current subject.
*
* Note that you need to enable the new type inference of Kotlin (or use Kotlin 1.4 and above) in order that Kotlin
* is able to infer the types.
* As workaround you can use the overload which expects `MetaFeatureOption<T>.(T) -> MetaFeature<R>`
* and use `it` after the call (import from the package workaround). For instance:
*
* ```
* // use
* expect(person) feature { f(it::age) } it { o toBe 20 }
*
* // instead of (which causes problems with Kotlin < 1.4)
* expect(person) feature of({ f(it::age) }) { o toBe 20 }
* ```
*
* @param of Use the function `of({ ... }) { ... }` to create the [MetaFeatureOptionWithCreator] where the first
* argument is a lambda with a [MetaFeatureOption] as receiver which has to create a [MetaFeature]
* where the subject of the assertion is available via implicit parameter `it`.
* Usually you use [f][MetaFeatureOption.f] to create a [MetaFeature],
* e.g. `feature of({ f(it::size) }) { o toBe 3 }`
*
* @return An [Expect] for the current subject of the assertion.
* @since 0.10.0
*/
infix fun <T, R> Expect<T>.feature(of: MetaFeatureOptionWithCreator<T, R>): Expect<T> =
ExpectImpl.feature.genericSubjectBasedFeature(this) {
MetaFeatureOption(this).(of.provider)(it)
}.addToInitial(of.assertionCreator)
/**
* Creates a [MetaFeature] using the given [provider] and [description].
*
* This can be used to create complex features with a custom description or as workaround where Kotlin is not able to
* infer the types properly.
*
* For instance:
* ```
* expect(person) feature { f("first underage child", it.children.first { it < 18 }) }
* ```
*
* @return The newly created [MetaFeature].
*/
@Suppress("unused" /* unused receiver, but that's fine */)
fun <T, R> MetaFeatureOption<T>.f(description: String, provider: R): MetaFeature<R> =
MetaFeature(description, provider)
/**
* Parameter object which contains a [description] of a feature along with an [extractor]
* which actually extracts the feature out of a subject of an assertion.
*
* Use `of(K..., ...) { ... }` to create this representation where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX` and the remaining arguments are the required arguments in case of a `KFunctionX`
* where `X` > 1.
*
* @property description The description of the feature.
* @property extractor The extractor which extracts the feature out of the subject of the assertion.
* @since 0.10.0
*/
data class Feature<T, R>(val description: String, val extractor: (T) -> R)
/**
* Parameter object which contains a [description] of a feature along with an [extractor]
* which actually extracts the feature out of a subject of an assertion + an [assertionCreator]
* which defines assertions for the feature.
*
* Use `of(K..., ...) { ... }` to create this representation where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX`, the last an [assertionCreator]-lambda and the remaining arguments in-between the
* required arguments in case of a `KFunctionX` where `X` > 1.
*
* @property description The description of the feature.
* @property extractor The extractor which extracts the feature out of the subject of the assertion.
* @property assertionCreator The `assertionCreator`-lambda which defines assertions for the feature.
*
* @since 0.10.0
*/
data class FeatureWithCreator<T, R>(
val description: String,
val extractor: (T) -> R,
val assertionCreator: Expect<R>.() -> Unit
)
//@formatter:off
/**
* Helper function to create a [Feature] based on a [KFunction2] + arguments.
*/
fun <T, A1, R> of(f: KFunction2<T, A1, R>, a1: A1): Feature<T, R> =
Feature(f.name) { f.invoke(it, a1) }
/**
* Helper function to create a [Feature] based on a [KFunction3] + arguments.
*/
fun <T, A1, A2, R > of(f: KFunction3<T, A1, A2, R>, a1: A1, a2: A2): Feature<T, R> =
Feature(f.name) { f.invoke(it, a1, a2) }
/**
* Helper function to create a [Feature] based on a [KFunction4] + arguments.
*/
fun <T, A1, A2, A3, R> of(f: KFunction4<T, A1, A2, A3, R>, a1: A1, a2: A2, a3: A3): Feature<T, R> =
Feature(f.name) { f.invoke(it, a1, a2, a3) }
/**
* Helper function to create a [Feature] based on a [KFunction5] + arguments.
*/
fun <T, A1, A2, A3, A4, R> of(f: KFunction5<T, A1, A2, A3, A4, R>, a1: A1, a2: A2, a3: A3, a4: A4): Feature<T, R> =
Feature(f.name) { f.invoke(it, a1, a2, a3, a4) }
/**
* Helper function to create a [Feature] based on a [KFunction5] + arguments.
*/
fun <T, A1, A2, A3, A4, A5, R> of(f: KFunction6<T, A1, A2, A3, A4, A5, R>, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5): Feature<T, R> =
Feature(f.name) { f.invoke(it, a1, a2, a3, a4, a5) }
/**
* Helper function to create a [FeatureWithCreator] based on a [KProperty1] + [assertionCreator].
*/
fun <T, R> of(property: KProperty1<T, R>, assertionCreator: Expect<R>.() -> Unit): FeatureWithCreator<T, R> =
FeatureWithCreator(property.name, { property.invoke(it) }, assertionCreator)
/**
* Helper function to create a [FeatureWithCreator] based on a [KFunction1] + [assertionCreator].
*/
fun <T, R> of(f: KFunction1<T, R>, assertionCreator: Expect<R>.() -> Unit): FeatureWithCreator<T, R> =
FeatureWithCreator(f.name, { f.invoke(it) }, assertionCreator)
/**
* Helper function to create a [FeatureWithCreator] based on a [KFunction2] + arguments + [assertionCreator].
*/
fun <T, A1, R> of(f: KFunction2<T, A1, R>, a1: A1, assertionCreator: Expect<R>.() -> Unit): FeatureWithCreator<T, R> =
FeatureWithCreator(f.name, { f.invoke(it, a1) }, assertionCreator)
/**
* Helper function to create a [FeatureWithCreator] based on a [KFunction3] + arguments + [assertionCreator].
*/
fun <T, A1, A2, R > of(f: KFunction3<T, A1, A2, R>, a1: A1, a2: A2, assertionCreator: Expect<R>.() -> Unit): FeatureWithCreator<T, R> =
FeatureWithCreator(f.name, { f.invoke(it, a1, a2) }, assertionCreator)
/**
* Helper function to create a [FeatureWithCreator] based on a [KFunction4] + arguments + [assertionCreator].
*/
fun <T, A1, A2, A3, R> of(f: KFunction4<T, A1, A2, A3, R>, a1: A1, a2: A2, a3: A3, assertionCreator: Expect<R>.() -> Unit): FeatureWithCreator<T, R> =
FeatureWithCreator(f.name, { f.invoke(it, a1, a2, a3) }, assertionCreator)
/**
* Helper function to create a [FeatureWithCreator] based on a [KFunction5] + arguments + [assertionCreator].
*/
fun <T, A1, A2, A3, A4, R> of(f: KFunction5<T, A1, A2, A3, A4, R>, a1: A1, a2: A2, a3: A3, a4: A4, assertionCreator: Expect<R>.() -> Unit): FeatureWithCreator<T, R> =
FeatureWithCreator(f.name, { f.invoke(it, a1, a2, a3, a4) }, assertionCreator)
//@formatter:on
/**
* Parameter object which combines a lambda with a [MetaFeatureOption] receiver (called [provider])
* and an [assertionCreator].
*
* Use the function `of({ ... }) { ... }` to create this representation where the first
* argument is a lambda with a [MetaFeatureOption] as receiver which has to create a [MetaFeature]
* where the subject of the assertion is available via implicit parameter `it`.
* Usually you use [f][MetaFeatureOption.f] to create a [MetaFeature],
* e.g. `feature of({ f(it::size) }) { o toBe 3 }`
*
* @since 0.10.0
*/
data class MetaFeatureOptionWithCreator<T, R>(
val provider: MetaFeatureOption<T>.(T) -> MetaFeature<R>,
val assertionCreator: Expect<R>.() -> Unit
)
/**
* Helper function to create a [MetaFeatureOptionWithCreator] based on a lambda with
* [MetaFeatureOption] receiver (has to return a [MetaFeature]) and an [assertionCreator].
*/
fun <T, R> of(
provider: MetaFeatureOption<T>.(T) -> MetaFeature<R>,
assertionCreator: Expect<R>.() -> Unit
): MetaFeatureOptionWithCreator<T, R> = MetaFeatureOptionWithCreator(provider, assertionCreator)

View File

@@ -27,40 +27,48 @@ object Blank : Keyword
/**
* Represents the pseudo keyword `contain` as in [to] `contain`.
* It can be used for a parameter less function so that it has one parameter and thus can be used as infix function.
*/
object contain : Keyword
/**
* Represents the pseudo keyword `case` as in [ignoring] `case`.
* It can be used for a parameter less function so that it has one parameter and thus can be used as infix function.
*/
object case : Keyword
/**
* Represents the pseudo keyword `entries` as in [grouped] `entries`.
* It can be used for a parameter less function so that it has one parameter and thus can be used as infix function.
*/
object entries : Keyword
/**
* Represents the pseudo keyword `group` as in [within] `group`.
* It can be used for a parameter less function so that it has one parameter and thus can be used as infix function.
*/
object group : Keyword
/**
* Represents a filler, a pseudo keyword where there isn't really a good keyword.
* A reader should skip this filler without reading it. For instance, `contains o atleast 1...` should be read as
* A reader should skip this filler without reading it. For instance, `contains o atLeast 1...` should be read as
* `contains at least once...`
*
* It can be used for a parameter less function so that it has one parameter and thus can be used as infix function.
*
* @since 0.10.0
*/
object o : Keyword
/**
* Represents the pseudo keyword `only` as in [and] `only`.
* It can be used for a parameter less function so that it has one parameter and thus can be used as infix function.
*/
object only : Keyword
/**
* Represents the pseudo keyword `order` as in [inAny] `order`.
* It can be used for a parameter less function so that it has one parameter and thus can be used as infix function.
*/
object order : Keyword

View File

@@ -3,32 +3,34 @@ package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.utils.VarArgHelper
/**
* Parameter object to express `T, vararg T`.
*/
class All<out T>(override val expected: T, override vararg val otherExpected: T) : VarArgHelper<T>
/**
* Wrapper for a single index -- can be used as distinguishable type for an overload where Int is already in use.
*/
data class Index(val index: Int)
data class Key<out K>(val key: K)
/**
* Parameter object to express `T, vararg T` in the infix-api.
*/
class All<out T>(override val expected: T, override vararg val otherExpected: T) : VarArgHelper<T>
/**
* Parameter object to express `Pair<K, V>, vararg Pair<K, V>` in the infix-api.
*/
class Pairs<out K, out V>(
override val expected: Pair<K, V>,
override vararg val otherExpected: Pair<K, V>
) : VarArgHelper<Pair<K, V>>
/**
* Parameter object to express a key/value [Pair] whose value type is a lambda with an
* [Assert][AssertionPlant] receiver, which means one can either pass a lambda or `null`.
* [Expect] receiver, which means one can either pass a lambda or `null`.
*/
data class KeyValue<out K, V : Any>(val key: K, val valueAssertionCreatorOrNull: (Expect<V>.() -> Unit)?) {
fun toPair(): Pair<K, (Expect<V>.() -> Unit)?> = key to valueAssertionCreatorOrNull
override fun toString(): String =
"KeyValue(key=$key, value=${if (valueAssertionCreatorOrNull == null) "null" else "lambda"})"
}
/**
* Parameter object to express `Pair<K, V>, vararg Pair<K, V>`.
*/
class Pairs<out K, out V>(
override val expected: Pair<K, V>,
override vararg val otherExpected: Pair<K, V>
) : VarArgHelper<Pair<K, V>>

View File

@@ -1,3 +1,5 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl.changeSubject
@@ -9,8 +11,8 @@ import ch.tutteli.atrium.domain.builders.ExpectImpl.changeSubject
*
* @return The newly created [Expect] for the transformed subject.
*/
fun <E, T : Sequence<E>> Expect<T>.asIterable(): Expect<Iterable<E>>
= changeSubject(this).unreported { it.asIterable() }
fun <E, T : Sequence<E>> Expect<T>.asIterable(): Expect<Iterable<E>> =
changeSubject(this).unreported { it.asIterable() }
/**
* Expects that the subject of the assertion holds all assertions the given [assertionCreator] creates for

View File

@@ -0,0 +1,25 @@
package ch.tutteli.atrium.api.infix.en_GB.workaround
import ch.tutteli.atrium.api.infix.en_GB.and
import ch.tutteli.atrium.creating.Expect
/**
* Can be used to create a group of sub assertions when using the fluent API.
*
* Intended to be used in combination with feature assertions where Kotlin < 1.4 is not able to infer the correct type.
* For instance:
* ```
* // use
* expect(person) feature { f(it::age) } it { o toBe 20 }
*
* // instead of (which causes problems with Kotlin < 1.4)
* expect(person) feature of({ f(it::age) }) { o toBe 20 }
* ```
*
* Note that this workaround will be removed in some minor version after a major version with Kotlin 1.4 support
* (most likely with Atrium v1.1.0 where Atrium v1.0.0 requires Kotlin 1.4)
*
* @return An [Expect] for the current subject of the assertion.
*/
@Suppress("NOTHING_TO_INLINE" /* inline so that one does not actually call `it` on binary level */)
inline infix fun <T> Expect<T>.it(noinline assertionCreator: Expect<T>.() -> Unit): Expect<T> = and(assertionCreator)

View File

@@ -80,23 +80,16 @@ object NewFeatureAssertionsBuilder : NewFeatureAssertions {
description: Translatable,
provider: T.() -> R
): ExtractedFeaturePostStep<T, R> =
genericFeature(expect, createMetaFeature(expect, description, provider))
genericFeature(expect, ExpectImpl.feature.meta.create(expect, description, provider))
fun <T, R> genericSubjectBasedFeature(
expect: Expect<T>,
provider: (T) -> MetaFeature<R>
): ExtractedFeaturePostStep<T, R> = ExpectImpl.feature.genericFeature(
expect,
expect.maybeSubject.fold(this::createFeatureSubjectNotDefined) { provider(it) }
ExpectImpl.feature.meta.createSubjectBased(expect, provider)
)
private fun <R> createFeatureSubjectNotDefined(): MetaFeature<R> =
MetaFeature(
ErrorMessages.DEDSCRIPTION_BASED_ON_SUBJECT,
RawString.create(ErrorMessages.REPRESENTATION_BASED_ON_SUBJECT_NOT_DEFINED),
None
)
override inline fun <T, R> genericFeature(
expect: Expect<T>,
metaFeature: MetaFeature<R>
@@ -107,30 +100,7 @@ object NewFeatureAssertionsBuilder : NewFeatureAssertions {
description: String,
provider: (T) -> R
): ExtractedFeaturePostStep<T, R> =
genericFeature(expect, createMetaFeature(expect, description, provider))
private fun <T, R> createMetaFeature(
expect: Expect<T>,
description: String,
provider: (T) -> R
): MetaFeature<R> = createMetaFeature(expect, Untranslatable(description), provider)
private fun <T, R> createMetaFeature(
expect: Expect<T>,
description: Translatable,
provider: (T) -> R
): MetaFeature<R> {
return expect.maybeSubject.fold({
MetaFeature(
description,
RawString.create(ErrorMessages.REPRESENTATION_BASED_ON_SUBJECT_NOT_DEFINED),
None
)
}) {
val prop = provider(it)
MetaFeature(description, prop, Some(prop))
}
}
genericFeature(expect, ExpectImpl.feature.meta.create(expect, description, provider))
/**
* Returns [MetaFeatureBuilder] which helps to create a [MetaFeature].
@@ -143,6 +113,7 @@ object NewFeatureAssertionsBuilder : NewFeatureAssertions {
* into an overload ambiguity, then either [p] (for property) or one of the `fN` functions (e.g. [f2] for
* a function which expects 2 arguments).
*/
//TODO move to API, this could potentially be different per API
class MetaFeatureOption<T>(private val expect: Expect<T>) {
/**
@@ -338,4 +309,43 @@ object MetaFeatureBuilder {
fun <A1, A2, A3, A4, A5, R> f5(expect: Expect<*>, f: KFunction5<A1, A2, A3, A4, A5, R>, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) =
MetaFeature(coreFactory.newMethodCallFormatter().formatCall(f.name, arrayOf(a1, a2, a3, a4, a5)), f.invoke(a1, a2, a3, a4, a5))
//@formatter:on
/**
* creates a [MetaFeature] which is entirely based on the subject (i.e. also the description).
*/
fun <T, R> createSubjectBased(
expect: Expect<T>,
provider: (T) -> MetaFeature<R>
): MetaFeature<R> = expect.maybeSubject.fold(this::createFeatureSubjectNotDefined) { provider(it) }
private fun <R> createFeatureSubjectNotDefined(): MetaFeature<R> =
MetaFeature(
ErrorMessages.DEDSCRIPTION_BASED_ON_SUBJECT,
RawString.create(ErrorMessages.REPRESENTATION_BASED_ON_SUBJECT_NOT_DEFINED),
None
)
fun <T, R> create(
expect: Expect<T>,
description: String,
provider: (T) -> R
): MetaFeature<R> = create(expect, Untranslatable(description), provider)
fun <T, R> create(
expect: Expect<T>,
description: Translatable,
provider: (T) -> R
): MetaFeature<R> {
return expect.maybeSubject.fold({
MetaFeature(
description,
RawString.create(ErrorMessages.REPRESENTATION_BASED_ON_SUBJECT_NOT_DEFINED),
None
)
}) {
val feature = provider(it)
MetaFeature(description, feature, Some(feature))
}
}
}

View File

@@ -4,6 +4,7 @@ import ch.tutteli.atrium.core.None
import ch.tutteli.atrium.core.Option
import ch.tutteli.atrium.core.Some
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.FeatureExpect
import ch.tutteli.atrium.domain.builders.creating.changers.FeatureExtractorBuilder
import ch.tutteli.atrium.domain.builders.creating.changers.FeatureOptions
import ch.tutteli.atrium.domain.creating.changers.ExtractedFeaturePostStep
@@ -68,13 +69,13 @@ class FinalStepImpl<T, R>(
extractAndApply = { assertionCreator -> extractIt(this, Some(assertionCreator)) }
)
private fun extractIt(expect: Expect<T>, subAssertions: Option<Expect<R>.() -> Unit>) =
private fun extractIt(expect: Expect<T>, maybeSubAssertions: Option<Expect<R>.() -> Unit>): FeatureExpect<T, R> =
featureExtractor.extract(
expect,
featureOptions?.description ?: featureExtractionStep.description,
featureExtractionStep.representationForFailure,
featureExtraction,
subAssertions,
maybeSubAssertions,
featureOptions?.representationInsteadOfFeature
)
}