Added ParameterMatcher for (optional) throwing closures and optionals, e.g. anyThrowingClosure(), anyOptionalThrowingClosure and null()
Added Acceptance test for inheritance stubbing and verifying
This commit is contained in:
parent
f0f31bd0e9
commit
eaab7db26d
|
@ -45,6 +45,10 @@ public func anyString() -> ParameterMatcher<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a matcher matching any closure.
|
/// Returns a matcher matching any closure.
|
||||||
|
public func anyThrowingClosure<IN, OUT>() -> ParameterMatcher<(IN) throws -> OUT> {
|
||||||
|
return ParameterMatcher()
|
||||||
|
}
|
||||||
|
|
||||||
public func anyClosure<IN, OUT>() -> ParameterMatcher<(IN) -> OUT> {
|
public func anyClosure<IN, OUT>() -> ParameterMatcher<(IN) -> OUT> {
|
||||||
return ParameterMatcher()
|
return ParameterMatcher()
|
||||||
}
|
}
|
||||||
|
@ -97,9 +101,28 @@ public func anyClosure<IN, OUT>() -> ParameterMatcher<(((IN)) -> OUT)?> {
|
||||||
return notNil()
|
return notNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func anyOptionalThrowingClosure<IN, OUT>() -> ParameterMatcher<(((IN)) throws -> OUT)?> {
|
||||||
|
return notNil()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a matcher matching any non nil value.
|
/// Returns a matcher matching any non nil value.
|
||||||
public func notNil<T>() -> ParameterMatcher<T?> {
|
public func notNil<T>() -> ParameterMatcher<T?> {
|
||||||
return ParameterMatcher {
|
return ParameterMatcher {
|
||||||
if case .none = $0 { return false } else { return true }
|
if case .none = $0 {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a matcher matching any nil value
|
||||||
|
public func isNil<T>() -> ParameterMatcher<T?> {
|
||||||
|
return ParameterMatcher {
|
||||||
|
if case .none = $0 {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,11 +54,28 @@ class ParameterMatcherFunctionsTest: XCTestCase {
|
||||||
func testAny() {
|
func testAny() {
|
||||||
XCTAssertTrue(any().matches(X()))
|
XCTAssertTrue(any().matches(X()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testIsNil() {
|
||||||
|
let optional: X? = nil
|
||||||
|
XCTAssertTrue(isNil().matches(nil as X?))
|
||||||
|
XCTAssertTrue(isNil().matches(optional))
|
||||||
|
XCTAssertFalse(isNil().matches(X()))
|
||||||
|
}
|
||||||
|
|
||||||
func testAnyClosure() {
|
func testAnyClosure() {
|
||||||
XCTAssertTrue(anyClosure().matches({ 0 }))
|
XCTAssertTrue(anyClosure().matches({ 0 }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAnyThrowingClosure() {
|
||||||
|
XCTAssertTrue(anyThrowingClosure().matches({ 0 }))
|
||||||
|
XCTAssertTrue(anyThrowingClosure().matches({ (p: Int) throws in 1 }))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAnyOptionalThrowingClosure() {
|
||||||
|
XCTAssertTrue(anyOptionalThrowingClosure().matches({ (i: String) in 0 }))
|
||||||
|
XCTAssertTrue(anyOptionalThrowingClosure().matches({ (p: Int) throws in 1 }))
|
||||||
|
}
|
||||||
|
|
||||||
func testOptionalEqualToEquatable() {
|
func testOptionalEqualToEquatable() {
|
||||||
XCTAssertTrue(equal(to: nil as Int?).matches(nil))
|
XCTAssertTrue(equal(to: nil as Int?).matches(nil))
|
||||||
XCTAssertTrue(equal(to: 1 as Int?).matches(1))
|
XCTAssertTrue(equal(to: 1 as Int?).matches(1))
|
||||||
|
|
|
@ -56,19 +56,19 @@ class TestedSubclass: TestedClass, TestedProtocol {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func withThrowingClosure(closure: ((String)) throws -> String?) -> String? {
|
func withThrowingClosure(closure: (String) throws -> String?) -> String? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func withThrowingClosureThrows(closure: ((String)) throws -> String?) throws -> String? {
|
func withThrowingClosureThrows(closure: (String) throws -> String?) throws -> String? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func withThrowingEscapingClosure(closure: @escaping ((String)) throws -> String?) -> String? {
|
func withThrowingEscapingClosure(closure: @escaping (String) throws -> String?) -> String? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func withThrowingOptionalClosureThrows(closure: (((String)) throws -> String?)?) throws -> String? {
|
func withThrowingOptionalClosureThrows(closure: ((String) throws -> String?)?) throws -> String? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,3 +80,11 @@ class TestedSubclass: TestedClass, TestedProtocol {
|
||||||
return "c"
|
return "c"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TestedSubSubClass: TestedSubclass {
|
||||||
|
|
||||||
|
func subSubMethod() -> String? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -10,65 +10,65 @@ import XCTest
|
||||||
@testable import Cuckoo
|
@testable import Cuckoo
|
||||||
|
|
||||||
class StubbingTest: XCTestCase {
|
class StubbingTest: XCTestCase {
|
||||||
|
|
||||||
func testMultipleReturns() {
|
func testMultipleReturns() {
|
||||||
let mock = MockTestedClass()
|
let mock = MockTestedClass()
|
||||||
stub(mock) { mock in
|
stub(mock) { mock in
|
||||||
when(mock.readOnlyProperty.get).thenReturn("a").thenReturn("b", "c")
|
when(mock.readOnlyProperty.get).thenReturn("a").thenReturn("b", "c")
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTAssertEqual(mock.readOnlyProperty, "a")
|
XCTAssertEqual(mock.readOnlyProperty, "a")
|
||||||
XCTAssertEqual(mock.readOnlyProperty, "b")
|
XCTAssertEqual(mock.readOnlyProperty, "b")
|
||||||
XCTAssertEqual(mock.readOnlyProperty, "c")
|
XCTAssertEqual(mock.readOnlyProperty, "c")
|
||||||
XCTAssertEqual(mock.readOnlyProperty, "c")
|
XCTAssertEqual(mock.readOnlyProperty, "c")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testOverrideStubWithMoreGeneralParameterMatcher() {
|
func testOverrideStubWithMoreGeneralParameterMatcher() {
|
||||||
let mock = MockTestedClass()
|
let mock = MockTestedClass()
|
||||||
stub(mock) { mock in
|
stub(mock) { mock in
|
||||||
when(mock.count(characters: "a")).thenReturn(2)
|
when(mock.count(characters: "a")).thenReturn(2)
|
||||||
when(mock.count(characters: anyString())).thenReturn(1)
|
when(mock.count(characters: anyString())).thenReturn(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTAssertEqual(mock.count(characters: "a"), 1)
|
XCTAssertEqual(mock.count(characters: "a"), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testOverrideStubWithMoreSpecificParameterMatcher() {
|
func testOverrideStubWithMoreSpecificParameterMatcher() {
|
||||||
let mock = MockTestedClass()
|
let mock = MockTestedClass()
|
||||||
stub(mock) { mock in
|
stub(mock) { mock in
|
||||||
when(mock.count(characters: anyString())).thenReturn(1)
|
when(mock.count(characters: anyString())).thenReturn(1)
|
||||||
when(mock.count(characters: "a")).thenReturn(2)
|
when(mock.count(characters: "a")).thenReturn(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTAssertEqual(mock.count(characters: "a"), 2)
|
XCTAssertEqual(mock.count(characters: "a"), 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testUnstubbedSpy() {
|
func testUnstubbedSpy() {
|
||||||
let mock = MockTestedClass().withEnabledSuperclassSpy()
|
let mock = MockTestedClass().withEnabledSuperclassSpy()
|
||||||
|
|
||||||
XCTAssertEqual(mock.count(characters: "a"), 1)
|
XCTAssertEqual(mock.count(characters: "a"), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testStubOfMultipleDifferentCalls() {
|
func testStubOfMultipleDifferentCalls() {
|
||||||
let mock = MockTestedClass()
|
let mock = MockTestedClass()
|
||||||
stub(mock) { mock in
|
stub(mock) { mock in
|
||||||
when(mock.readOnlyProperty.get).thenReturn("a")
|
when(mock.readOnlyProperty.get).thenReturn("a")
|
||||||
when(mock.count(characters: "a")).thenReturn(1)
|
when(mock.count(characters: "a")).thenReturn(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTAssertEqual(mock.readOnlyProperty, "a")
|
XCTAssertEqual(mock.readOnlyProperty, "a")
|
||||||
XCTAssertEqual(mock.count(characters: "a"), 1)
|
XCTAssertEqual(mock.count(characters: "a"), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSubClass() {
|
func testSubClass() {
|
||||||
let mock = MockTestedSubclass()
|
let mock = MockTestedSubclass()
|
||||||
|
|
||||||
XCTAssertNotNil(mock)
|
XCTAssertNotNil(mock)
|
||||||
|
|
||||||
stub(mock) { mock in
|
stub(mock) { mock in
|
||||||
when(mock.readOnlyProperty.get).thenReturn("a").thenReturn("b", "c")
|
when(mock.readOnlyProperty.get).thenReturn("a").thenReturn("b", "c")
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTAssertEqual(mock.readOnlyProperty, "a")
|
XCTAssertEqual(mock.readOnlyProperty, "a")
|
||||||
XCTAssertEqual(mock.readOnlyProperty, "b")
|
XCTAssertEqual(mock.readOnlyProperty, "b")
|
||||||
XCTAssertEqual(mock.readOnlyProperty, "c")
|
XCTAssertEqual(mock.readOnlyProperty, "c")
|
||||||
|
@ -93,6 +93,7 @@ class StubbingTest: XCTestCase {
|
||||||
XCTAssertNotNil(mock)
|
XCTAssertNotNil(mock)
|
||||||
|
|
||||||
stub(mock) { mock in
|
stub(mock) { mock in
|
||||||
|
when(mock.methodWithParameter(anyInt())).thenReturn("t1")
|
||||||
when(mock.methodWithParameter(anyString())).thenReturn("t")
|
when(mock.methodWithParameter(anyString())).thenReturn("t")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +107,7 @@ class StubbingTest: XCTestCase {
|
||||||
|
|
||||||
stub(mock) { mock in
|
stub(mock) { mock in
|
||||||
when(mock.methodWithParameter(anyInt())).thenReturn("t1")
|
when(mock.methodWithParameter(anyInt())).thenReturn("t1")
|
||||||
|
when(mock.methodWithParameter(anyString())).thenReturn("s1")
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTAssertEqual(mock.methodWithParameter(1), "t1")
|
XCTAssertEqual(mock.methodWithParameter(1), "t1")
|
||||||
|
@ -123,4 +125,174 @@ class StubbingTest: XCTestCase {
|
||||||
|
|
||||||
XCTAssertEqual(mock.protocolMethod(), "a1")
|
XCTAssertEqual(mock.protocolMethod(), "a1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testSubclassWithGrandparentsInheritanceAcceptanceTest() {
|
||||||
|
let mock = MockTestedSubSubClass()
|
||||||
|
|
||||||
|
XCTAssertNotNil(mock)
|
||||||
|
|
||||||
|
var setReadWriteProperty: Int = -1
|
||||||
|
var setOptionalProperty = -1
|
||||||
|
var callNoReturn = false
|
||||||
|
var callNoReturnThrows = false
|
||||||
|
var callWithEscape = false
|
||||||
|
var callWithOptionalClosure = false
|
||||||
|
var callWithLabelAndUnderscore = false
|
||||||
|
|
||||||
|
// Mock all available functions
|
||||||
|
stub(mock) { stub in
|
||||||
|
// sub-sub class
|
||||||
|
when(stub.subSubMethod()).thenReturn("not nil")
|
||||||
|
|
||||||
|
// sub-class
|
||||||
|
when(stub.withImplicitlyUnwrappedOptional(i: anyInt())).thenReturn("implicit unwrapped return")
|
||||||
|
when(stub.withThrows()).thenReturn(10)
|
||||||
|
when(stub.withNamedTuple(tuple: any())).thenReturn(11)
|
||||||
|
when(stub.subclassMethod()).thenReturn(12)
|
||||||
|
when(stub.withOptionalClosureAndReturn(anyString(), closure: isNil())).thenReturn(2)
|
||||||
|
when(stub.withClosureAndParam(anyString(), closure: anyClosure())).thenReturn(3)
|
||||||
|
when(stub.withMultClosures(closure: anyClosure(), closureB: anyClosure(), closureC: anyClosure())).thenReturn(4)
|
||||||
|
when(stub.withThrowingClosure(closure: anyThrowingClosure())).thenReturn("throwing closure")
|
||||||
|
when(stub.withThrowingClosureThrows(closure: anyThrowingClosure())).thenReturn("closure throwing")
|
||||||
|
when(stub.withThrowingEscapingClosure(closure: anyThrowingClosure())).thenReturn("escaping closure")
|
||||||
|
when(stub.withThrowingOptionalClosureThrows(closure: anyOptionalThrowingClosure())).thenReturn("optional closure throwing")
|
||||||
|
when(stub.methodWithParameter(anyString())).thenReturn("parameter string")
|
||||||
|
when(stub.methodWithParameter(anyInt())).thenReturn("parameter int")
|
||||||
|
|
||||||
|
// class
|
||||||
|
when(stub.privateSetProperty.get).thenReturn(5)
|
||||||
|
when(stub.readOnlyProperty.get).thenReturn("b")
|
||||||
|
when(stub.readWriteProperty.set(anyInt())).then { i in
|
||||||
|
setReadWriteProperty = i
|
||||||
|
}
|
||||||
|
when(stub.readWriteProperty.get).thenReturn(7)
|
||||||
|
when(stub.optionalProperty.set(anyInt())).then { i in
|
||||||
|
setOptionalProperty = i!
|
||||||
|
}
|
||||||
|
when(stub.optionalProperty.get).thenReturn(8)
|
||||||
|
when(stub.noReturn()).then { _ in
|
||||||
|
callNoReturn = true
|
||||||
|
}
|
||||||
|
when(stub.count(characters: anyString())).thenReturn(9)
|
||||||
|
when(stub.withNoReturnThrows()).then { _ in
|
||||||
|
callNoReturnThrows = true
|
||||||
|
}
|
||||||
|
when(stub.withClosure(anyClosure())).thenReturn(14)
|
||||||
|
when(stub.withEscape(anyString(), action: anyClosure())).then { _ in
|
||||||
|
callWithEscape = true
|
||||||
|
}
|
||||||
|
when(stub.withOptionalClosure(anyString(), closure: anyClosure())).then { _ in
|
||||||
|
callWithOptionalClosure = true
|
||||||
|
}
|
||||||
|
when(stub.withLabelAndUnderscore(labelA: anyString(), anyString())).then { _ in
|
||||||
|
callWithLabelAndUnderscore = true
|
||||||
|
}
|
||||||
|
when(stub.callingCountCharactersMethodWithHello()).thenReturn(15)
|
||||||
|
|
||||||
|
// protocol
|
||||||
|
when(stub.protocolMethod()).thenReturn("protocol method")
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test stubbing
|
||||||
|
XCTAssertEqual(mock.subSubMethod(), "not nil")
|
||||||
|
|
||||||
|
// sub-class
|
||||||
|
XCTAssertEqual(mock.withImplicitlyUnwrappedOptional(i: 0), "implicit unwrapped return")
|
||||||
|
XCTAssertEqual(try! mock.withThrows(), 10)
|
||||||
|
XCTAssertEqual(mock.withNamedTuple(tuple: (a: "A", b: "B")), 11)
|
||||||
|
XCTAssertEqual(mock.subclassMethod(), 12)
|
||||||
|
XCTAssertEqual(mock.withOptionalClosureAndReturn("a", closure: nil), 2)
|
||||||
|
XCTAssertEqual(mock.withClosureAndParam("a", closure: { _ in 0 }), 3)
|
||||||
|
XCTAssertEqual(mock.withMultClosures(closure: { _ in 0 }, closureB: { _ in 1 }, closureC: { _ in 2 }), 4)
|
||||||
|
XCTAssertEqual(mock.withThrowingClosure { p throws in
|
||||||
|
return nil
|
||||||
|
}, "throwing closure")
|
||||||
|
XCTAssertEqual(try! mock.withThrowingClosureThrows(closure: { _ in nil }), "closure throwing")
|
||||||
|
XCTAssertEqual(mock.withThrowingEscapingClosure(closure: { _ in nil }), "escaping closure")
|
||||||
|
XCTAssertEqual(try! mock.withThrowingOptionalClosureThrows(closure: { _ in nil }), "optional closure throwing")
|
||||||
|
XCTAssertEqual(mock.methodWithParameter("a"), "parameter string")
|
||||||
|
XCTAssertEqual(mock.methodWithParameter(1), "parameter int")
|
||||||
|
|
||||||
|
// class
|
||||||
|
XCTAssertEqual(mock.privateSetProperty, 5)
|
||||||
|
XCTAssertEqual(mock.readOnlyProperty, "b")
|
||||||
|
XCTAssertEqual({
|
||||||
|
mock.readWriteProperty = 100;
|
||||||
|
return setReadWriteProperty
|
||||||
|
}(), 100)
|
||||||
|
XCTAssertEqual(mock.readWriteProperty, 7)
|
||||||
|
XCTAssertEqual({
|
||||||
|
mock.optionalProperty = 200
|
||||||
|
return setOptionalProperty
|
||||||
|
}(), 200)
|
||||||
|
XCTAssertEqual(mock.optionalProperty, 8)
|
||||||
|
XCTAssertTrue({
|
||||||
|
mock.noReturn()
|
||||||
|
return callNoReturn
|
||||||
|
}())
|
||||||
|
XCTAssertEqual(mock.count(characters: "a"), 9)
|
||||||
|
XCTAssertTrue({
|
||||||
|
try! mock.withNoReturnThrows()
|
||||||
|
return callNoReturnThrows
|
||||||
|
}())
|
||||||
|
XCTAssertEqual(mock.withClosure { _ in
|
||||||
|
41
|
||||||
|
}, 14)
|
||||||
|
XCTAssertTrue({
|
||||||
|
mock.withEscape("a") { _ in }
|
||||||
|
return callWithEscape
|
||||||
|
}())
|
||||||
|
XCTAssertTrue({
|
||||||
|
mock.withOptionalClosure("a") { _ in }
|
||||||
|
return callWithOptionalClosure
|
||||||
|
}())
|
||||||
|
XCTAssertTrue({
|
||||||
|
mock.withLabelAndUnderscore(labelA: "labelA", "any")
|
||||||
|
return callWithLabelAndUnderscore
|
||||||
|
}())
|
||||||
|
XCTAssertEqual(mock.callingCountCharactersMethodWithHello(), 15)
|
||||||
|
|
||||||
|
// Protocol
|
||||||
|
XCTAssertEqual(mock.protocolMethod(), "protocol method")
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test Verification
|
||||||
|
// sub-sub class
|
||||||
|
verify(mock, times(1)).subSubMethod()
|
||||||
|
|
||||||
|
// sub-class
|
||||||
|
verify(mock, times(1)).withImplicitlyUnwrappedOptional(i: anyInt())
|
||||||
|
verify(mock, times(1)).withThrows()
|
||||||
|
verify(mock, times(1)).withNamedTuple(tuple: any())
|
||||||
|
verify(mock, times(1)).subclassMethod()
|
||||||
|
verify(mock, times(1)).withOptionalClosureAndReturn(anyString(), closure: isNil())
|
||||||
|
verify(mock, times(1)).withClosureAndParam(anyString(), closure: anyClosure())
|
||||||
|
verify(mock, times(1)).withMultClosures(closure: anyClosure(), closureB: anyClosure(), closureC: anyClosure())
|
||||||
|
verify(mock, times(1)).withThrowingClosure(closure: anyThrowingClosure())
|
||||||
|
verify(mock, times(1)).withThrowingClosureThrows(closure: anyThrowingClosure())
|
||||||
|
verify(mock, times(1)).withThrowingEscapingClosure(closure: anyThrowingClosure())
|
||||||
|
verify(mock, times(1)).withThrowingOptionalClosureThrows(closure: anyOptionalThrowingClosure())
|
||||||
|
verify(mock, times(1)).methodWithParameter(anyString())
|
||||||
|
verify(mock, times(1)).methodWithParameter(anyInt())
|
||||||
|
|
||||||
|
// class
|
||||||
|
verify(mock, times(1)).privateSetProperty.get
|
||||||
|
verify(mock, times(1)).readOnlyProperty.get
|
||||||
|
verify(mock, times(1)).readWriteProperty.set(anyInt())
|
||||||
|
verify(mock, times(1)).readWriteProperty.get
|
||||||
|
verify(mock, times(1)).optionalProperty.set(anyInt())
|
||||||
|
verify(mock, times(1)).optionalProperty.get
|
||||||
|
verify(mock, times(1)).noReturn()
|
||||||
|
verify(mock, times(1)).count(characters: anyString())
|
||||||
|
verify(mock, times(1)).withNoReturnThrows()
|
||||||
|
verify(mock, times(1)).withClosure(anyClosure())
|
||||||
|
verify(mock, times(1)).withEscape(anyString(), action: anyClosure())
|
||||||
|
verify(mock, times(1)).withOptionalClosure(anyString(), closure: anyClosure())
|
||||||
|
verify(mock, times(1)).withLabelAndUnderscore(labelA: anyString(), anyString())
|
||||||
|
verify(mock, times(1)).callingCountCharactersMethodWithHello()
|
||||||
|
|
||||||
|
// protocol
|
||||||
|
verify(mock, times(1)).protocolMethod()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue