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 clear and direct assertions #5

Merged
merged 4 commits into from
Feb 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Expand Up @@ -132,6 +132,11 @@ abstract class FunMockery<ReturnValue, SideEffect : Function<ReturnValue>>(
override fun getArgumentsForCall(callIndex: Int): Array<out Any?>? = arguments.access { it[callIndex] }

override fun clear() {
TODO("Not yet implemented")
_returnValue.set(null)
_returnValues.set(null)
_sideEffect.set(null)
_calls.set(0)
provider.set(PROVIDER.NO_PROVIDER)
arguments.access { it.clear() }
}
}
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) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ class PropertyMockery<Value>(
override fun getArgumentsForCall(callIndex: Int): GetOrSet = arguments.access { it[callIndex] }

override fun clear() {
TODO("Not yet implemented")
provider.set(null)
_get.set(null)
_getMany.set(null)
_set.set { /*Do Nothing on Default*/ }
_calls.set(0)
arguments.access { it.clear() }
}
}
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ class Verifier : KMockContract.Verifier, KMockContract.Collector {
}

override fun clear() {
TODO("Not yet implemented")
_references.access { it.clear() }
}
}
Loading