Skip to content

Commit

Permalink
Add noInteraction for Then (#98)
Browse files Browse the repository at this point in the history
* Add noInteraction

* Generate XCTestManifests.swift

* Fix SwiftFormat

* Update CHANGELOG.md

* update codacy duplication

* update codacy duplication
  • Loading branch information
leoture committed May 6, 2020
1 parent a88bb44 commit 7dac9f8
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 9 deletions.
3 changes: 2 additions & 1 deletion .codacy.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
engines:
duplication:
enabled: false
exclude_paths:
- '**/*Tests.swift'
exclude_paths:
- 'docs/**'
- 'fastlane/**'
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
## Unrelease
[Compare](https://github.com/leoture/MockSwift/compare/v0.6.0...HEAD)
#### Added
- Add noInteraction for Then [#98](https://github.com/leoture/MockSwift/pull/98) by [Jordhan Leoture](https://github.com/leoture)

#### Changed

Expand Down
1 change: 1 addition & 0 deletions Sources/MockSwift/Core/Mock/Call/CallRegister.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import Foundation

protocol CallRegister {
var isEmpty: Bool { get }
func recordCall(for identifier: FunctionIdentifier, with parameters: [ParameterType])
func recordedCall(for identifier: FunctionIdentifier, when matchs: [AnyPredicate]) -> [FunctionCall]
}
4 changes: 4 additions & 0 deletions Sources/MockSwift/Core/Mock/Mock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ public class Mock<WrappedType> {
// MARK: - CallRegister

extension Mock: CallRegister {
var isEmpty: Bool {
callRegister.isEmpty
}

func recordCall(for identifier: FunctionIdentifier, with parameters: [ParameterType]) {
callRegister.recordCall(for: identifier, with: parameters)
}
Expand Down
19 changes: 17 additions & 2 deletions Sources/MockSwift/Core/Then/Then.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ import Foundation

func then<WrappedType>(_ value: WrappedType,
errorHandler: ErrorHandler,
failureRecorder: FailureRecorder,
file: StaticString,
line: UInt) -> Then<WrappedType> {
guard let mock = value as? Mock<WrappedType> else {
return errorHandler.handle(InternalError.cast(source: value, target: Mock<WrappedType>.self))
}
return Then(callRegister: mock, failureRecorder: XCTestFailureRecorder())
return Then(callRegister: mock, failureRecorder: failureRecorder)
}

// MARK: - Public Global Methods
Expand All @@ -48,7 +49,7 @@ func then<WrappedType>(_ value: WrappedType,
public func then<WrappedType>(_ value: WrappedType,
file: StaticString = #file,
line: UInt = #line) -> Then<WrappedType> {
then(value, errorHandler: ErrorHandler(), file: file, line: line)
then(value, errorHandler: ErrorHandler(), failureRecorder: XCTestFailureRecorder(), file: file, line: line)
}

/// Call `completion` with a `Then` based on `value`.
Expand Down Expand Up @@ -85,6 +86,20 @@ public class Then<WrappedType> {
}
}

public extension Then {
/// Checks that the `WrappedType` value has not registered calls.
/// - Parameters:
/// - file: The file name where the method is called.
/// - line: The line where the method is called.
func noInteraction(file: StaticString = #file, line: UInt = #line) {
if !callRegister.isEmpty {
failureRecorder.recordFailure(message: "\(WrappedType.self) expect to have no interaction.",
file: file,
line: line)
}
}
}

// MARK: - VerifiableBuilder

protocol VerifiableBuilder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import Foundation
class FunctionCallRegister: CallRegister {
private var calls: [FunctionIdentifier: [FunctionCall]]

var isEmpty: Bool {
calls.isEmpty
}

init() {
calls = [:]
}
Expand Down
12 changes: 12 additions & 0 deletions Tests/MockSwiftTests/Core/Mock/Mock+CallRegisterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,16 @@ final class MockCallRegisterTests: XCTestCase {
XCTAssertEqual(result.count, 1)
XCTAssertEqual(result[0].parameters as? [Int], [1, 2])
}

func test_isEmpty_shouldReturnFromCallRegister() {
// Given
callRegister.isEmptyReturn = true

// When
let result = mock.isEmpty

// Then
XCTAssertEqual(callRegister.isEmptyCallCount, 1)
XCTAssertTrue(result)
}
}
68 changes: 63 additions & 5 deletions Tests/MockSwiftTests/Core/Then/ThenTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,21 @@ private class CustomImpl: Custom {}
extension Mock: Custom where WrappedType == Custom {}

class ThenTests: XCTestCase {
@Mock private var customMock: Custom
private let customImpl = CustomImpl()
private let errorHandler = ErrorHandlerMock()
private var customMock: Mock<Custom>!
private var errorHandler: ErrorHandlerMock!
private var callRegister: CallRegisterMock!
private var failureRecorder: FailureRecorderMock!

override func setUp() {
errorHandler = ErrorHandlerMock()
failureRecorder = FailureRecorderMock()
callRegister = CallRegisterMock()
customMock = Mock(callRegister: callRegister,
behaviourRegister: BehaviourRegisterMock(),
stubRegister: StubRegisterMock(),
strategy: UnresolvedStrategy(errorHandler),
errorHandler: errorHandler)
}

func test_then_shouldPass() {
let _: Then<Custom> = then(customMock)
Expand All @@ -45,16 +57,62 @@ class ThenTests: XCTestCase {
then(customMock) { (_: Then<Custom>) in }
}

func test_then_shouldFailWithCast() {
func test_then_whenTypeIsNotAMockShouldFailWithCast() {
// Given
let customImpl = CustomImpl()
let thenCustom: Then<Custom> = then(customMock)
errorHandler.handleReturn = thenCustom

// When
let result: Then<Custom> = then(customImpl, errorHandler: errorHandler, file: "file", line: 42)
let result: Then<Custom> = then(customImpl,
errorHandler: errorHandler,
failureRecorder: failureRecorder,
file: "file",
line: 42)

// Then
XCTAssertTrue(result === thenCustom)
XCTAssertEqual(errorHandler.handleReceived[0], .cast(source: customImpl, target: Mock<Custom>.self))
}

func test_noInteraction_whenWhenCallRegisterIsEmptyShouldCorrectlyCallFailureRecorder() {
// Given
let thenCustom: Then<Custom> = then(customMock)
errorHandler.handleReturn = thenCustom
callRegister.isEmptyReturn = false

// When
let result: Then<Custom> = then(customMock,
errorHandler: errorHandler,
failureRecorder: failureRecorder,
file: "",
line: 0)
result.noInteraction(file: "file", line: 42)

// Then
XCTAssertEqual(callRegister.isEmptyCallCount, 1)
XCTAssertEqual(failureRecorder.recordFailureReceived.count, 1)
let (message, file, line) = failureRecorder.recordFailureReceived[0]
XCTAssertEqual(message, "Custom expect to have no interaction.")
XCTAssertEqual("\(file) \(line)", "file 42")
}

func test_noInteraction_whenWhenCallRegisterIsNotEmptyShouldNotCallFailureRecorder() {
// Given
let thenCustom: Then<Custom> = then(customMock)
errorHandler.handleReturn = thenCustom
callRegister.isEmptyReturn = true

// When
let result: Then<Custom> = then(customMock,
errorHandler: errorHandler,
failureRecorder: failureRecorder,
file: "",
line: 0)
result.noInteraction(file: "file", line: 42)

// Then
XCTAssertEqual(callRegister.isEmptyCallCount, 1)
XCTAssertEqual(failureRecorder.recordFailureReceived.count, 0)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,27 @@ class FunctionCallRegisterTests: XCTestCase {
XCTAssertEqual(calls[0].parameters[0] as? UUID, firstParameter)
XCTAssertEqual(calls[1].parameters[0] as? UUID, secondParameter)
}

func test_isEmpty_whenCallHasNotBeenRecordedShouldReturnTrue() {
// Given

// When
let result = functionCallRegister.isEmpty

// Then
XCTAssertTrue(result)
}

func test_isEmpty_whenCallHasBeenRecordedShouldReturnFalse() {
// Given
let predicate = AnyPredicateMock()
predicate.satisfyReturn = false
functionCallRegister.recordCall(for: .stub(), with: [0])

// When
let result = functionCallRegister.isEmpty

// Then
XCTAssertFalse(result)
}
}
7 changes: 7 additions & 0 deletions Tests/MockSwiftTests/Helpers/Mocks/CallRegisterMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ import Foundation
@testable import MockSwift

class CallRegisterMock: CallRegister {
var isEmptyReturn: Bool!
private(set) var isEmptyCallCount = 0
var isEmpty: Bool {
isEmptyCallCount += 1
return isEmptyReturn
}

var recordCallReceived: [(identifier: FunctionIdentifier, parameters: [ParameterType])] = []
func recordCall(for identifier: FunctionIdentifier, with parameters: [ParameterType]) {
recordCallReceived.append((identifier, parameters))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,8 @@ class ThenIntegrationTests: XCTestCase {
// Then
then(custom)[x: 1, y: 2].set(.match(when: \.isEmpty)).called(times: 0)
}

func test_noInteraction_whenNoInteractionWithMockShouldSucceed() {
then(custom).noInteraction()
}
}
8 changes: 7 additions & 1 deletion Tests/MockSwiftTests/XCTestManifests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ extension FunctionCallRegisterTests {
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__FunctionCallRegisterTests = [
("test_isEmpty_whenCallHasBeenRecordedShouldReturnFalse", test_isEmpty_whenCallHasBeenRecordedShouldReturnFalse),
("test_isEmpty_whenCallHasNotBeenRecordedShouldReturnTrue", test_isEmpty_whenCallHasNotBeenRecordedShouldReturnTrue),
("test_recordedCalls_shouldReturnEmptyWhenNoFunctionCall", test_recordedCalls_shouldReturnEmptyWhenNoFunctionCall),
("test_recordedCalls_shouldReturnEmptyWhenNoFunctionCallMatched", test_recordedCalls_shouldReturnEmptyWhenNoFunctionCallMatched),
("test_recordedCalls_shouldReturnFunctionCallsMatched", test_recordedCalls_shouldReturnFunctionCallsMatched),
Expand Down Expand Up @@ -194,6 +196,7 @@ extension MockCallRegisterTests {
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__MockCallRegisterTests = [
("test_isEmpty_shouldReturnFromCallRegister", test_isEmpty_shouldReturnFromCallRegister),
("test_recordCall_shouldCallRegister", test_recordCall_shouldCallRegister),
("test_recordedCall_shouldReturnFromCallRegister", test_recordedCall_shouldReturnFromCallRegister),
]
Expand Down Expand Up @@ -488,6 +491,7 @@ extension ThenIntegrationTests {
static let __allTests__ThenIntegrationTests = [
("test_callCount_whenParametersMatched", test_callCount_whenParametersMatched),
("test_function_shouldBeCalledWhenParametersMatched", test_function_shouldBeCalledWhenParametersMatched),
("test_noInteraction_whenNoInteractionWithMockShouldSucceed", test_noInteraction_whenNoInteractionWithMockShouldSucceed),
("test_Readable_get_shouldBeCalled", test_Readable_get_shouldBeCalled),
("test_receivedParameters_whenParametersMatched", test_receivedParameters_whenParametersMatched),
("test_subscriptFirstSecond_get_shouldBeCalled", test_subscriptFirstSecond_get_shouldBeCalled),
Expand All @@ -506,8 +510,10 @@ extension ThenTests {
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__ThenTests = [
("test_then_shouldFailWithCast", test_then_shouldFailWithCast),
("test_noInteraction_whenWhenCallRegisterIsEmptyShouldCorrectlyCallFailureRecorder", test_noInteraction_whenWhenCallRegisterIsEmptyShouldCorrectlyCallFailureRecorder),
("test_noInteraction_whenWhenCallRegisterIsNotEmptyShouldNotCallFailureRecorder", test_noInteraction_whenWhenCallRegisterIsNotEmptyShouldNotCallFailureRecorder),
("test_then_shouldPass", test_then_shouldPass),
("test_then_whenTypeIsNotAMockShouldFailWithCast", test_then_whenTypeIsNotAMockShouldFailWithCast),
("test_thenCompletion_shouldPass", test_thenCompletion_shouldPass),
]
}
Expand Down

0 comments on commit 7dac9f8

Please sign in to comment.