From d6e0345d665e609587594380a8a57a67fafc1678 Mon Sep 17 00:00:00 2001 From: Andrey Ostanin Date: Tue, 2 Apr 2019 19:54:53 +0300 Subject: [PATCH] Adds matchers for Swift 5 Result type --- Nimble.xcodeproj/project.pbxproj | 16 +++++ README.md | 30 ++++++++++ Sources/Nimble/Matchers/BeResult.swift | 33 +++++++++++ Tests/NimbleTests/Matchers/BeResultTest.swift | 59 +++++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 Sources/Nimble/Matchers/BeResult.swift create mode 100644 Tests/NimbleTests/Matchers/BeResultTest.swift diff --git a/Nimble.xcodeproj/project.pbxproj b/Nimble.xcodeproj/project.pbxproj index fc2f9a593..d61846cae 100644 --- a/Nimble.xcodeproj/project.pbxproj +++ b/Nimble.xcodeproj/project.pbxproj @@ -24,6 +24,12 @@ 0477153523B740AD00402D4E /* DispatchTimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0477153423B740AD00402D4E /* DispatchTimeInterval.swift */; }; 0477153623B740B700402D4E /* DispatchTimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0477153423B740AD00402D4E /* DispatchTimeInterval.swift */; }; 0477153723B740B800402D4E /* DispatchTimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0477153423B740AD00402D4E /* DispatchTimeInterval.swift */; }; + 106112BD2251DFE7000A5848 /* BeResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 106112BC2251DFE7000A5848 /* BeResult.swift */; }; + 106112C02251E0FA000A5848 /* BeResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 106112BC2251DFE7000A5848 /* BeResult.swift */; }; + 106112C22251E0FD000A5848 /* BeResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 106112BC2251DFE7000A5848 /* BeResult.swift */; }; + 106112C52251E13B000A5848 /* BeResultTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 106112C42251E13B000A5848 /* BeResultTest.swift */; }; + 106112C62251E13B000A5848 /* BeResultTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 106112C42251E13B000A5848 /* BeResultTest.swift */; }; + 106112C72251E13B000A5848 /* BeResultTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 106112C42251E13B000A5848 /* BeResultTest.swift */; }; 1F0648CC19639F5A001F9C46 /* ObjectWithLazyProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0648CB19639F5A001F9C46 /* ObjectWithLazyProperty.swift */; }; 1F0648CD19639F5A001F9C46 /* ObjectWithLazyProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0648CB19639F5A001F9C46 /* ObjectWithLazyProperty.swift */; }; 1F0648D41963AAB2001F9C46 /* SynchronousTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0648D31963AAB2001F9C46 /* SynchronousTest.swift */; }; @@ -492,6 +498,8 @@ /* Begin PBXFileReference section */ 0477153423B740AD00402D4E /* DispatchTimeInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DispatchTimeInterval.swift; sourceTree = ""; }; + 106112BC2251DFE7000A5848 /* BeResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeResult.swift; sourceTree = ""; }; + 106112C42251E13B000A5848 /* BeResultTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeResultTest.swift; sourceTree = ""; }; 1F0648CB19639F5A001F9C46 /* ObjectWithLazyProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectWithLazyProperty.swift; sourceTree = ""; }; 1F0648D31963AAB2001F9C46 /* SynchronousTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronousTest.swift; sourceTree = ""; }; 1F14FB63194180C5009F2A08 /* utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = utils.swift; sourceTree = ""; }; @@ -812,6 +820,7 @@ 1F925F0A195C18E100ED456B /* BeLessThanTest.swift */, 1F925EEE195C136500ED456B /* BeLogicalTest.swift */, 1F925EF8195C175000ED456B /* BeNilTest.swift */, + 106112C42251E13B000A5848 /* BeResultTest.swift */, 1F91DD2C1C74BF36002C309F /* BeVoidTest.swift */, 7B13BA091DD360DE00C9098C /* ContainElementSatisfyingTest.swift */, 1F925F01195C189500ED456B /* ContainTest.swift */, @@ -864,6 +873,7 @@ 1FD8CD161968AB07008ED995 /* BeLessThanOrEqual.swift */, 1FD8CD171968AB07008ED995 /* BeLogical.swift */, 1FD8CD181968AB07008ED995 /* BeNil.swift */, + 106112BC2251DFE7000A5848 /* BeResult.swift */, 1F91DD301C74BF61002C309F /* BeVoid.swift */, 1FD8CD1A1968AB07008ED995 /* Contain.swift */, 7B13BA051DD360AA00C9098C /* ContainElementSatisfying.swift */, @@ -1321,6 +1331,7 @@ CDFB6A401F7E082500AD8CC7 /* CwlDarwinDefinitions.swift in Sources */, 1FD8CD401968AB07008ED995 /* BeCloseTo.swift in Sources */, 1F1871C81CA89EDB00A34BF2 /* NMBExceptionCapture.m in Sources */, + 106112C02251E0FA000A5848 /* BeResult.swift in Sources */, 1FD8CD361968AB07008ED995 /* Expectation.swift in Sources */, 1FD8CD321968AB07008ED995 /* NimbleXCTestHandler.swift in Sources */, 1F43728F1A1B344000EB80F8 /* Stringers.swift in Sources */, @@ -1430,6 +1441,7 @@ 1F299EAB19627B2D002641AF /* BeEmptyTest.swift in Sources */, 7B13BA111DD361EB00C9098C /* ObjCContainElementSatisfyingTest.m in Sources */, 1F925EF6195C147800ED456B /* BeCloseToTest.swift in Sources */, + 106112C62251E13B000A5848 /* BeResultTest.swift in Sources */, 1F4A56791A3B32E3009E1637 /* ObjCBeGreaterThanOrEqualToTest.m in Sources */, A8A3B6F6207329DD00E25A08 /* SatisfyAllOfTest.swift in Sources */, AE7ADE491C80C00D00B94CD3 /* MatchErrorTest.swift in Sources */, @@ -1505,6 +1517,7 @@ 1F5DF17A1BDCA0F500C3A531 /* BeEmpty.swift in Sources */, 1F5DF18C1BDCA0F500C3A531 /* Await.swift in Sources */, 1F1871D81CA89EEF00A34BF2 /* NMBStringify.m in Sources */, + 106112C22251E0FD000A5848 /* BeResult.swift in Sources */, 1F5DF1821BDCA0F500C3A531 /* BeNil.swift in Sources */, 1F5DF16F1BDCA0F500C3A531 /* AssertionDispatcher.swift in Sources */, 964CFEFF1C4FF48900513336 /* ThrowAssertion.swift in Sources */, @@ -1568,6 +1581,7 @@ 1F5DF1A61BDCA10200C3A531 /* EndWithTest.swift in Sources */, CD79C9A31D2CC841004B6F9A /* ObjCBeFalseTest.m in Sources */, 1F5DF1A71BDCA10200C3A531 /* EqualTest.swift in Sources */, + 106112C72251E13B000A5848 /* BeResultTest.swift in Sources */, CD79C9AA1D2CC848004B6F9A /* ObjCBeLessThanOrEqualToTest.m in Sources */, 1F5DF1931BDCA10200C3A531 /* SynchronousTest.swift in Sources */, CD79C9A11D2CC83B004B6F9A /* ObjCBeCloseToTest.m in Sources */, @@ -1602,6 +1616,7 @@ CDFB6A3F1F7E082500AD8CC7 /* CwlDarwinDefinitions.swift in Sources */, 1FD8CD411968AB07008ED995 /* BeCloseTo.swift in Sources */, 1F1871D31CA89EEE00A34BF2 /* NMBExceptionCapture.m in Sources */, + 106112BD2251DFE7000A5848 /* BeResult.swift in Sources */, 1FD8CD371968AB07008ED995 /* Expectation.swift in Sources */, 1FD8CD331968AB07008ED995 /* NimbleXCTestHandler.swift in Sources */, 1F43728E1A1B343F00EB80F8 /* Stringers.swift in Sources */, @@ -1719,6 +1734,7 @@ 1F4A56801A3B333F009E1637 /* ObjCBeLessThanTest.m in Sources */, 1F925EE7195C121200ED456B /* AsynchronousTest.swift in Sources */, 1F0648CD19639F5A001F9C46 /* ObjectWithLazyProperty.swift in Sources */, + 106112C52251E13B000A5848 /* BeResultTest.swift in Sources */, 1F4A56861A3B33A0009E1637 /* ObjCBeTruthyTest.m in Sources */, DD9A9A9019CF43AD00706F49 /* BeIdenticalToObjectTest.swift in Sources */, 1F4BB8B61DACA0E30048464B /* ThrowAssertionTest.swift in Sources */, diff --git a/README.md b/README.md index aca6f67c4..affb5f093 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ expect(ocean.isClean).toEventually(beTruthy()) - [Collection Elements](#collection-elements) - [Collection Count](#collection-count) - [Notifications](#notifications) + - [Result](#result) - [Matching a value to any of a group of matchers](#matching-a-value-to-any-of-a-group-of-matchers) - [Custom Validation](#custom-validation) - [Writing Your Own Matchers](#writing-your-own-matchers) @@ -1190,6 +1191,35 @@ expect { > This matcher is only available in Swift. +## Result + +```swift +// Swift +let aResult: Result = .success("Hooray") + +// passes if result is .success +expect(aResult).to(beSuccess()) + +// passes if result value is .success and validates Success value +expect(aResult).to(beSuccess { value in + expect(value).to(equal("Hooray")) +}) + + +enum AnError: Error { + case somethingHappened +} +let otherResult: Result = .failure(AnError.somethingHappened) + +// passes if result is .failure +expect(otherResult).to(beFailure()) + +// passes if result value is .failure and validates error +expect(otherResult).to(beFailure { error in + expect(error).to(matchError(AnError.somethingHappened)) +}) +``` + ## Matching a value to any of a group of matchers ```swift diff --git a/Sources/Nimble/Matchers/BeResult.swift b/Sources/Nimble/Matchers/BeResult.swift new file mode 100644 index 000000000..84ac6fba6 --- /dev/null +++ b/Sources/Nimble/Matchers/BeResult.swift @@ -0,0 +1,33 @@ +import Foundation + +/// +/// A Nimble matcher for Result that succeeds when the actual value is success +/// +/// You can pass a closure to do any arbitrary custom matching +/// to the value inside result. The closure only gets called when result is success. +public func beSuccess(test: ((T) -> Void)? = nil) -> Predicate> { + return Predicate.define("be ") { expression, message in + guard case let .success(value)? = try expression.evaluate() + else { + return PredicateResult(status: .doesNotMatch, message: message) + } + test?(value) + return PredicateResult(status: .matches, message: message) + } +} + +/// +/// A Nimble matcher for Result that succeeds when the actual value is failure +/// +/// You can pass a closure to do custom matching for the error inside result. +/// The closure only gets called when result is failure. +public func beFailure(test: ((Error) -> Void)? = nil) -> Predicate> { + return Predicate.define("be ") { expression, message in + guard case let .failure(error)? = try expression.evaluate() + else { + return PredicateResult(status: .doesNotMatch, message: message) + } + test?(error) + return PredicateResult(status: .matches, message: message) + } +} diff --git a/Tests/NimbleTests/Matchers/BeResultTest.swift b/Tests/NimbleTests/Matchers/BeResultTest.swift new file mode 100644 index 000000000..4b6496126 --- /dev/null +++ b/Tests/NimbleTests/Matchers/BeResultTest.swift @@ -0,0 +1,59 @@ +import XCTest +import Nimble + +private struct StubError: Error, CustomDebugStringConvertible { + let debugDescription = "StubError" +} + +final class BeSuccessTest: XCTestCase { + func testPositiveMatch() { + let successfulResult: Result = .success(1) + expect(successfulResult).to(beSuccess()) + } + + func testPositiveMatchWithValueTesting() { + let stubValue = 1 + let successfulResult: Result = .success(stubValue) + expect(successfulResult).to(beSuccess { value in + expect(value).to(equal(stubValue)) + }) + } + + func testNegativeMatch() { + let failureResult: Result = .failure(StubError()) + expect(failureResult).toNot(beSuccess()) + } + + func testExpectationFailureMessage() { + let failureResult: Result = .failure(StubError()) + failsWithErrorMessage("expected to be , got ") { + expect(failureResult).to(beSuccess()) + } + } +} + +final class BeFailureTest: XCTestCase { + func testPositiveMatch() { + let failureResult: Result = .failure(StubError()) + expect(failureResult).to(beFailure()) + } + + func testPositiveMatchWithValueTesting() { + let failureResult: Result = .failure(StubError()) + expect(failureResult).to(beFailure { value in + expect(value).to(matchError(StubError.self)) + }) + } + + func testNegativeMatch() { + let successfulResult: Result = .success(1) + expect(successfulResult).toNot(beFailure()) + } + + func testExpectationFailureMessage() { + let successfulResult: Result = .success(1) + failsWithErrorMessage("expected to be , got ") { + expect(successfulResult).to(beFailure()) + } + } +}