Skip to content

Commit

Permalink
Add attachable assertions
Browse files Browse the repository at this point in the history
  • Loading branch information
bitPogo committed Feb 12, 2022
1 parent eec9976 commit 1c52545
Show file tree
Hide file tree
Showing 12 changed files with 457 additions and 76 deletions.
5 changes: 4 additions & 1 deletion .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ documentation:
- readmeOSS.adoc

# Library
test-utils:
kmock:
- kmock-gradle/**/*

examples:
- examples/**/*
4 changes: 4 additions & 0 deletions .github/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,7 @@
- name: kmock
description: KMock
color: 000080

- name: examples
description: Examples
color: 000080
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@ import tech.antibytes.kmock.KMockContract.Collector
import tech.antibytes.kmock.PropertyMockery
import tech.antibytes.kmock.SyncFunMockery
import tech.antibytes.kmock.Verifier
import tech.antibytes.kmock.assertWasCalledStrictlyWith
import tech.antibytes.kmock.example.ExampleContract.SampleDomainObject
import tech.antibytes.kmock.example.ExampleContract.SampleLocalRepository
import tech.antibytes.kmock.example.ExampleContract.SampleRemoteRepository
import tech.antibytes.kmock.verify
import tech.antibytes.kmock.verifyOrder
import tech.antibytes.kmock.verifyStrictOrder
import tech.antibytes.kmock.wasCalledWithArguments
import tech.antibytes.kmock.wasCalledWithArgumentsStrict
import tech.antibytes.kmock.wasCalledWithoutArguments
import tech.antibytes.kmock.wasGotten
import tech.antibytes.kmock.wasSet
import tech.antibytes.kmock.wasSetTo
import tech.antibytes.kmock.withArguments
import tech.antibytes.kmock.withSameArguments
import tech.antibytes.kmock.withoutArguments
import tech.antibytes.util.test.coroutine.AsyncTestReturnValue
import tech.antibytes.util.test.coroutine.defaultTestContext
import tech.antibytes.util.test.coroutine.runBlockingTestWithTimeout
Expand Down Expand Up @@ -80,22 +81,22 @@ class SampleControllerSpec {
// Then
actual mustBe domainObject

verify(exactly = 1) { remote.fetch.withSameArguments(url) }
verify(exactly = 1) { local.store.withSameArguments(id[1], number) }
verify(exactly = 1) { remote.fetch.wasCalledWithArgumentsStrict(url) }
verify(exactly = 1) { local.store.wasCalledWithArgumentsStrict(id[1], number) }

verifier.verifyStrictOrder {
withSameArguments(remote.fetch, url)
wasCalledWithArgumentsStrict(remote.fetch, url)
wasGotten(domainObject.propId)
wasSet(domainObject.propId)
wasGotten(domainObject.propId)
wasGotten(domainObject.propValue)
withSameArguments(local.store, id[1], number)
wasCalledWithArgumentsStrict(local.store, id[1], number)
}

verifier.verifyOrder {
withArguments(remote.fetch, url)
wasCalledWithArguments(remote.fetch, url)
wasSetTo(domainObject.propId, "42")
withArguments(local.store, id[1])
wasCalledWithArguments(local.store, id[1])
}
}
}
Expand Down Expand Up @@ -134,20 +135,20 @@ class SampleControllerSpec {

delay(20)

verify(exactly = 1) { local.contains.withSameArguments(idOrg) }
verify(exactly = 1) { local.fetch.withSameArguments(id) }
verify(exactly = 1) { remote.find.withSameArguments(idOrg) }
local.contains.assertWasCalledStrictlyWith(1, idOrg)
local.fetch.assertWasCalledStrictlyWith(1, id)
remote.find.assertWasCalledStrictlyWith(1, idOrg)

verifier.verifyStrictOrder {
withSameArguments(local.contains, idOrg)
withSameArguments(remote.find, idOrg)
wasCalledWithArgumentsStrict(local.contains, idOrg)
wasCalledWithArgumentsStrict(remote.find, idOrg)
wasGotten(domainObject.propId)
withSameArguments(local.fetch, id)
wasCalledWithArgumentsStrict(local.fetch, id)
wasSet(domainObject.propId)
}

verifier.verifyOrder {
withoutArguments(local.contains, "abc")
wasCalledWithoutArguments(local.contains, "abc")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ package tech.antibytes.kmock

import tech.antibytes.kmock.KMockContract.GetOrSet

internal fun Array<out Any?>?.withArguments(vararg values: Any?): Boolean {
internal fun Array<out Any?>?.wasCalledWithArguments(vararg values: Any?): Boolean {
return when {
this == null -> values.isEmpty()
values.isEmpty() -> true
else -> values.all { value -> this.contains(value) }
}
}

internal fun Array<out Any?>?.withSameArguments(vararg values: Any?): Boolean {
internal fun Array<out Any?>?.wasCalledWithArgumentsStrict(vararg values: Any?): Boolean {
return this?.contentDeepEquals(values) ?: values.isEmpty()
}

internal fun Array<out Any?>?.withoutArguments(vararg values: Any?): Boolean {
internal fun Array<out Any?>?.wasCalledWithoutArguments(vararg values: Any?): Boolean {
return if (this == null) {
values.isNotEmpty()
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved.
*
* Use of this source code is governed by Apache v2.0
*/

package tech.antibytes.kmock

import tech.antibytes.kmock.KMockContract.FunMockery

fun FunMockery<*, *>.assertWasCalled(
exactly: Int,
) {
verify(exactly = exactly) {
this.wasCalledWithArguments()
}
}

fun FunMockery<*, *>.assertWasCalledWith(
exactly: Int,
vararg arguments: Any?
) {
verify(exactly = exactly) {
this.wasCalledWithArguments(*arguments)
}
}

fun FunMockery<*, *>.assertWasCalledStrictlyWith(
exactly: Int,
vararg arguments: Any?
) {
verify(exactly = exactly) {
this.wasCalledWithArgumentsStrict(*arguments)
}
}

fun FunMockery<*, *>.assertWasNotCalled() {
verify(exactly = 0) {
this.wasCalledWithArguments()
}
}

fun FunMockery<*, *>.assertWasNotCalledWith(vararg illegal: Any?) {
verify(exactly = 0) {
this.wasCalledWithArguments(*illegal)
}
}

/**
* Assertion call for a FunMockery
* Fails if a given argument is referenced in a call
* @param arguments variable amount of nullable Any typed argument
* @throws AssertionError if a call contains a at least one given argument.
*/
fun FunMockery<*, *>.assertWasCalledWithout(
vararg arguments: Any?
) {
verify(atMost = 100) {
this.wasCalledWithoutArguments(*arguments)
}
}

fun KMockContract.PropMockery<*>.assertWasGotten(exactly: Int) {
verify(exactly = exactly) { this.wasGotten() }
}

fun KMockContract.PropMockery<*>.assertWasSet(exactly: Int) {
verify(exactly = exactly) { this.wasSet() }
}

fun KMockContract.PropMockery<*>.assertWasSetTo(exactly: Int, value: Any?) {
verify(exactly = exactly) { this.wasSetTo(value) }
}
11 changes: 11 additions & 0 deletions kmock/src/commonMain/kotlin/tech/antibytes/kmock/Verification.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ private infix fun VerificationHandle.mustBeAtMost(value: Int) {
}
}

/**
* Verification function for a given assertion.
* Note:<ul>
* <li>any boundary parameter will cause a failure in combination with wasCalledWithoutArguments if at least one argument matches.</li>
* </ul>
* @param exactly Int or null - the exact amount of calls. This parameter overrides atLeast and atMost. Use null to deactivate the criteria.
* @param atLeast Int - the minimum amount of calls.
* @param atMost Int or null - the maximum amount of calls. Use null to deactivate the criteria.
* @param action ArgumentMatcher - Primary criteria to select VerificationHandles.
* @throws AssertionError if it does not match the given criteria.
*/
fun verify(
exactly: Int? = null,
atLeast: Int = 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ class VerificationChainBuilder : KMockContract.VerificationHandleContainer {
override fun toList(): List<VerificationHandle> = handles.toList()
}

fun VerificationChainBuilder.withArguments(mockery: FunMockery<*, *>, vararg arguments: Any?) {
this.add(mockery.withArguments(*arguments))
fun VerificationChainBuilder.wasCalledWithArguments(mockery: FunMockery<*, *>, vararg arguments: Any?) {
this.add(mockery.wasCalledWithArguments(*arguments))
}

fun VerificationChainBuilder.withSameArguments(mockery: FunMockery<*, *>, vararg arguments: Any?) {
this.add(mockery.withSameArguments(*arguments))
fun VerificationChainBuilder.wasCalledWithArgumentsStrict(mockery: FunMockery<*, *>, vararg arguments: Any?) {
this.add(mockery.wasCalledWithArgumentsStrict(*arguments))
}

fun VerificationChainBuilder.withoutArguments(mockery: FunMockery<*, *>, vararg arguments: Any?) {
this.add(mockery.withoutArguments(*arguments))
fun VerificationChainBuilder.wasCalledWithoutArguments(mockery: FunMockery<*, *>, vararg arguments: Any?) {
this.add(mockery.wasCalledWithoutArguments(*arguments))
}

fun VerificationChainBuilder.wasGotten(mockery: KMockContract.PropMockery<*>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,19 @@ private fun <T> traverseMock(
return VerificationHandle(mock.id, callIndices)
}

fun FunMockery<*, *>.withArguments(
fun FunMockery<*, *>.wasCalledWithArguments(
vararg values: Any?
): VerificationHandle = traverseMock(this) { withArguments(*values) }
): VerificationHandle = traverseMock(this) {
wasCalledWithArguments(*values)
}

fun FunMockery<*, *>.withSameArguments(
fun FunMockery<*, *>.wasCalledWithArgumentsStrict(
vararg values: Any?
): VerificationHandle = traverseMock(this) { withSameArguments(*values) }
): VerificationHandle = traverseMock(this) { wasCalledWithArgumentsStrict(*values) }

fun FunMockery<*, *>.withoutArguments(
fun FunMockery<*, *>.wasCalledWithoutArguments(
vararg values: Any?
): VerificationHandle = traverseMock(this) { withoutArguments(*values) }
): VerificationHandle = traverseMock(this) { wasCalledWithoutArguments(*values) }

fun PropMockery<*>.wasGotten(): VerificationHandle = traverseMock(this) { wasGotten() }

Expand Down
Loading

0 comments on commit 1c52545

Please sign in to comment.