From 295ba56d965d5d3a427d3e663ca09129a4d16978 Mon Sep 17 00:00:00 2001 From: Marcelo Gobetti Date: Sun, 21 Oct 2018 21:57:00 -0300 Subject: [PATCH 1/4] Added support to RxTest as an alternative to RxBlocking Users of RxNimble may now opt between RxBlocking and RxTest (mutually or exclusively). In order to keep compability with previous versions, the `default_subspec` is "RxBlocking", but "RxNimble/RxTest" can be added to clients' Podfiles, or replace "RxNimble" entirely in order to remove the dependency on `RxBlocking`. --- Demo/Podfile.lock | 10 +++++--- RxNimble.podspec | 25 +++++++++++++++---- Source/{ => Core}/Expectation+Ext.swift | 0 .../Expectation+Blocking.swift | 0 Source/{ => RxBlocking}/RxNimble.swift | 0 Source/RxTest/Equal+RxTest.swift | 16 ++++++++++++ Source/RxTest/Expectation+RxTest.swift | 22 ++++++++++++++++ 7 files changed, 65 insertions(+), 8 deletions(-) rename Source/{ => Core}/Expectation+Ext.swift (100%) rename Source/{ => RxBlocking}/Expectation+Blocking.swift (100%) rename Source/{ => RxBlocking}/RxNimble.swift (100%) create mode 100644 Source/RxTest/Equal+RxTest.swift create mode 100644 Source/RxTest/Expectation+RxTest.swift diff --git a/Demo/Podfile.lock b/Demo/Podfile.lock index 30827b9..4401694 100644 --- a/Demo/Podfile.lock +++ b/Demo/Podfile.lock @@ -3,10 +3,14 @@ PODS: - Quick (1.3.2) - RxBlocking (4.3.1): - RxSwift (~> 4.0) - - RxNimble (4.2.0): + - RxNimble (4.4.0): + - RxNimble/RxBlocking (= 4.4.0) + - RxNimble/Core (4.4.0): - Nimble (~> 7.0) - - RxBlocking (~> 4.0) - RxSwift (~> 4.2) + - RxNimble/RxBlocking (4.4.0): + - RxBlocking + - RxNimble/Core - RxSwift (4.3.1) DEPENDENCIES: @@ -29,7 +33,7 @@ SPEC CHECKSUMS: Nimble: 04f732da099ea4d153122aec8c2a88fd0c7219ae Quick: 2623cb30d7a7f41ca62f684f679586558f483d46 RxBlocking: 64c051285261ca2339481e91b5f70eb06b03660a - RxNimble: cb8c3dd1ca79b83c60744f1c327130c1aef202a1 + RxNimble: 170cdfa19fb020c25608760961fd35053f2a2646 RxSwift: fe0fd770a43acdb7d0a53da411c9b892e69bb6e4 PODFILE CHECKSUM: 7405369509db0cbd242146add66181ab5ab0efb0 diff --git a/RxNimble.podspec b/RxNimble.podspec index 340b4fb..31f35aa 100644 --- a/RxNimble.podspec +++ b/RxNimble.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "RxNimble" - s.version = "4.3.0" + s.version = "4.4.0" s.summary = "Nimble extensions that making unit testing with RxSwift easier 🎉" s.description = <<-DESC This library includes functions that make testing RxSwift projects easier with Nimble. @@ -13,10 +13,25 @@ Pod::Spec.new do |s| s.osx.deployment_target = "10.10" s.tvos.deployment_target = "9.0" s.source = { :git => "https://github.com/RxSwiftCommunity/RxNimble.git", :tag => s.version } - s.source_files = "Source/**/*.swift" - s.dependency "Nimble", "~> 7.0" - s.dependency "RxSwift", "~> 4.2" - s.dependency "RxBlocking", "~> 4.0" + s.default_subspec = "RxBlocking" s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO', 'FRAMEWORK_SEARCH_PATHS' => '$(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"' } + + s.subspec "Core" do |ss| + ss.source_files = "Source/Core/" + ss.dependency "Nimble", "~> 7.0" + ss.dependency "RxSwift", "~> 4.2" + end + + s.subspec "RxBlocking" do |ss| + ss.source_files = "Source/RxBlocking/" + ss.dependency "RxNimble/Core" + ss.dependency "RxBlocking" + end + + s.subspec "RxTest" do |ss| + ss.source_files = "Source/RxTest/" + ss.dependency "RxNimble/Core" + ss.dependency "RxTest" + end end diff --git a/Source/Expectation+Ext.swift b/Source/Core/Expectation+Ext.swift similarity index 100% rename from Source/Expectation+Ext.swift rename to Source/Core/Expectation+Ext.swift diff --git a/Source/Expectation+Blocking.swift b/Source/RxBlocking/Expectation+Blocking.swift similarity index 100% rename from Source/Expectation+Blocking.swift rename to Source/RxBlocking/Expectation+Blocking.swift diff --git a/Source/RxNimble.swift b/Source/RxBlocking/RxNimble.swift similarity index 100% rename from Source/RxNimble.swift rename to Source/RxBlocking/RxNimble.swift diff --git a/Source/RxTest/Equal+RxTest.swift b/Source/RxTest/Equal+RxTest.swift new file mode 100644 index 0000000..64fb2e7 --- /dev/null +++ b/Source/RxTest/Equal+RxTest.swift @@ -0,0 +1,16 @@ +import Nimble +import RxSwift +@testable import RxTest + +public func equal(_ expectedEvents: RecordedEvents) -> Predicate> { + return Predicate.define { actualEvents in + let actualEquatableEvents = try actualEvents.evaluate()?.map { AnyEquatable(target: $0, comparer: ==) } + let expectedEquatableEvents = expectedEvents.map { AnyEquatable(target: $0, comparer: ==) } + + let matches = (actualEquatableEvents == expectedEquatableEvents) + return PredicateResult(bool: matches, + message: .expectedActualValueTo( + "emit <\(stringify(expectedEquatableEvents))>") + ) + } +} diff --git a/Source/RxTest/Expectation+RxTest.swift b/Source/RxTest/Expectation+RxTest.swift new file mode 100644 index 0000000..027079a --- /dev/null +++ b/Source/RxTest/Expectation+RxTest.swift @@ -0,0 +1,22 @@ +import Nimble +import RxSwift +import RxTest + +public typealias RecordedEvents = [Recorded>] + +public extension Expectation where T: ObservableType { + func events(scheduler: TestScheduler, + disposeBag: DisposeBag, + startAt initialTime: Int = 0) -> Expectation> { + return transform { source in + let results = scheduler.createObserver(T.E.self) + + scheduler.scheduleAt(initialTime) { + source?.subscribe(results).disposed(by: disposeBag) + } + scheduler.start() + + return results.events + } + } +} From 67e9eb1ab51391b46e3544b6e9806c8a3a2b21df Mon Sep 17 00:00:00 2001 From: Marcelo Gobetti Date: Sun, 21 Oct 2018 22:15:43 -0300 Subject: [PATCH 2/4] Added tests for RxTest --- Demo/Demo.xcodeproj/project.pbxproj | 18 ++- Demo/DemoTests/AnyError.swift | 5 + ...ts.swift => RxNimbleRxBlockingTests.swift} | 14 +-- Demo/DemoTests/RxNimbleRxTestTests.swift | 106 ++++++++++++++++++ Demo/Podfile | 2 +- Demo/Podfile.lock | 14 ++- Source/RxTest/ThrowError+RxTest.swift | 36 ++++++ 7 files changed, 175 insertions(+), 20 deletions(-) create mode 100644 Demo/DemoTests/AnyError.swift rename Demo/DemoTests/{DemoTests.swift => RxNimbleRxBlockingTests.swift} (85%) create mode 100644 Demo/DemoTests/RxNimbleRxTestTests.swift create mode 100644 Source/RxTest/ThrowError+RxTest.swift diff --git a/Demo/Demo.xcodeproj/project.pbxproj b/Demo/Demo.xcodeproj/project.pbxproj index 78d4d75..9e39b24 100644 --- a/Demo/Demo.xcodeproj/project.pbxproj +++ b/Demo/Demo.xcodeproj/project.pbxproj @@ -7,12 +7,14 @@ objects = { /* Begin PBXBuildFile section */ + 3A4288F2217D7B0000D3651D /* RxNimbleRxTestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4288F1217D7B0000D3651D /* RxNimbleRxTestTests.swift */; }; + 3A4288F4217D7B6200D3651D /* AnyError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4288F3217D7B6200D3651D /* AnyError.swift */; }; 5E47F32C1C3EAE9B00EC0751 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E47F32B1C3EAE9B00EC0751 /* AppDelegate.swift */; }; 5E47F32E1C3EAE9B00EC0751 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E47F32D1C3EAE9B00EC0751 /* ViewController.swift */; }; 5E47F3311C3EAE9B00EC0751 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E47F32F1C3EAE9B00EC0751 /* Main.storyboard */; }; 5E47F3331C3EAE9B00EC0751 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E47F3321C3EAE9B00EC0751 /* Assets.xcassets */; }; 5E47F3361C3EAE9B00EC0751 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E47F3341C3EAE9B00EC0751 /* LaunchScreen.storyboard */; }; - 5E47F3411C3EAE9B00EC0751 /* DemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E47F3401C3EAE9B00EC0751 /* DemoTests.swift */; }; + 5E47F3411C3EAE9B00EC0751 /* RxNimbleRxBlockingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E47F3401C3EAE9B00EC0751 /* RxNimbleRxBlockingTests.swift */; }; D04F8C922A4EA6727B35872D /* Pods_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9265FBADB809B4B52B402D23 /* Pods_Demo.framework */; }; F189612069FC08D6409FD015 /* Pods_DemoTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 305E5AFA494DF2A10E0B8767 /* Pods_DemoTests.framework */; }; /* End PBXBuildFile section */ @@ -30,6 +32,8 @@ /* Begin PBXFileReference section */ 305E5AFA494DF2A10E0B8767 /* Pods_DemoTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DemoTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3388B98AFD3E9302DB045597 /* Pods-DemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DemoTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DemoTests/Pods-DemoTests.debug.xcconfig"; sourceTree = ""; }; + 3A4288F1217D7B0000D3651D /* RxNimbleRxTestTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxNimbleRxTestTests.swift; sourceTree = ""; }; + 3A4288F3217D7B6200D3651D /* AnyError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyError.swift; sourceTree = ""; }; 5E47F3281C3EAE9B00EC0751 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 5E47F32B1C3EAE9B00EC0751 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 5E47F32D1C3EAE9B00EC0751 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -38,7 +42,7 @@ 5E47F3351C3EAE9B00EC0751 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 5E47F3371C3EAE9B00EC0751 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 5E47F33C1C3EAE9B00EC0751 /* DemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 5E47F3401C3EAE9B00EC0751 /* DemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoTests.swift; sourceTree = ""; }; + 5E47F3401C3EAE9B00EC0751 /* RxNimbleRxBlockingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxNimbleRxBlockingTests.swift; sourceTree = ""; }; 5E47F3421C3EAE9B00EC0751 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9265FBADB809B4B52B402D23 /* Pods_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 94276AA888ACDCBD57B00C60 /* Pods-Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.release.xcconfig"; path = "Pods/Target Support Files/Pods-Demo/Pods-Demo.release.xcconfig"; sourceTree = ""; }; @@ -113,7 +117,9 @@ 5E47F33F1C3EAE9B00EC0751 /* DemoTests */ = { isa = PBXGroup; children = ( - 5E47F3401C3EAE9B00EC0751 /* DemoTests.swift */, + 3A4288F3217D7B6200D3651D /* AnyError.swift */, + 5E47F3401C3EAE9B00EC0751 /* RxNimbleRxBlockingTests.swift */, + 3A4288F1217D7B0000D3651D /* RxNimbleRxTestTests.swift */, 5E47F3421C3EAE9B00EC0751 /* Info.plist */, ); path = DemoTests; @@ -259,6 +265,7 @@ "${BUILT_PRODUCTS_DIR}/RxBlocking/RxBlocking.framework", "${BUILT_PRODUCTS_DIR}/RxNimble/RxNimble.framework", "${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework", + "${BUILT_PRODUCTS_DIR}/RxTest/RxTest.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -267,6 +274,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxBlocking.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxNimble.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxTest.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -307,7 +315,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5E47F3411C3EAE9B00EC0751 /* DemoTests.swift in Sources */, + 3A4288F4217D7B6200D3651D /* AnyError.swift in Sources */, + 3A4288F2217D7B0000D3651D /* RxNimbleRxTestTests.swift in Sources */, + 5E47F3411C3EAE9B00EC0751 /* RxNimbleRxBlockingTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Demo/DemoTests/AnyError.swift b/Demo/DemoTests/AnyError.swift new file mode 100644 index 0000000..7b3c347 --- /dev/null +++ b/Demo/DemoTests/AnyError.swift @@ -0,0 +1,5 @@ +import Foundation + +enum AnyError: Error { + case any +} diff --git a/Demo/DemoTests/DemoTests.swift b/Demo/DemoTests/RxNimbleRxBlockingTests.swift similarity index 85% rename from Demo/DemoTests/DemoTests.swift rename to Demo/DemoTests/RxNimbleRxBlockingTests.swift index 6c706d2..a911f50 100644 --- a/Demo/DemoTests/DemoTests.swift +++ b/Demo/DemoTests/RxNimbleRxBlockingTests.swift @@ -3,16 +3,8 @@ import Nimble import RxSwift import RxNimble -class RxNimbleTest: QuickSpec { +class RxNimbleRxBlockingTests: QuickSpec { override func spec() { - /// A type-erased `Swift.Error` for testing purposes - struct AnyError: Swift.Error { - let message: String - init(_ message: String = "") { - self.message = message - } - } - //MARK: First describe("First") { it("works with plain observables") { @@ -39,7 +31,7 @@ class RxNimbleTest: QuickSpec { it("get first error") { let subject = ReplaySubject.createUnbounded() - subject.onError(AnyError()) + subject.onError(AnyError.any) expect(subject).first.to(throwError()) } @@ -64,7 +56,7 @@ class RxNimbleTest: QuickSpec { it("error, if terminated with error") { let subject = ReplaySubject.createUnbounded() subject.onNext("Hello, world!") - subject.onError(AnyError()) + subject.onError(AnyError.any) expect(subject).last.to(throwError()) } diff --git a/Demo/DemoTests/RxNimbleRxTestTests.swift b/Demo/DemoTests/RxNimbleRxTestTests.swift new file mode 100644 index 0000000..c205c8c --- /dev/null +++ b/Demo/DemoTests/RxNimbleRxTestTests.swift @@ -0,0 +1,106 @@ +import Quick +import Nimble +import RxSwift +import RxTest +import RxNimble + +class RxNimbleRxTestTests: QuickSpec { + override func spec() { + describe("Events") { + let initialClock = 0 + var scheduler: TestScheduler! + var disposeBag: DisposeBag! + + beforeEach { + disposeBag = DisposeBag() + scheduler = TestScheduler(initialClock: initialClock, simulateProcessingDelay: false) + } + + it("works with uncompleted streams") { + let subject = scheduler.createHotObservable([ + next(5, "Hello"), + next(10, "World"), + ]) + + expect(subject).events(scheduler: scheduler, disposeBag: disposeBag) + .to(equal([ + Recorded.next(5, "Hello"), + Recorded.next(10, "World") + ])) + } + + it("works with completed streams") { + let subject = scheduler.createHotObservable([ + next(5, "Hello"), + next(10, "World"), + completed(100) + ]) + + expect(subject).events(scheduler: scheduler, disposeBag: disposeBag) + .to(equal([ + Recorded.next(5, "Hello"), + Recorded.next(10, "World"), + Recorded.completed(100) + ])) + } + + it("works with errored streams") { + let subject: TestableObservable = scheduler.createHotObservable([ + error(5, AnyError.any) + ]) + + expect(subject).events(scheduler: scheduler, disposeBag: disposeBag) + .to(equal([ + Recorded.error(5, AnyError.any) + ])) + } + + it("throws error if any event is error") { + let subject = scheduler.createHotObservable([ + Recorded.next(5, "Hello"), + Recorded.next(10, "World"), + error(15, AnyError.any) + ]) + + expect(subject).events(scheduler: scheduler, disposeBag: disposeBag) + .to(throwError()) + } + + it("does not throw error if no errors") { + let subject = scheduler.createHotObservable([ + Recorded.next(5, "Hello"), + Recorded.next(10, "World") + ]) + + expect(subject).events(scheduler: scheduler, disposeBag: disposeBag) + .toNot(throwError()) + } + + it("subscribes at specified initial time") { + let initialTime = 50 + let eventTime = 100 + let subject = scheduler.createColdObservable([ + next(eventTime, "Hi") + ]) + + expect(subject).events(scheduler: scheduler, disposeBag: disposeBag, startAt: initialTime) + .to(equal([ + Recorded.next(initialTime + eventTime, "Hi") + ])) + } + + it("ignores hot stream events before initial time") { + let subject = scheduler.createHotObservable([ + next(5, "Hello"), + next(10, "World"), + completed(100) + ]) + + expect(subject).events(scheduler: scheduler, disposeBag: disposeBag, startAt: 15) + .to(equal([ + Recorded.completed(100) + ])) + } + } + } +} diff --git a/Demo/Podfile b/Demo/Podfile index 9537adb..b476642 100644 --- a/Demo/Podfile +++ b/Demo/Podfile @@ -12,7 +12,7 @@ target 'DemoTests' do pod 'Quick' pod 'Nimble' -pod 'RxNimble', path: '../' +pod 'RxNimble', subspecs: ['RxBlocking', 'RxTest'], path: '../' end diff --git a/Demo/Podfile.lock b/Demo/Podfile.lock index 4401694..8a598d0 100644 --- a/Demo/Podfile.lock +++ b/Demo/Podfile.lock @@ -3,20 +3,24 @@ PODS: - Quick (1.3.2) - RxBlocking (4.3.1): - RxSwift (~> 4.0) - - RxNimble (4.4.0): - - RxNimble/RxBlocking (= 4.4.0) - RxNimble/Core (4.4.0): - Nimble (~> 7.0) - RxSwift (~> 4.2) - RxNimble/RxBlocking (4.4.0): - RxBlocking - RxNimble/Core + - RxNimble/RxTest (4.4.0): + - RxNimble/Core + - RxTest - RxSwift (4.3.1) + - RxTest (4.3.1): + - RxSwift (~> 4.0) DEPENDENCIES: - Nimble - Quick - - RxNimble (from `../`) + - RxNimble/RxBlocking (from `../`) + - RxNimble/RxTest (from `../`) SPEC REPOS: https://github.com/cocoapods/specs.git: @@ -24,6 +28,7 @@ SPEC REPOS: - Quick - RxBlocking - RxSwift + - RxTest EXTERNAL SOURCES: RxNimble: @@ -35,7 +40,8 @@ SPEC CHECKSUMS: RxBlocking: 64c051285261ca2339481e91b5f70eb06b03660a RxNimble: 170cdfa19fb020c25608760961fd35053f2a2646 RxSwift: fe0fd770a43acdb7d0a53da411c9b892e69bb6e4 + RxTest: ea97a208826906f3904c0debdd09575ebcbfe216 -PODFILE CHECKSUM: 7405369509db0cbd242146add66181ab5ab0efb0 +PODFILE CHECKSUM: 41dbff57d3169ba9e4e1dd63856bcace5b3f96c1 COCOAPODS: 1.5.3 diff --git a/Source/RxTest/ThrowError+RxTest.swift b/Source/RxTest/ThrowError+RxTest.swift new file mode 100644 index 0000000..99e072b --- /dev/null +++ b/Source/RxTest/ThrowError+RxTest.swift @@ -0,0 +1,36 @@ +import Nimble +import RxSwift +import RxTest + +public func throwError() -> Predicate> { + func extractError(_ recorded: RecordedEvents?) -> [Error]? { + func extractError(_ recorded: Recorded>) -> Error? { + return recorded.value.error + } + + #if swift(>=4.1) + return recorded?.compactMap(extractError) + #else + return recorded?.flatMap(extractError) + #endif + } + + + return Predicate { actualEvents in + var actualError: Error? + do { + let recordedEvents = try actualEvents.evaluate() + if let error = extractError(recordedEvents)?.first { + throw error + } + } catch { + actualError = error + } + + if let actualError = actualError { + return PredicateResult(bool: true, message: .expectedCustomValueTo("throw any error", "<\(actualError)>")) + } else { + return PredicateResult(bool: false, message: .expectedCustomValueTo("throw any error", "no error")) + } + } +} From 6dd63fd6cc327b8bb4d431c4741195643e6d2041 Mon Sep 17 00:00:00 2001 From: Marcelo Gobetti Date: Mon, 22 Oct 2018 18:54:26 -0300 Subject: [PATCH 3/4] Added docs to RxTest methods --- Source/RxTest/Equal+RxTest.swift | 1 + Source/RxTest/Expectation+RxTest.swift | 7 +++++++ Source/RxTest/ThrowError+RxTest.swift | 2 ++ 3 files changed, 10 insertions(+) diff --git a/Source/RxTest/Equal+RxTest.swift b/Source/RxTest/Equal+RxTest.swift index 64fb2e7..3349538 100644 --- a/Source/RxTest/Equal+RxTest.swift +++ b/Source/RxTest/Equal+RxTest.swift @@ -2,6 +2,7 @@ import Nimble import RxSwift @testable import RxTest +/// A Nimble matcher that succeeds when the actual events are equal to the expected events. public func equal(_ expectedEvents: RecordedEvents) -> Predicate> { return Predicate.define { actualEvents in let actualEquatableEvents = try actualEvents.evaluate()?.map { AnyEquatable(target: $0, comparer: ==) } diff --git a/Source/RxTest/Expectation+RxTest.swift b/Source/RxTest/Expectation+RxTest.swift index 027079a..bfe8e7b 100644 --- a/Source/RxTest/Expectation+RxTest.swift +++ b/Source/RxTest/Expectation+RxTest.swift @@ -5,6 +5,13 @@ import RxTest public typealias RecordedEvents = [Recorded>] public extension Expectation where T: ObservableType { + /// Make an expectation on the events emitted by an observable. + /// + /// - Parameters: + /// - scheduler: the scheduler used to record events in virtual time units. + /// - disposeBag: the dispose bag that will dispose all of its resources between tests. + /// - initialTime: the time at which subscription/recording should begin. + /// - Returns: an expectation of the actual events emitted by the observable. func events(scheduler: TestScheduler, disposeBag: DisposeBag, startAt initialTime: Int = 0) -> Expectation> { diff --git a/Source/RxTest/ThrowError+RxTest.swift b/Source/RxTest/ThrowError+RxTest.swift index 99e072b..25c8a1c 100644 --- a/Source/RxTest/ThrowError+RxTest.swift +++ b/Source/RxTest/ThrowError+RxTest.swift @@ -2,6 +2,8 @@ import Nimble import RxSwift import RxTest +/// A Nimble matcher that succeeds when the actual events emit an error +/// of any type. public func throwError() -> Predicate> { func extractError(_ recorded: RecordedEvents?) -> [Error]? { func extractError(_ recorded: Recorded>) -> Error? { From 8cea8497d92b5e11827159c254ad9284100a8e52 Mon Sep 17 00:00:00 2001 From: Marcelo Gobetti Date: Mon, 22 Oct 2018 19:57:40 -0300 Subject: [PATCH 4/4] Added RxTest support to Changelog and updated README --- Changelog.md | 2 +- README.md | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 55e4041..703d9f3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ ## Current Master -- Nothing yet. +- Added support to RxTest. Users may now choose between `RxTest` and `RxBlocking` (or both) ## 4.3.0 - Swift 4.2 support diff --git a/README.md b/README.md index d2a0aaa..2318660 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,48 @@ expect(observable).first == 42 Nice. +--- + +If on the other hand you'd rather use [RxTest](http://cocoapods.org/pods/RxTest) instead of `RxBlocking`, you can do it by specifying RxNimble's `RxTest` subspec. With _RxTest_ you can have more powerful tests, checking a stream as a whole instead of being limited to `first`, `last` and `array` (while the last 2 implicitly require the stream to have completed). + +That means _RxTest_ allows you to verify the occurrence of multiple `next`, `error` and `completed` events at specific virtual times: + +``` +expect(subject).events(scheduler: scheduler, disposeBag: disposeBag) + .to(equal([ + Recorded.next(5, "Hello"), + Recorded.next(10, "World"), + Recorded.completed(100) + ])) +``` + +You may also verify specific error types: + +``` +expect(imageSubject).events(scheduler: scheduler, disposeBag: disposeBag) + .to(equal([ + Recorded.error(5, ImageError.invalidImage) + ])) +``` + ## Installation -Add to your podfile: +Add to the tests target in your Podfile: + +```rb +pod 'RxNimble' # same as RxNimble/RxBlocking +``` + +or + +```rb +pod 'RxNimble/RxTest' # installs RxTest instead of RxBlocking +``` + +or even ```rb -pod 'RxNimble' +pod 'RxNimble', subspecs: ['RxBlocking', 'RxTest'] # installs both dependencies ``` And `pod install` and that's it!