Skip to content

Commit

Permalink
Better mismatched argument messages for built-in matchers
Browse files Browse the repository at this point in the history
  • Loading branch information
paweldudek committed Aug 6, 2019
1 parent 778eac3 commit d77ab8e
Show file tree
Hide file tree
Showing 15 changed files with 126 additions and 41 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
@@ -1,5 +1,6 @@
# Next (1.1.4)
- Nothing yet
- Mismatched arguments are now included in failure message (thanks @Eluss)
- Added shorthand for `AnyMatcher()`

# 1.1.3
- Add '.atMost' VerificationMode (thanks @kamwysoc)
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
@@ -1,4 +1,4 @@
source "https://rubygems.org"

gem 'xcpretty'
gem 'cocoapods', '1.7.0.rc.1'
gem 'cocoapods'
18 changes: 9 additions & 9 deletions Gemfile.lock
Expand Up @@ -8,11 +8,11 @@ GEM
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
atomos (0.1.3)
claide (1.0.2)
cocoapods (1.7.0.rc.1)
claide (1.0.3)
cocoapods (1.7.5)
activesupport (>= 4.0.2, < 5)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.7.0.rc.1)
cocoapods-core (= 1.7.5)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.2.2, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
Expand All @@ -22,13 +22,13 @@ GEM
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (>= 2.2.0, < 3.0)
fourflusher (>= 2.3.0, < 3.0)
gh_inspector (~> 1.0)
molinillo (~> 0.6.6)
nap (~> 1.0)
ruby-macho (~> 1.4)
xcodeproj (>= 1.8.2, < 2.0)
cocoapods-core (1.7.0.rc.1)
xcodeproj (>= 1.10.0, < 2.0)
cocoapods-core (1.7.5)
activesupport (>= 4.0.2, < 6)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
Expand All @@ -45,7 +45,7 @@ GEM
colored2 (3.1.2)
concurrent-ruby (1.1.5)
escape (0.0.4)
fourflusher (2.2.0)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
i18n (0.9.5)
Expand All @@ -60,7 +60,7 @@ GEM
thread_safe (0.3.6)
tzinfo (1.2.5)
thread_safe (~> 0.1)
xcodeproj (1.9.0)
xcodeproj (1.12.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
Expand All @@ -73,7 +73,7 @@ PLATFORMS
ruby

DEPENDENCIES
cocoapods (= 1.7.0.rc.1)
cocoapods
xcpretty

BUNDLED WITH
Expand Down
4 changes: 4 additions & 0 deletions Mimus.xcodeproj/project.pbxproj
Expand Up @@ -32,6 +32,7 @@
B27A920B9C2CA66BCBADDB19 /* EqualMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B27A9B7F44828628232184BF /* EqualMatcher.swift */; };
B27A93145E8B802D1E9FD894 /* MatcherShorthands.swift in Sources */ = {isa = PBXBuildFile; fileRef = B27A994E11A3C51C19C9E3E5 /* MatcherShorthands.swift */; };
B27A9968CAC21E5D7E37A8C5 /* InstanceOfMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B27A9F47A538073105189D9B /* InstanceOfMatcher.swift */; };
B27A9A77BB135DC528685B5C /* OptionalExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B27A964B76F3B531642899FD /* OptionalExtension.swift */; };
B27A9AC9D7C76B52740F86F6 /* NotMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B27A9A21A02DFFE58DF8F120 /* NotMatcher.swift */; };
B27A9BACD1A73B2F79D6FD2B /* MatcherHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B27A915D909A304A0CB50B1C /* MatcherHelpers.swift */; };
B27A9FD07F0DBB1F59345E79 /* ClosureMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B27A9C05F0C24BC211A5ADA7 /* ClosureMatcher.swift */; };
Expand Down Expand Up @@ -85,6 +86,7 @@
43190C9A1F41A1F2000F6813 /* MatcherExtensionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatcherExtensionsTests.swift; sourceTree = "<group>"; };
437E55B81FCC7DC9007D5A3C /* ApplicationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationService.swift; sourceTree = "<group>"; };
B27A915D909A304A0CB50B1C /* MatcherHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatcherHelpers.swift; sourceTree = "<group>"; };
B27A964B76F3B531642899FD /* OptionalExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptionalExtension.swift; sourceTree = "<group>"; };
B27A994E11A3C51C19C9E3E5 /* MatcherShorthands.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatcherShorthands.swift; sourceTree = "<group>"; };
B27A9A21A02DFFE58DF8F120 /* NotMatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotMatcher.swift; sourceTree = "<group>"; };
B27A9B7F44828628232184BF /* EqualMatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EqualMatcher.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -238,6 +240,7 @@
isa = PBXGroup;
children = (
40E02F215A9DF8B857C63421 /* TestLocation.swift */,
B27A964B76F3B531642899FD /* OptionalExtension.swift */,
);
path = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -412,6 +415,7 @@
B27A9FD07F0DBB1F59345E79 /* ClosureMatcher.swift in Sources */,
B27A9AC9D7C76B52740F86F6 /* NotMatcher.swift in Sources */,
B27A93145E8B802D1E9FD894 /* MatcherShorthands.swift in Sources */,
B27A9A77BB135DC528685B5C /* OptionalExtension.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
56 changes: 56 additions & 0 deletions Mimus.xcodeproj/xcshareddata/xcschemes/MimusExamples.xcscheme
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "10539B571E7BF218006EA51F"
BuildableName = "MimusExamples.xctest"
BlueprintName = "MimusExamples"
ReferencedContainer = "container:Mimus.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
16 changes: 16 additions & 0 deletions Mimus/Source/Helpers/OptionalExtension.swift
@@ -0,0 +1,16 @@
//
// Copyright (©) 2019 AirHelp. All rights reserved.
//

import Foundation

extension Optional {
func mimusDescription() -> String {
switch self {
case .none:
return "<(nil)>"
case .some(let value):
return "<(\(String(describing: value)))>"
}
}
}
6 changes: 3 additions & 3 deletions Mimus/Source/Matchers/ClosureMatcher.swift
Expand Up @@ -4,14 +4,14 @@

import Foundation

class ClosureMatcher<T>: MockEquatable {
public class ClosureMatcher<T>: MockEquatable {
private let closure: (T?) -> Bool

init(_ closure: @escaping (T?) -> Bool) {
public init(_ closure: @escaping (T?) -> Bool) {
self.closure = closure
}

func equalTo(other: Any?) -> Bool {
public func equalTo(other: Any?) -> Bool {
guard let otherAsType = other as? T? else {
return false
}
Expand Down
6 changes: 5 additions & 1 deletion Mimus/Source/Matchers/EqualMatcher.swift
@@ -1,6 +1,6 @@
import Foundation

public final class EqualTo<T: Equatable>: MockEquatable {
public final class EqualTo<T: Equatable>: MockEquatable, CustomStringConvertible {

private let object: T?

Expand All @@ -14,4 +14,8 @@ public final class EqualTo<T: Equatable>: MockEquatable {
}
return other == nil && object == nil
}

public var description: String {
return "\(type(of: self)) - \(object.mimusDescription())"
}
}
10 changes: 7 additions & 3 deletions Mimus/Source/Matchers/IdenticalMatcher.swift
@@ -1,17 +1,21 @@
import Foundation

public final class IdenticalTo<T: AnyObject>: MockEquatable {

private let object: T?

public init(_ object: T?) {
self.object = object
}

public func equalTo(other: Any?) -> Bool {
if let otherObject = other as? T {
return object === otherObject
}
return other == nil && object == nil
}

public var description: String {
return "\(type(of: self)) - \(object.mimusDescription())"
}
}
4 changes: 2 additions & 2 deletions Mimus/Source/Matchers/InstanceOfMatcher.swift
Expand Up @@ -5,9 +5,9 @@
import Foundation

public final class InstanceOf<T>: MockEquatable {

public init() { }

public func equalTo(other: Any?) -> Bool {
return other is T
}
Expand Down
3 changes: 3 additions & 0 deletions Mimus/Source/Matchers/MatcherShorthands.swift
Expand Up @@ -25,3 +25,6 @@ public func mClosure<T>(_ closure: @escaping (T?) -> Bool) -> MockEquatable {
return ClosureMatcher(closure)
}

public func mAny() -> MockEquatable {
return AnyMatcher()
}
5 changes: 4 additions & 1 deletion Mimus/Source/Matchers/NotMatcher.swift
Expand Up @@ -14,5 +14,8 @@ public class NotMatcher: MockEquatable {
public func equalTo(other: Any?) -> Bool {
return !containedMatcher.equalTo(other: other)
}
}

public var description: String {
return "\(type(of: self)) - \(containedMatcher)"
}
}
28 changes: 10 additions & 18 deletions Mimus/Source/Verification/MismatchMessageBuilder.swift
Expand Up @@ -5,13 +5,13 @@
import Foundation

class MismatchMessageBuilder {

func message(for mismatchedResults: [MatchResult]) -> String {
guard !mismatchedResults.isEmpty else {
return ""
}
let messages = buildRecordedCallsMessages(for: mismatchedResults)
return joined(messages)
return joined(messages, newlineSeparator: "\n\n")
}

private func buildRecordedCallsMessages(for matchResults: [MatchResult]) -> [String] {
Expand All @@ -34,28 +34,20 @@ class MismatchMessageBuilder {
return "Unexpected behavior, no arguments comparison to present"
}
let messages = comparisons.map { comparison -> String in
let details = "expected \(displayText(for: comparison.expected)), but was \(displayText(for: comparison.actual))."
let details = "expected \(comparison.expected.mimusDescription()), but was \(comparison.actual.mimusDescription())."
return "Mismatch in argument #\(comparison.argumentIndex) - \(details)"
}
return joined(messages)
}

private func displayText(for value: Any?) -> String {
if let value = value {
return "<(\(String(describing: value)))>"
} else {
return "<(nil)>"
}
}

private func joined(_ messages: [String]) -> String {
private func joined(_ messages: [String], newlineSeparator: String = "\n") -> String {
return messages
.enumerated()
.reduce("", { (result, element) in
let (index, message) = element
let isLastMessage = messages.count == index + 1
return isLastMessage ? "\(result)\(message)" : "\(result)\(message)\n"
})
.enumerated()
.reduce("", { (result, element) in
let (index, message) = element
let isLastMessage = messages.count == index + 1
return isLastMessage ? "\(result)\(message)" : "\(result)\(message)\(newlineSeparator)"
})

}
}
4 changes: 2 additions & 2 deletions Mimus/Source/Verification/VerificationHandler.swift
Expand Up @@ -74,7 +74,7 @@ internal class VerificationHandler {
let mismatchedResultsMessage = mismatchMessageBuilder.message(for: differentArgumentsMatch)
message = """
Call with identifier \(callIdentifier) was recorded \(matchCount) times, but expected at least \(times).
\(differentArgumentsMatchCount) additional call(s) matched identifier, but not arguments:\n\(mismatchedResultsMessage)
\(differentArgumentsMatchCount) additional call(s) matched identifier, but not arguments:\n\n\(mismatchedResultsMessage)
"""
} else if differentArgumentsMatchCount > 0 {
let mismatchedResultsMessage = mismatchMessageBuilder.message(for: differentArgumentsMatch)
Expand All @@ -97,7 +97,7 @@ internal class VerificationHandler {
let mismatchedResultsMessage = mismatchMessageBuilder.message(for: differentArgumentsMatch)
message = """
Call with identifier was recorded \(matchCount) times, but expected \(times).
\(differentArgumentsMatchCount) additional call(s) matched identifier, but not arguments:\n\(mismatchedResultsMessage)
\(differentArgumentsMatchCount) additional call(s) matched identifier, but not arguments:\n\n\(mismatchedResultsMessage)
"""
} else if differentArgumentsMatchCount > 0 {
let mismatchedResultsMessage = mismatchMessageBuilder.message(for: differentArgumentsMatch)
Expand Down
2 changes: 2 additions & 0 deletions MimusTests/MismatchMessageBuilderTests.swift
Expand Up @@ -73,9 +73,11 @@ class MismatchMessageBuilderTests: XCTestCase {
Mismatched call #1:
Mismatch in argument #1 - expected <(1)>, but was <(2)>.
Mismatch in argument #2 - expected <(2)>, but was <(3)>.
Mismatched call #2:
Mismatch in argument #1 - expected <(1)>, but was <(2)>.
Mismatch in argument #2 - expected <(2)>, but was <(3)>.
Mismatched call #3:
Mismatch in argument #1 - expected <(1)>, but was <(2)>.
Mismatch in argument #2 - expected <(2)>, but was <(3)>.
Expand Down

0 comments on commit d77ab8e

Please sign in to comment.