Refactor fail description.
This commit is contained in:
parent
97d56e0b6a
commit
39b233b766
|
@ -10,6 +10,7 @@
|
|||
183D03FF1C4691C600EBAEF3 /* Cuckoo.h in Headers */ = {isa = PBXBuildFile; fileRef = 183D03FE1C4691C600EBAEF3 /* Cuckoo.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
183D04061C4691C600EBAEF3 /* Cuckoo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 183D03FB1C4691C600EBAEF3 /* Cuckoo.framework */; };
|
||||
183D04161C46926A00EBAEF3 /* CuckooFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183D04151C46926A00EBAEF3 /* CuckooFunctions.swift */; };
|
||||
DC1A82B51D2D6BD500A217F0 /* FailTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC1A82B31D2D6A2100A217F0 /* FailTest.swift */; };
|
||||
DC4094EE1D211563006FB137 /* StubFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4094EC1D211563006FB137 /* StubFunction.swift */; };
|
||||
DC4094EF1D211563006FB137 /* StubThrowingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4094ED1D211563006FB137 /* StubThrowingFunction.swift */; };
|
||||
DC4094F31D211598006FB137 /* ToBeStubbedProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4094F11D211598006FB137 /* ToBeStubbedProperty.swift */; };
|
||||
|
@ -82,6 +83,7 @@
|
|||
183D04051C4691C600EBAEF3 /* CuckooTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CuckooTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
183D040C1C4691C600EBAEF3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
183D04151C46926A00EBAEF3 /* CuckooFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuckooFunctions.swift; sourceTree = "<group>"; };
|
||||
DC1A82B31D2D6A2100A217F0 /* FailTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FailTest.swift; path = ../../../Brightify/Cuckoo/Tests/FailTest.swift; sourceTree = "<group>"; };
|
||||
DC4094EC1D211563006FB137 /* StubFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StubFunction.swift; path = Stubbing/StubFunction/StubFunction.swift; sourceTree = "<group>"; };
|
||||
DC4094ED1D211563006FB137 /* StubThrowingFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StubThrowingFunction.swift; path = Stubbing/StubFunction/StubThrowingFunction.swift; sourceTree = "<group>"; };
|
||||
DC4094F11D211598006FB137 /* ToBeStubbedProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ToBeStubbedProperty.swift; path = Stubbing/ToBeStubbedProperty/ToBeStubbedProperty.swift; sourceTree = "<group>"; };
|
||||
|
@ -198,6 +200,7 @@
|
|||
183D040C1C4691C600EBAEF3 /* Info.plist */,
|
||||
DC70D0A91D2AB5F500014C5F /* ClassTest.swift */,
|
||||
DC70D0A61D2AB5B300014C5F /* CuckooFunctionsTest.swift */,
|
||||
DC1A82B31D2D6A2100A217F0 /* FailTest.swift */,
|
||||
DC70D0AC1D2AB62100014C5F /* ProtocolTest.swift */,
|
||||
DC70D0B71D2ABA3100014C5F /* TestUtils.swift */,
|
||||
DC70D0A01D2AB53D00014C5F /* Describing */,
|
||||
|
@ -547,6 +550,7 @@
|
|||
DC70D0B31D2AB6B900014C5F /* MatchableTest.swift in Sources */,
|
||||
DC70D0BE1D2AF40800014C5F /* TestedClass.swift in Sources */,
|
||||
DC70D0D21D2B007300014C5F /* GeneratedMocks.swift in Sources */,
|
||||
DC1A82B51D2D6BD500A217F0 /* FailTest.swift in Sources */,
|
||||
DC70D0D41D2B026200014C5F /* VerificationTest.swift in Sources */,
|
||||
DC70D0AD1D2AB62100014C5F /* ProtocolTest.swift in Sources */,
|
||||
DC70D0C51D2AFA8E00014C5F /* StubFunctionTest.swift in Sources */,
|
||||
|
|
|
@ -19,8 +19,8 @@ public func when<F>(function: F) -> F {
|
|||
|
||||
/// Creates object used for verification of calls.
|
||||
@warn_unused_result
|
||||
public func verify<M: Mock>(mock: M, _ callMatcher: CallMatcher = times(1), sourceLocation: SourceLocation = (#file, #line)) -> M.Verification {
|
||||
return mock.getVerificationProxy(callMatcher, sourceLocation: sourceLocation)
|
||||
public func verify<M: Mock>(mock: M, _ callMatcher: CallMatcher = times(1), file: StaticString = #file, line: UInt = #line) -> M.Verification {
|
||||
return mock.getVerificationProxy(callMatcher, sourceLocation: (file, line))
|
||||
}
|
||||
|
||||
/// Clears all invocations and stubs of mocks.
|
||||
|
@ -45,8 +45,8 @@ public func clearInvocations<M: Mock>(mocks: M...) {
|
|||
}
|
||||
|
||||
/// Checks if there are no more uverified calls.
|
||||
public func verifyNoMoreInteractions<M: Mock>(mocks: M..., sourceLocation: SourceLocation = (#file, #line)) {
|
||||
public func verifyNoMoreInteractions<M: Mock>(mocks: M..., file: StaticString = #file, line: UInt = #line) {
|
||||
mocks.forEach { mock in
|
||||
mock.manager.verifyNoMoreInteractions(sourceLocation)
|
||||
mock.manager.verifyNoMoreInteractions((file, line))
|
||||
}
|
||||
}
|
|
@ -8,34 +8,35 @@
|
|||
|
||||
/// CallMatcher is used in verification to assert how many times was the call made. It can also be used to do different asserts on stub calls matched with parameter matchers.
|
||||
public struct CallMatcher {
|
||||
private let matchesFunction: [StubCall] throws -> Bool
|
||||
public let name: String
|
||||
|
||||
public init(matchesFunction: [StubCall] -> Bool = { _ in true }) {
|
||||
private let matchesFunction: [StubCall] -> Bool
|
||||
|
||||
public init(name: String, matchesFunction: [StubCall] -> Bool) {
|
||||
self.name = name
|
||||
self.matchesFunction = matchesFunction
|
||||
}
|
||||
|
||||
public init(numberOfExpectedCalls: Int, compareCallsFunction: (expected: Int, actual: Int) -> Bool) {
|
||||
self.matchesFunction = {
|
||||
public init(name: String, numberOfExpectedCalls: Int, compareCallsFunction: (expected: Int, actual: Int) -> Bool) {
|
||||
self.init(name: name) {
|
||||
return compareCallsFunction(expected: numberOfExpectedCalls, actual: $0.count)
|
||||
}
|
||||
}
|
||||
|
||||
public func matches(calls: [StubCall]) -> Bool {
|
||||
do {
|
||||
return try matchesFunction(calls)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
return matchesFunction(calls)
|
||||
}
|
||||
|
||||
public func or(otherMatcher: CallMatcher) -> CallMatcher {
|
||||
return CallMatcher {
|
||||
let name = "either \(self.name) or \(otherMatcher.name)"
|
||||
return CallMatcher(name: name) {
|
||||
return self.matches($0) || otherMatcher.matches($0)
|
||||
}
|
||||
}
|
||||
|
||||
public func and(otherMatcher: CallMatcher) -> CallMatcher {
|
||||
return CallMatcher {
|
||||
let name = "both \(self.name) and \(otherMatcher.name)"
|
||||
return CallMatcher(name: name) {
|
||||
return self.matches($0) && otherMatcher.matches($0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
/// Returns a matcher ensuring a call was made **`count`** times.
|
||||
@warn_unused_result
|
||||
public func times(count: Int) -> CallMatcher {
|
||||
return CallMatcher(numberOfExpectedCalls: count, compareCallsFunction: ==)
|
||||
let name = count == 0 ? "never" : "\(count) times"
|
||||
return CallMatcher(name: name, numberOfExpectedCalls: count, compareCallsFunction: ==)
|
||||
}
|
||||
|
||||
/// Returns a matcher ensuring no call was made.
|
||||
|
@ -27,11 +28,11 @@ public func atLeastOnce() -> CallMatcher {
|
|||
/// Returns a matcher ensuring call was made at least `count` times.
|
||||
@warn_unused_result
|
||||
public func atLeast(count: Int) -> CallMatcher {
|
||||
return CallMatcher(numberOfExpectedCalls: count, compareCallsFunction: <=)
|
||||
return CallMatcher(name: "at least \(count) times", numberOfExpectedCalls: count, compareCallsFunction: <=)
|
||||
}
|
||||
|
||||
/// Returns a matcher ensuring call was made at most `count` times.
|
||||
@warn_unused_result
|
||||
public func atMost(count: Int) -> CallMatcher {
|
||||
return CallMatcher(numberOfExpectedCalls: count, compareCallsFunction: >=)
|
||||
return CallMatcher(name: "at most \(count) times",numberOfExpectedCalls: count, compareCallsFunction: >=)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
/// ParameterMatcher matches parameters of methods in stubbing and verification.
|
||||
public struct ParameterMatcher<T>: Matchable {
|
||||
private let matchesFunction: T throws -> Bool
|
||||
private let matchesFunction: T -> Bool
|
||||
|
||||
public init(matchesFunction: T -> Bool = { _ in true }) {
|
||||
self.matchesFunction = matchesFunction
|
||||
|
@ -19,10 +19,6 @@ public struct ParameterMatcher<T>: Matchable {
|
|||
}
|
||||
|
||||
public func matches(input: T) -> Bool {
|
||||
do {
|
||||
return try matchesFunction(input)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
return matchesFunction(input)
|
||||
}
|
||||
}
|
|
@ -84,8 +84,8 @@ public class MockManager {
|
|||
unverifiedStubCallsIndexes = unverifiedStubCallsIndexes.filter { !indexesToRemove.contains($0) }
|
||||
|
||||
if callMatcher.matches(calls) == false {
|
||||
let description = Description()
|
||||
MockManager.fail(message: description.description, sourceLocation: sourceLocation)
|
||||
let message = "Wanted \(callMatcher.name) but \(calls.count == 0 ? "not invoked" : "invoked \(calls.count) times")."
|
||||
MockManager.fail(message: message, sourceLocation: sourceLocation)
|
||||
}
|
||||
return __DoNotUse()
|
||||
}
|
||||
|
@ -106,14 +106,26 @@ public class MockManager {
|
|||
|
||||
func verifyNoMoreInteractions(sourceLocation: SourceLocation) {
|
||||
if unverifiedStubCallsIndexes.isEmpty == false {
|
||||
let unverifiedCalls = unverifiedStubCallsIndexes.map { stubCalls[$0] }.map { String($0) }.joinWithSeparator(", ")
|
||||
MockManager.fail(message: "Found unverified call(s): " + unverifiedCalls, sourceLocation: sourceLocation)
|
||||
let unverifiedCalls = unverifiedStubCallsIndexes.map { stubCalls[$0] }.map { call in
|
||||
if let bracketIndex = call.method.rangeOfString("(")?.startIndex {
|
||||
let name = call.method.substringToIndex(bracketIndex)
|
||||
return name + call.parametersAsString
|
||||
} else {
|
||||
if call.method.hasSuffix("#set") {
|
||||
return call.method + call.parametersAsString
|
||||
} else {
|
||||
return call.method
|
||||
}
|
||||
}
|
||||
}.enumerate().map { "\($0 + 1). " + $1 }.joinWithSeparator("\n")
|
||||
let message = "No more interactions wanted but some found:\n"
|
||||
MockManager.fail(message: message + unverifiedCalls, sourceLocation: sourceLocation)
|
||||
}
|
||||
}
|
||||
|
||||
@noreturn
|
||||
private func failAndCrash(message: String, sourceLocation: SourceLocation = (#file, #line)) {
|
||||
MockManager.fail(message: message, sourceLocation: sourceLocation)
|
||||
private func failAndCrash(message: String, file: StaticString = #file, line: UInt = #line) {
|
||||
MockManager.fail(message: message, sourceLocation: (file, line))
|
||||
fatalError(message)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,23 @@
|
|||
|
||||
public protocol StubCall {
|
||||
var method: String { get }
|
||||
var parametersAsString: String { get }
|
||||
}
|
||||
|
||||
public struct ConcreteStubCall<IN>: StubCall {
|
||||
public let method: String
|
||||
let parameters: IN
|
||||
public let parameters: IN
|
||||
|
||||
public var parametersAsString: String {
|
||||
let string = String(parameters)
|
||||
if (string.rangeOfString(",") != nil && string.hasPrefix("(")) || string == "()" {
|
||||
return string
|
||||
} else {
|
||||
// If only one parameter add brackets and quotes
|
||||
let wrappedParameter = String((parameters, 0))
|
||||
return wrappedParameter.substringToIndex(wrappedParameter.endIndex.advancedBy(-4)) + ")"
|
||||
}
|
||||
}
|
||||
|
||||
public init(method: String, parameters: IN) {
|
||||
self.method = method
|
||||
|
|
|
@ -64,19 +64,4 @@ class CuckooFunctionsTest: XCTestCase {
|
|||
|
||||
verifyNoMoreInteractions(mock)
|
||||
}
|
||||
|
||||
func testVerifyNoMoreInteractionsFail() {
|
||||
let error = TestUtils.catchCuckooFail {
|
||||
let mock = MockTestedClass()
|
||||
stub(mock) { mock in
|
||||
when(mock.noReturn()).thenDoNothing()
|
||||
}
|
||||
|
||||
mock.noReturn()
|
||||
|
||||
verifyNoMoreInteractions(mock)
|
||||
}
|
||||
|
||||
XCTAssertNotNil(error)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
//
|
||||
// FailTest.swift
|
||||
// Cuckoo
|
||||
//
|
||||
// Created by Filip Dolnik on 06.07.16.
|
||||
// Copyright © 2016 Brightify. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Cuckoo
|
||||
|
||||
class FailTest: XCTestCase {
|
||||
|
||||
func testMissingInvocation() {
|
||||
let error = TestUtils.catchCuckooFail {
|
||||
let mock = MockTestedClass()
|
||||
|
||||
verify(mock).noReturn()
|
||||
}
|
||||
|
||||
XCTAssertEqual(error, "Wanted 1 times but not invoked.")
|
||||
}
|
||||
|
||||
func testNoInvocation2Wanted() {
|
||||
let error = TestUtils.catchCuckooFail {
|
||||
let mock = MockTestedClass()
|
||||
stub(mock) { mock in
|
||||
when(mock.noReturn()).thenDoNothing()
|
||||
}
|
||||
|
||||
mock.noReturn()
|
||||
|
||||
verify(mock, times(2)).noReturn()
|
||||
}
|
||||
|
||||
XCTAssertEqual(error, "Wanted 2 times but invoked 1 times.")
|
||||
}
|
||||
|
||||
func testInvocationNeverWanted() {
|
||||
let error = TestUtils.catchCuckooFail {
|
||||
let mock = MockTestedClass()
|
||||
stub(mock) { mock in
|
||||
when(mock.noReturn()).thenDoNothing()
|
||||
}
|
||||
|
||||
mock.noReturn()
|
||||
|
||||
verify(mock, never()).noReturn()
|
||||
}
|
||||
|
||||
XCTAssertEqual(error, "Wanted never but invoked 1 times.")
|
||||
}
|
||||
|
||||
func testInvocationAtLeast2Wanted() {
|
||||
let error = TestUtils.catchCuckooFail {
|
||||
let mock = MockTestedClass()
|
||||
stub(mock) { mock in
|
||||
when(mock.noReturn()).thenDoNothing()
|
||||
}
|
||||
|
||||
mock.noReturn()
|
||||
|
||||
verify(mock, atLeast(2)).noReturn()
|
||||
}
|
||||
|
||||
XCTAssertEqual(error, "Wanted at least 2 times but invoked 1 times.")
|
||||
}
|
||||
|
||||
func test2InvocationAtMost1Wanted() {
|
||||
let error = TestUtils.catchCuckooFail {
|
||||
let mock = MockTestedClass()
|
||||
stub(mock) { mock in
|
||||
when(mock.noReturn()).thenDoNothing()
|
||||
}
|
||||
|
||||
mock.noReturn()
|
||||
mock.noReturn()
|
||||
|
||||
verify(mock, atMost(1)).noReturn()
|
||||
}
|
||||
|
||||
XCTAssertEqual(error, "Wanted at most 1 times but invoked 2 times.")
|
||||
}
|
||||
|
||||
func testCallMatcherOr() {
|
||||
let error = TestUtils.catchCuckooFail {
|
||||
let mock = MockTestedClass()
|
||||
stub(mock) { mock in
|
||||
when(mock.noReturn()).thenDoNothing()
|
||||
}
|
||||
|
||||
verify(mock, times(1).or(times(2))).noReturn()
|
||||
}
|
||||
|
||||
XCTAssertEqual(error, "Wanted either 1 times or 2 times but not invoked.")
|
||||
}
|
||||
|
||||
func testCallMatcherAnd() {
|
||||
let error = TestUtils.catchCuckooFail {
|
||||
let mock = MockTestedClass()
|
||||
stub(mock) { mock in
|
||||
when(mock.noReturn()).thenDoNothing()
|
||||
}
|
||||
|
||||
verify(mock, atLeast(1).and(atMost(2))).noReturn()
|
||||
}
|
||||
|
||||
XCTAssertEqual(error, "Wanted both at least 1 times and at most 2 times but not invoked.")
|
||||
}
|
||||
|
||||
func testVerifyNoMoreInteractionsFail() {
|
||||
let error = TestUtils.catchCuckooFail {
|
||||
let mock = MockTestedClass(spyOn: TestedClass())
|
||||
|
||||
mock.withOptionalClosure("a", closure: nil)
|
||||
mock.noReturn()
|
||||
mock.countCharacters("b")
|
||||
let _ = mock.readWriteProperty
|
||||
mock.readWriteProperty = 1
|
||||
mock.withOptionalClosure("c", closure: { _ in })
|
||||
|
||||
verifyNoMoreInteractions(mock)
|
||||
}
|
||||
|
||||
XCTAssertEqual(error, "No more interactions wanted but some found:\n" +
|
||||
"1. withOptionalClosure(\"a\", nil)\n" +
|
||||
"2. noReturn()\n" +
|
||||
"3. countCharacters(\"b\")\n" +
|
||||
"4. readWriteProperty#get\n" +
|
||||
"5. readWriteProperty#set(1)\n" +
|
||||
"6. withOptionalClosure(\"c\", Optional((Function)))")
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ class CallMatcherTest: XCTestCase {
|
|||
}
|
||||
|
||||
func testMatches() {
|
||||
let matcher = CallMatcher { ($0.first?.method ?? "") == "A"}
|
||||
let matcher = CallMatcher(name: "") { ($0.first?.method ?? "") == "A"}
|
||||
let nonMatchingCalls = [ConcreteStubCall(method: "B", parameters: Void()) as StubCall]
|
||||
|
||||
XCTAssertTrue(matcher.matches(call))
|
||||
|
|
|
@ -47,14 +47,4 @@ class VerificationTest: XCTestCase {
|
|||
verify(mock).noReturn()
|
||||
verify(mock).countCharacters(anyString())
|
||||
}
|
||||
|
||||
func testVerifyFail() {
|
||||
let error = TestUtils.catchCuckooFail {
|
||||
let mock = MockTestedClass()
|
||||
|
||||
verify(mock).noReturn()
|
||||
}
|
||||
|
||||
XCTAssertNotNil(error)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue