XCTest Single Asynchronous SetUp With Semaphores - swift

I am working on testing an API through Alamofire. I need to make a single call to the server to prepare it for the integration test. Once that is done, I am ready to start running tests.
The usual override setUp() is run for every test, so I do not want to do that.
I have therefore chosen to override the class setUp() as described here: https://developer.apple.com/reference/xctest/xctestcase
That's all well and good, but now, I no longer can use the standard waitForExpectations. (In the class override setUp()) I get several compiler errors that tell me that I am no longer calling the same waitForExpectations because I am in a class method, not a test case.
To try to get around this, I wanted to use a semaphore like so:
class ServiceLayerTests: XCTestCase {
static var apiService: APIService = APIService()
let sessionManager = SessionManager(serverTrustPolicyManager: ServerTrustPolicyManager(policies: ["http://localhost:3000/": .disableEvaluation]))
static var companyManger: UserWebResource?
static var companyManagerID = -1
override class func setUp() {
apiService.baseURL = "http://localhost:3000/"
beginIntegrationTests()
}
class func beginIntegrationTests() {
var semaphore = DispatchSemaphore(value: 0)
apiService.beginIntegrationTests(completion: {resource, error in
if let resource = resource {
if let manager = resource as? UserWebResource {
companyManger = manager
companyManagerID = manager.id
semaphore.signal()
}
}
})
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
}
}
This does not work. Under the hood, there is an alamo fire call to the server and it responds with the user to use for the integration tests. I do see the server spinning, so I know that the actual communication is happening, but I never get into the completion closure.
I suspect I am not understanding how Swift does semaphores and that I have created a deadlock somehow. If somebody has a better solution, I'd be more than happy to hear it.

I get several compiler errors that tell me that I am no longer calling
the same waitForExpectations because I am in a class method, not a
test case
That makes sense. What you probably want is to refactor so that you are in a test case:
override class func setUp() {
apiService.baseURL = "http://localhost:3000/"
}
func testIntegrationTests() {
let urlExpectation = expectation(description: "INTEGRATION TEST")
apiService.beginIntegrationTests(completion: {resource, error in
// ...
urlExpectation.fulfill()
})
// not sure what an acceptable timeout would be, I chose this at random
waitForExpectations(timeout: 25) { error in
if let error = error {
print("Error: \(error.localizedDescription)")
}
}
}
One of the best resources with some good test examples can be found here: http://nshipster.com/xctestcase/

You can create the expectation as a lazy var that executes your one-time set up and fulfills on completion.
At the beginning of your per-test setUp() function you can wait for that expectation.
None of your tests will run until it is fulfilled, and the initial setup will run only once.
class WaitForSetup_Tests: XCTestCase {
lazy var initialSetupFinished: XCTestExpectation = {
let initialSetupFinished = expectation(description: "initial setup finished")
initialSetupTask() { // your setup task that executes this closure on completion
initialSetupFinished.fulfill()
return
}
return initialSetupFinished
}()
override func setUp() {
wait(for: [initialSignOutFinished], timeout: 2.0)
// do your per-test setup here
}
}
Note: This solution avoids using the override class function setUp() class method, because I couldn't figure out how to use the expectations except for in an instance.

Related

Unit Test: Evaluate Singleton into Function

I need your help, this time for Unit Test and how to evaluate a Singleton into Function. This is my context:
I have a class, which it has a Singleton like this:
class ClassToTest (){
var testMessage = ""
init(){
// This is only a empty init
}
func funcWhichHasASingleton() {
testMessage = "Function was covered"
MySingleton.shared.sendAnalytics("params")
}
}
As you can see, there is a singleton where is sending analytics
MySingleton.shared.sendAnalytics("params")
And I have my test function like this:
func testExample() {
// Given
let sut = ClassToTest()
// When
sut.funcWhichHasASingleton()
// Then
XCTAssertEqual(sut.testMessage, "Function was covered")
}
The my question is: how I can test the Singleton into ClassToTest, such as Xcode pass into that function but my SonarQube says I need to cover the Singleton line. How can I do that?
Breaking this down a little, what you actually need to to test is not the function being called but what the function does. In this case, I think you want to make sure the analytics are being called.
In which case you would need to do something like:
class ClassToTest {
let analytics: MySingleton
init(analytics: MySingleton) {
self.analytics = analytics
}
func funcWhichHasASingleton() {
analytics.sendAnalytics("params")
}
}
Then you are in a position to test the analytics, possibly with a mock.
func testExample() {
let analytics = MySingleton.shared // or better, some kind of Mock class.
// Given
let sut = ClassToTest(analytics: analytics)
// When
sut.funcWhichHasASingleton()
// Then
XCTAssertEqual(analytics.messageCount, 1) // You would need to be able to monitor what the singletons does.
}
Potentially, if you can have a mock MySingleton, then you can do this. Maybe using Mockingbird or use a protocol witness.
let didSendSendExpectation = expectation("")
analytics.didRecieve { message in
didSendSendExpectation.fulfill()
}
// When
sut.funcWhichHasASingleton()
wait(for: didSendSendExpectation, timeout: 0.1)

Swift Unit Test function that async dispatches a block that sets a variable I want to test

I have some code that is like the following
class Vibration: NSObject {
var status: VibrationStatus // an enum
}
and a function on another class (of type NSObject) like the following, that is part of an object that has a property vibration of type Vibration
func vibrate() {
DispatchQueue.main.async { [weak self] in
vibration.status = .vibrating
// do some real HW vibrate stuff
}
}
None of the properties or class definitions include #objc or #objcMembers
I am trying to create a test that will wait for that async call to set the vibration.status.
I have a test function that seems to work (see below) when I declare the status property as #objc or put #objcMembers on the Vibration class.
func testVibrate() {
let invite: SignalingInviteBody = SignalingInviteBody()
let incomingCall = IncomingCall(invite)
let expectation = XCTNSPredicateExpectation(predicate: NSPredicate(format: "status = 2"), object: incomingCall.vibration)
incomingCall.startRing() // this calls the function vibrate()
wait(for: [expectation], timeout: 3.0)
}
This test also requires #objc on the enum declaration with Objective-C compatible enum declaration, which I don't want as it would only be for testing.
Except for testing, there is no need to make the status property #objc or the Vibration class as #objcMembers and I would rather not change the base program code to a more inefficient style when I don't need the Objective-C compatibility in the base program.
Is there a way to unit test this in a real, honest to goodness, Swift way?
Preface: this is just some pseudo-code I nailed out quickly in the browser. It'll probably need some polishing before it compiles properly.
I would use a mock and dependency injection:
class MockVibration: Vibration {
let statusChanged: (VibrationStatus) -> Void
init(statusChanged: (VibrationStatus) -> Void) {
self.statusChanged = statusChanged
}
var status: VibrationStatus {
didSet {
statusChanged(status)
}
}
}
I would probably have a protocol, and have Vibration and MockVibration both conform to it, but having MockVibration: Vibration should work well. Once you've defined this mock, you can use it to fulfill an expectation in your test case:
func testVibrate() {
let didVibrate = self.expectation(description: "Started vibrating")
let mockVibration = MockVibration { newStatus in
XCTAssertEqual(newStatus, .vibrating)
didVibrate.fulfill()
}
let invite = SignalingInviteBody()
let incomingCall = IncomingCall(invite, mockVibration)
incomingCall.startRing() // this calls the function vibrate()
wait(for: [didVibrate], timeout: 3.0)
}
You might even be able to rework this interface so that DispatchQueue.main.async {} happens within the Vibration class as an internal detail. If you do that, the interaction between IncomingCall and Vibration becomes synchronous, so you wouldn't need to use expectations. Your incoming call test would reduce to:
class MockVibration {
// The mock can just have its status set simply/synchronously
var status: VibrationStatus = .off // or some other "initial" value
}
func testVibrate() {
let mockVibration = MockVibration()
let invite = SignalingInviteBody()
let incomingCall = IncomingCall(invite, mockVibration)
incomingCall.startRing() // this calls the function vibrate()
XCTAssertEqual(mockVibration.status, .vibrating)
}
Of course, then you'd need a separate test that covers Vibration, and ensures that its public APIs cause it to change its internal state using the right dispatch queue or whatever.

Not getting all expected events when subscribing to sequence with TestScheduler

I'm trying to write an integration test for a Reactor in an app built with ReactorKit and Realm/RxRealm.
I'm having trouble using TestScheduler to simulate user actions and test the expected emitted states.
In a nutshell, my problem is this: I'm binding an action that will make my Reactor save an item to Realm, my Reactor also observes changes to this object in Realm, and I expect my Reactor to emit the new state of this item observed from Realm.
What I'm seeing is that my test does not get the emission of the newly saved object in time to assert its value, it's emitted after my test assertion runs.
There is a fair amount of code involved, but attempting to whittle it down into a self-contained example of what it all roughly looks like below:
struct MyObject {
var counter: Int = 0
}
class MyReactor: Reactor {
enum Action {
case load
case mutateState
}
enum Mutation {
case setObject(MyObject)
}
struct State {
var object: MyObject?
}
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .load:
return service.monitorObject().map(Mutation.setObject)
case .mutateState:
guard var myObject = currentState.object else { return .empty() }
myObject.counter += 1
return service.save(myObject).andThen(.empty())
}
}
func reduce(state: State, mutation: Mutation) -> Observable<State> {
var newState = state
switch mutation {
case let .setObject(object):
// Should be called twice in tests, once on load, once after mutateState action
newState.object = object
}
}
}
struct Service {
// There is always at least one default instance of `MyObject` in Realm.
func monitorObject() -> Observable<MyObject> {
return Observable
.collection(from: realm.objects(MyObject.self))
.map { $0.first! }
}
func save(_ object: MyObject) -> Completable {
return Completable.create { emitter in
try! realm.write {
realm.add(object, update: .modified)
}
emitter(.completed)
return Disposables.create()
}
}
}
class MyTest: QuickSpec {
var scheduler: TestScheduler!
var sut: MyReactor!
var disposeBag: DisposeBag!
var service: Service!
var config: Realm.Configuration!
override func spec() {
beforeEach {
config = Realm.Configuration(inMemoryIdentifier: UUID().uuidString)
scheduler = TestScheduler(initialClock: 0)
disposeBag = DisposeBag()
sut = MyReactor()
service = Service(realmConfig: config)
}
describe("when my reactor gets a mutateState action") {
it("should mutate state") {
scheduler.createHotObservable([
.next(1, Action.load),
.next(2, Action.mutateState),
])
.bind(to: sut.action)
.disposed(by: disposeBag)
let response = scheduler.start(created: 0, subscribed: 0, disposed: 1000) {
sut.state.map(\.object)
}
// Counter always equals 0
XCTAssertTrue(response.events.last!.value.element!!.counter == 1)
}
}
}
}
What I'm expecting to happen is my Reactor's state is set for a 2nd time, before the XCTAssertTrue is hit. What is actually happening is the assert is hit with the initially loaded state, and then, my reactor's state is set again.
I thought my problem might be related to schedulers. Something I tried was injecting the test scheduler into my Service and doing observeOn(testScheduler) on my monitorObject function. But I'm still observing the assert get hit before the reactor's state is set for the 2nd time. I'm also not sure if a nuance of RxRealm/Realm change set notifications is the cause - not sure how to verify whether that might be the case.
Hopefully the problem and question is clear. Thanks in advance for any help.
I decided attempting to write an integration test was more trouble than it was worth and probably not going to result in very useful tests anyway.
So you are trying to test to see if Realm works. I don't use Realm, but based on your description, it probably updates the object on an internal thread and then you get the emission on a subsequent cycle.
You can test it by using an XCTestExpectation. Here is documentation from Apple: https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations/testing_asynchronous_operations_with_expectations
Note however, that if something goes wrong in Realm and this test fails, there isn't anything you can do about it.

Avoid coupling when using enum in tests

Say we have this enum
enum Action: String {
case doThing
case doOtherThing
}
This enum is used this way:
func run(action: Action, block: () -> Void)
Now, I unit test the run method so I need to pass an Action this way:
func testActionRun() {
let expect = expectation(description: #function)
let sut = ActionRunner()
sut.run(action: .doThing) {
expect.fulfill()
// Assert something
}
waitForExpectations(timeout: 0.1, handler: nil)
}
As I need to test other situations on ActionRunner, I ended with a lot of .doThing spread over the whole test suite.
The problem is: if I make a change in production code and change case doThing to case doThatThing now all my test suite fails because there is no a case doThing.
The perfect thing would be to declare a dummy case in test code to allow something like
sut.run(action: .dummyAction) {
}
but enum does not allow that as it doesn't allows inheritance nor a extension to add a case.
The first option that came to my mind was to convert Action into a protocol, but that change is unnecessary in production and its only purpose is to accomplish something in test code.
So, is it there another option to achieve this?
The question of how to avoid coupling when using enums is a tricky one. I bumped into that myself a few times with no solid answer :/
One point you raise is the one of using a protocol, and that feels unnecessary in production. I sort of agree with that, but most time it's the necessary evil.
In the example you showed though I think maybe a tweak in the design might solve part of the problem.
In particular when looking at this code
func run(action: Action, block: () -> Void) {
// ...
}
func testActionRun() {
let expect = expectation(description: #function)
let sut = ActionRunner()
sut.run(action: .doThing) {
expect.fulfill()
// Assert something
}
waitForExpectations(timeout: 0.1, handler: nil)
}
What comes to mind to me is that your Action specifies a certain behaviour. That is when you test the run method passing .doThing you expect a different behaviour than when passing .doOtherThing.
If that's right, is there any reason why you need to pass the action enum instance and an action block to the run function?
You could separate the code that defines the behaviour from the one performs the actual action even more that what you've done already. For example:
protocol Actionable {
var action: () -> () { get }
}
enum Action: Actionable {
case doThing
case doOtherThing
var action {
switch self {
case .doThing: return ...
case .doOtherThing: return ...
}
}
class ActionRunner {
func run(actionable: Actionable) {
actionable.action()
}
}
func testActionRun() {
let expect = expectation(description: #function)
let sut = ActionRunner()
sut.run(actionable: FakeActionable()) {
expectation.fulfill()
}
waitForExpectations(timeout: 0.1, handler: nil)
}
class FakeActionable: Actionable {
let action = { }
}
func testDoThing() {
let sut = Action.doThing
sut.action()
// XCTAssert for the expected effect of the action
}
Note: I haven't actually compiled that code, so bear with me if it has some mistakes. It should give the idea though.
This way you have ActionRunner which only purpose is to properly run a given Actionable, and the Action enum which only purpose is to describe what different actions should do.
This example code is rather restrict in what it can do, only run () -> () actions, but you could build on top of it to achieve more advanced behaviours.
If you change your production code you have to change your test code too in order to test those new changes.
Maybe you can set the value on an Action variable in the setUp func of your XCTestCase class
import XCTest
class SharingKitTests: XCTestCase {
var theAction: Action!
override func setUp() {
super.setUp()
self.theAction = .doThing
}
}
Then you will be able to use this theAction var in all your test methods, and if you need to change the value you only need to change it in one place.

Unit test wrapper for URLSession completer

I have defined my own class with methods that fits signature of URLSession complete callback, e. g. (Data?, Response?, Error?) -> Void.
The method contains common logic for handling response, e. g. checking data, parsing it etc.
Now I would like to unit test this method. The methods contains some verification, for instance,
guard let data = data else {
//some logic
return
}
Here I would like to test that function will really be terminated. Of course it is not possible to achieve it against void return (I think so, maybe I missed something).
Another option - mark the method as throws, and then test for a specific errors. But then this method will not fit into URLSession.shared.dataTask method.
Am I paranoid about these things? Is there any possibility to achieve it?
Thanks in advance.
Usually I try to separate the query logic into several parts:
1) Router 2) API client which uses a router 3) mapping model
And all this parts you can test.
How you can test API client:
fileprivate func testPerformanceOfGetNewsFromAPI() {
let expectationTimeout: Double = 30.0
self.measure {
let expectation = self.expectation(description: "Get gifters")
NewsAPIClient.getNews(closure: { response in
expectation.fulfill()
})
self.waitForExpectations(timeout: expectationTimeout) { error in
XCTAssertNil(error)
}
}
}
This test will check. Could APIClient receive an response within 30 seconds.
How you can test mapping:
For mapping, I use JASON: https://github.com/delba/JASON
Setup your swift file:
import XCTest
import JASON
#testable import ProjectName
final class NewsTests: XCTestCase {
// MARK: - Properties
fileprivate var news: News!
// MARK: - Lyfecycles
override func setUp() {
super.setUp()
news = mockExample()
}
override func tearDown() {
news = nil
super.tearDown()
}
}
Then, create in this class your mock:
fileprivate func mockExample() -> ExampleModel? {
let data: Data
let json: JSON
do {
try data = Data(resource: "MyExampleFile.json") // Here enter your JSON example file. Target member ship for this file should be your test target
try json = JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions()) as! JSON
} catch let error {
XCTFail(error.localizedDescription)
return nil
}
let model = ExampleModel(json: json)
return model
}
Then, you can write test in this class:
fileprivate func testMapping() {
XCTAssertNotNil(news)
XCTAssertEqual(news.title, mockExample()?.title)
XCTAssertEqual(news.text, mockExample()?.text)
XCTAssertEqual(news.timeStamp, mockExample()?.timeStamp)
}
In the testing logic, you can also add image uploads (if they are present in JSON). Thus, you can check if the current model is correct for you, can process the JSON response.