Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add conditional invert function with parameter to conditionally invert #2658

Closed
Tracked by #2678
dalewking opened this issue Nov 20, 2021 · 5 comments · Fixed by #2772
Closed
Tracked by #2678

Add conditional invert function with parameter to conditionally invert #2658

dalewking opened this issue Nov 20, 2021 · 5 comments · Fixed by #2772
Labels
assertions 🔍 Related to the assertion mechanisms within the testing framework. enhancement ✨ Suggestions for adding new features or improving existing ones. good-first-issue 👶 Suitable for newcomers looking to contribute to the project.
Milestone

Comments

@dalewking
Copy link
Contributor

dalewking commented Nov 20, 2021

Sometimes you need to conditionally invert a matcher based on some data, so depending on the data you might need to invert the matcher or you may not. It occurs to me (and am writing an extension function to do it myself) that it would be hander if there was a member or extension function that did this:

fun <T> Matcher<T>.invertIf(invert: Boolean): Matcher<T> = if(invert) invert() else this

@dalewking dalewking added the enhancement ✨ Suggestions for adding new features or improving existing ones. label Nov 20, 2021
@dalewking dalewking changed the title Matcher invert function should take a parameter Matcher invert function should take a parameter to conditionally invert Nov 20, 2021
@sksamuel
Copy link
Member

sksamuel commented Nov 20, 2021 via email

@dalewking dalewking changed the title Matcher invert function should take a parameter to conditionally invert Add conditional invert function with parameter to conditionally invert Nov 20, 2021
@dalewking
Copy link
Contributor Author

In my case I am using reflection to verify the characteristics of some properties on a data class are defined correctly. This is a boiled down version of my code. Say you have this data class:

data class Property(
    val property: KProperty0<*>,
    val expectedName: String,
    val shouldBeNullable: Boolean = false,
    val shouldBeMutable: Boolean = false,
)

I might use it like this:

class MyTests : FunSpec({
  context("Properties should be defined correctly") {
    withData(
      Property(MyClass::foo, "foo"),
      Property(MyClass::mutableProperty, "mutableProperty", mutable = true),
      Property(MyClass::nullableProperty, "nullableProperty", nullable = true),
    ) { (property, name, nullable, mutable) ->
      property.asClue {
          it shouldBeNamed name
          it should beNotNullable.invertIf(nullable)
          it should beImmutable.invertIf(mutable)
    }
  }
})

which is using some reflective matchers I created on KProperty (let me know if you would like them to add to the library)

@sksamuel
Copy link
Member

Makes sense to add these I think.

@dalewking
Copy link
Contributor Author

I fixed the definition in the first comment that swallowed the generic parameters.

And if you are interested these are the reflective property matchers I mentioned:

fun beNullable() = Matcher<KProperty<*>> {
    MatcherResult(
        it.returnType.isMarkedNullable,
        "Property $it return type should be nullable",
        "Property $it return type should not be nullable",
    )
}

fun beNotNullable() = beNullable().invert()

fun beMutable() = Matcher<KProperty<*>> {
    MatcherResult(
        it is KMutableProperty<*>,
        "Property $it return type should be mutable",
        "Property $it return type should be immutable",
    )
}

fun beImmutable() = beMutable().invert()

fun beNamed(expectedName: String) = Matcher<KProperty<*>> {
    MatcherResult(
        it.name == expectedName,
        "Property $it should be named $expectedName",
        "Property $it should not be named $expectedName",
    )
}

fun KProperty<*>.shouldBeNullable() = this should beNullable()
fun KProperty<*>.shouldBeNotNullable() = this should beNullable().invert()
fun KProperty<*>.shouldBeMutable() = this should beMutable()
fun KProperty<*>.shouldBeImmutable() = this should beImmutable()
infix fun KProperty<*>.shouldBeNamed(expectedName: String) = this should beNamed(expectedName)

fun <T, V> T.shouldHave(property: KProperty1<T, V>, matcher: Matcher<V>): T =
    this.apply {
        withClue("Property $property") {
            property.get(this) shouldHave matcher
        }
    }

fun <T, V> T.shouldHave(property: KProperty1<T, V>, it: V): T =
    this.apply {
        withClue("Property $property") {
            property.get(this) shouldBe it
        }
    }

@sksamuel sksamuel added assertions 🔍 Related to the assertion mechanisms within the testing framework. good-first-issue 👶 Suitable for newcomers looking to contribute to the project. labels Nov 27, 2021
@sksamuel sksamuel added this to the 5.1 milestone Nov 27, 2021
@sksamuel sksamuel mentioned this issue Nov 27, 2021
16 tasks
sksamuel added a commit that referenced this issue Jan 12, 2022
sksamuel added a commit that referenced this issue Jan 12, 2022
@sksamuel
Copy link
Member

Will be included in 5.1.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
assertions 🔍 Related to the assertion mechanisms within the testing framework. enhancement ✨ Suggestions for adding new features or improving existing ones. good-first-issue 👶 Suitable for newcomers looking to contribute to the project.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants