Unit Testing in iOS using ReactorKit, Quick & Nimble, RxBlocking, Swift 4.1
Getting Started
We will be using RxBlocking to verify the state of your Reactor (ViewModel). We will also use Stubber to mock our network requests. Finally, your Podfile should contain the following:
def development_pods
pod 'ReactorKit'
pod 'RxSwift'
pod 'RxCocoa'
enddef testing_pods
pod 'RxBlocking'
pod 'Stubber'
pod 'Quick'
pod 'Nimble'
end
Simple Test Flow
A simple flow can involve sending an Action to your Reactor and testing the following things:
- State updates: Checking the updates to each state variable when an action is performed to verify your Reactor logic.
- Execution counts: Making sure that network requests and other function calls are being made the correct number of times.
- Making sure that the state is emitting the correct number of times.
Initial Setup
I use the following folder structure for my tests:
Stubbed Repositories: Repositories are structs that expose public methods which you can use to fetch data using a network request. Stubbed Repositories folder contains mock repos created using Stubber.
ReactorTests: Contains ViewModel/Reactor tests.
Stubs
Mocking your network request is very easy with Stubber.
You can also verify the execution count of your stubbed method by using
Stubber.executions(signIn).count
Unit Tests
In order to test the logic inside Reactor, we need to consider the Reactor as a black box. I will feed an action as input and get UI states as output.
Below I’m triggering the signIn
action with some invalid parameters which should ideally emit an error
state after doing some validation inside Reactor in a synchronous fashion. I’ll use RxBlocking to block the flow of the code until my state
observable emits one event.
Note the use of .delay()
operator. Without it, there are chances of missing some events before the blocking statement on line 7 gets executed.
state
emits one event as soon you subscribe to the stream which is usually the previous state which should be skipped. The skip(1)
helps with this.
Using take(1)
completes the stream after one emission. Otherwise RxBlocking will never allow the statement to fall through.
If the state doesn’t emit within 5 seconds, RxTimeoutException
is thrown by RxBlocking and the test case fails automatically.
After extracting the state, Nimble
is used to verify that the values of the state variables meet expectations.
The stream gets disposed right after all expectations are verified.
What if there are multiple state emissions?
Well if there are multiple emissions then instead of using first()
operator in RxBlocking, we can use toArray()
operator. toArray()
will give you the array of states emitted in the subsequent order.
Then you can use expect
to verify the state variables one by one.
Questions?
The complete project is available on Github. Feel free to share feedback or ask questions in the comments. :)