XCTest for when iCloud is enabled and disabled - swift

I have a viewMode that determines if iCloud is enabled or disabled with the result being a prompt to the User to login to iCloud or not.
Is there a way to progamatically login/logout to iCloud from an XCTest to reliable test all paths?
Here is my test
func testShowLoginButtonForiCloud() {
let viewModel = OnboardingViewModel()
let expectation = XCTestExpectation(description: "Wait for CKContainer auth check")
var iCloudEnabled: Bool?
viewModel.shouldShowiCloudLogin { result, error in
iCloudEnabled = result
expectation.fulfill()
}
wait(for: [expectation], timeout: 5.0)
XCTAssertNotNil(iCloudEnabled)
XCTAssertFalse(iCloudEnabled!)
}
Here is my ViewModel
typealias Completion = (Bool, Error?) -> Void
final class OnboardingViewModel {
func shouldShowiCloudLogin(completion: #escaping Completion) {
CKContainer.default().accountStatus { (status, error) in
switch status {
case .available :
completion(true, nil)
default :
completion(false, error)
}
}
}
}

Can we programmatically log in to CloudKit for unit testing? This is inadvisable, because even if we could, the tests would be slow & fragile. Instead, treat CloudKit as an architectural boundary. Unit tests can go right up to this boundary. And we can pretend stuff comes back from the boundary. In this way, we can test all paths.
To program this boundary into your code, use a protocol. This protocol will be a slice containing only those CKContainer methods you want. (This is the Interface Segregation Principle in action.) Since CKContainer already implements this method, we can attach it as an empty extension.
protocol CKContainerProtocol {
func accountStatus(completionHandler: #escaping (CKAccountStatus, Error?) -> Void)
}
extension CKContainer: CKContainerProtocol {}
Then add a property to your view model:
var cloudKitContainer: CKContainerProtocol = CKContainer.default()
The default value means your code will continue to use the real CKContainer unless told otherwise. Change your code to call cloudKitContainer instead of CKContainer.default().
Then in test code, you can provide a different implementation of CKContainerProtocol. This will let you do stubbing and mocking. You can confirm that accountStatus() is called exactly once. And you can exercise its closure with different CKAccountStatus values to confirm how your Completion closure is called.

Related

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.

Swift: Thread safe Singleton, why do we use sync for read?

While making a thread-safe Singleton, it is advised to use a sync for read and an async with a barrier for write operation.
My question is why do we use a sync for read? What might happen if we perform a read with async operation?
Here is an example of what is recommended:
func getUser(id: String) throws -> User {
var user: User!
try concurrentQueue.sync {
user = try storage.getUser(id)
}
return user
}
func setUser(_ user: User, completion: (Result<()>) -> Void) {
try concurrentQueue.async(flags: .barrier) {
do {
try storage.setUser(user)
completion(.value(())
} catch {
completion(.error(error))
}
}
}
The concept of using concurrent queue with “read concurrently with sync; write with barrier with async” is a very common synchronization pattern called “reader-writer”. The idea is that the concurrent queue is just for synchronizing writes with a barrier, but that reads will take place concurrently with respect to other reads.
So, here’s a simple, real-world example of using reader-writer for synchronized access to some private state property:
enum State {
case notStarted
case running
case complete
}
class ComplexProcessor {
private var readerWriterQueue = DispatchQueue(label: "...", attributes: .concurrent)
// private backing stored property
private var _state: State = .notStarted
// exposed computed property synchronizes access using reader-writer pattern
var state: State {
get { readerWriterQueue.sync { _state } }
set { readerWriterQueue.async { self._state = newValue } }
}
func start() {
state = .running
DispatchQueue.global().async {
// do something complicated here
self.state = .complete
}
}
}
Consider:
let processor = ComplexProcessor()
processor.start()
And then, later:
if processor.state == .complete {
...
}
The state computed property is using the reader-writer pattern to offer thread-safe access to the underlying stored property. It synchronizes access to some memory location, and we are confident that it will be responsive. In this case, we don’t need confusing #escaping closures: The sync reads result in very simple code that is easy to reason about.
That having been said, in your example, you’re not just synchronizing interaction with some property, but synchronizing the interaction with storage. If that’s local storage that is guaranteed to be responsive, then the reader-writer pattern is probably fine.
But if storage methods could take anything more than a few milliseconds to run, you wouldn’t want to use the reader-writer pattern. The fact that getUser can throw errors makes me wonder if storage is already doing complicated processing. And even if it is just reading quickly from some local store, what if it was later refactored to interact with some remote store, subject to unknown network latency/issues? Bottom line, it is questionable to have the getUser method making assumptions about implementation details of storage, assuming that the value will always be returned quickly.
In that case, you would refactor getUser method to use #escaping completion handler closure, as suggested by Jeffery Thomas. We never want to have a synchronous method that might take more than a few milliseconds, because we never want to block the calling thread (especially if it’s the main thread).
By the way, if you stay with reader-writer pattern, you can simplify your getUser, because sync returns whatever value its closure returns:
func getUser(id: String) throws -> User {
return try concurrentQueue.sync {
try storage.getUser(id)
}
}
And you can’t use try in conjunction with async (only within your do-catch block). So it’s just:
func setUser(_ user: User, completion: (Result<()>) -> Void) {
concurrentQueue.async(flags: .barrier) {
do {
try storage.setUser(user)
completion(.value(())
} catch {
completion(.error(error))
}
}
}
It's all in what you want. By changing get user to async, then you need to use a callback to wait for the value.
func getUser(id: String, completion: #escaping (Result<User>) -> Void) -> Void {
concurrentQueue.async {
do {
let user = try storage.getUser(id)
completion(.value(user))
} catch {
completion(.error(error))
}
}
}
func setUser(_ user: User, completion: #escaping (Result<()>) -> Void) {
concurrentQueue.async(flags: .barrier) {
do {
try storage.setUser(user)
completion(.value(()))
} catch {
completion(.error(error))
}
}
}
That changes the API of get user, so now when calling get user, a callback will need to be used.
Instead of somethings like this
do {
let user = try manager.getUser(id: "test")
updateUI(user: user)
} catch {
handleError(error)
}
you will need something like this
manager.getUser(id: "test") { [weak self] result in
switch result {
case .value(let user): self?.updateUI(user: user)
case .error(let error): self?.handleError(error)
}
}
Assuming you have somethings like a view controller with a property named manager and methods updateUI() and handleError()

How can I verify a class method is called using XCTAssert?

I have a service class, I would like to assert 2 things
A method is called
The correct params are passed to that method
Here is my class
protocol OAuthServiceProtocol {
func initAuthCodeFlow() -> Void
func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void
}
class OAuthService: OAuthServiceProtocol {
fileprivate let apiClient: APIClient
init(apiClient: APIClient) {
self.apiClient = apiClient
}
func initAuthCodeFlow() -> Void {
}
func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void {
}
}
Here are my tests
class OAuthServiceTests: XCTestCase {
var mockAPIClient: APIClient!
var mockURLSession: MockURLSession!
var sut: OAuthService!
override func setUp() {
mockAPIClient = APIClient()
mockAPIClient.session = MockURLSession(data: nil, urlResponse: nil, error: nil)
sut = OAuthService(apiClient: mockAPIClient)
}
func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")
class OAuthServiceMock: OAuthService {
override func initAuthCodeFlow() -> Void {
}
override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
renderOAuthWebViewExpectation.fulfill()
}
}
}
}
I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.
I believe this should satisfy point 1. However I cannot access my expectation when attempting to assign it as fulfilled as I get the following error
Class declaration cannot close over value
'renderOAuthWebViewExpectation' defined in outer scope
How can I mark this as fulfilled?
I am following a TDD approach, so I understand my OAuthService would produce a failing test at this point anyway*
I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.
I would strongly discourage you from using this approach. If your SUT is an instance of the subclass then your test is not truly testing OAuthService, but OAuthService mock.
Moreover, if we think of tests as a tool to:
prevent bugs when code is change
help refactoring and maintenance of the code
then I would argue that testing that calling a certain function calls another function is not a good test. That's harsh, I know, so let me unpack why that's the case.
The only thing it's testing is that initAuthCodeFlow() calls renderOAuthWebView(forService:, queryitems:) under the hood. It doesn't have any assertion on the actual behaviour of the system under test, on the outputs it produces directly or not. If I were to edit the implementation of renderOAuthWebView(forService:, queryitems:) and add some code that would crash at runtime this test would not fail.
A test like this doesn't help with keeping the codebase easy to change, because if you want to change the implementation of OAuthService, maybe by adding a parameter to renderOAuthWebView(forService:, queryitems:) or by renaming queryitems into queryItems to match the capitalization, you'll have to update both the production code and the test. In other words, the test will get in your way of refactoring -changing how the code looks without changing how it behaves- without any extra benefit.
So, how should one test OAuthService in a way that prevents bugs and helps moving fast? The trick is all in testing the behaviour instead of the implementation.
What should OAuthService do? initAuthCodeFlow() doesn't return any value, so we can check for direct outputs, but we can still check indirect outputs, side effects.
I'm making a guess here, but I from your test checking that renderOAuthWebView(forService:, queryitems:) I'd and the fact that it gets an APIClient type as input I'd say it'll present some kind of web view for a certain URL, and then make another request to the given APIClient maybe with the OAuth token received from the web view?
A way to test the interaction with APIClient is to make an assertion for the expected endpoint to be called. You can do it with a tool like OHHTTPStubs or with your a custom test double for URLSession that records the requests it gets and allows you to check them.
As for the presentation of the web view, you can use the delegate patter for it, and set a test double conforming to the delegate protocol which records whether it's called or not. Or you could test at a higher level and inspect the UIWindow in which the test are running to see if the root view controller is the one with the web view.
At the end of the day is all a matter of trade offs. The approach you've taken is not wrong, it just optimizes more towards asserting the code implementation rather than its behaviour. I hope that with this answer I showed a different kind of optimization, one biased towards the behaviour. In my experience this style of testing proves more helpful in the medium-long run.
Create a property on your mock, mutating it's value within the method you expect to call. You can then use your XCTAssertEqual to check that prop has been updated.
func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")
class OAuthServiceMock: OAuthService {
var renderOAuthWebViewExpectation: XCTestExpectation!
var didCallRenderOAuthWebView = false
override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
didCallRenderOAuthWebView = true
renderOAuthWebViewExpectation.fulfill()
}
}
let sut = OAuthServiceMock(apiClient: mockAPIClient)
XCTAssertEqual(sut.didCallRenderOAuthWebView, false)
sut.renderOAuthWebViewExpectation = renderOAuthWebViewExpectation
sut.initAuthCodeFlow()
waitForExpectations(timeout: 1) { _ in
XCTAssertEqual(sut.didCallRenderOAuthWebView, true)
}
}

generically injecting a side effect in a SignalProducer

My app has a status area at the top that shows progress information (similar to Xcode and iTunes). I want to update it by injecting side effects into an event stream, using a closure that converts the stream's value into the ProgressUpdate value. I'm using an extension on SignalProducer so any signal producer in my app can update the app's status area (there is a lot more involved to allow for multiple signals at once, but that doesn't affect this problem).
I'm basing it on SignalProducer's on(starting:, started:, ...). It requires the latest swift 3.1 beta to allow the constraint on the error type, but this is straight from a playground.
import ReactiveSwift
struct Rc2Error: Error {
}
struct ProgressUpdate {
let message: String
let value: Double = -1
}
class MacAppStatus {
fileprivate func process(event: (Event<ProgressUpdate, Rc2Error>) -> Void)
{
//update UI based on the event
}
}
extension SignalProducer where Error == Rc2Error {
func updateProgress<Value>(status: MacAppStatus, converter: #escaping (Value) -> ProgressUpdate) -> SignalProducer<Value, Error>
{
return SignalProducer<Value, Error> { observer, compositeDisposable in
self.startWithSignal { signal, disposable in
compositeDisposable += disposable
compositeDisposable += signal
.on(event: { (orignal) in
switch original {
case .completed:
status.process(Event<ProgressUpdate, Rc2Error>.completed)
case .interrupted:
status.process(Event<ProgressUpdate, Rc2Error>.interrupted)
case .failed(let err):
status.process(Event<ProgressUpdate, Rc2Error>.failed(err))
case .value(let val):
status.process(Event<ProgressUpdate, Rc2Error>.value(converter(val)))
}
})
.observe(observer)
}
}
}
}
```
The last line of .observe(observer) produces an error:
error: cannot convert value of type 'Observer<Value, Rc2Error>' to expected argument type 'Observer<_, Rc2Error>'
Any ideas why this conversion fails? Suggestions on a different way to accomplish this?
It looks like it was just bad error reporting from the compiler. The actual problem was that process() should take an Event, not a closure that takes an event. It also needed an empty external parameter name.
Changing the signature to
fileprivate func process(_ event: Event<ProgressUpdate, Rc2Error>)
and fixing the original typo Mike Taverne pointed out fixed it.

PromiseKit: delegate-system wrappers seem to return immediately when not used at the beginning of the chain

I am fairly new to PromiseKit and I have been trying for a couple of days to figure out a solution for an unexpected behaviour of promise-wrapped delegate systems (UIALertView+PromiseKit, PMKLocationManager etc..).
In my fairly typical scenario of an app's Setup process, I am trying to chain up a series of operations that the user has to go through when the app loads.
For the sake of this example, let's restrict the case to only two steps:
logging the user into a Restful system followed by presenting an alertView and waiting for user's interaction.
Below is my code, where:
LoginToService is a promise-able version of a block-based method obtained via extending MCUser with PromiseKit. This works as expected and returns once the user had logged in, or fails with an error.
In the 'then' clause of the successful login, I present an alertView by returning its promised version via alert.promise().
I would expect that promise to be fulfilled before the successive .then clause (and in the end the 'finally' clause) gets called - the alert's promise should be fulfilled when the the user clicks on button to dismiss it, as per implementation of PromiseKit's delegate system wrappers: this works just fine the observed behaviour when I use alert.promise().then to start the chain of Promises -
// Doesn't work: alert.promise returns immediately
let user = MCUser.sharedInstance()
user.logInToService(.RestServiceOne, delegate: self).then { _ -> AnyPromise in
MCLogInfo("LogInToService: Promise fulfilled")
let alert = UIAlertView(title: "Title", message: "Msg", delegate: nil, cancelButtonTitle: "Cancel", otherButtonTitles: "Hello")
return alert.promise()
}.then { (obj:AnyObject) -> Void in
print("Clicked")
}.finally {
print("Finally")
}.catch_ { error in
print("Error")
}
What I observe is that the chain continues immediately without waiting for the user to click, with the 'Clicked' and 'Finally' messages being printed to console and the alert on screen waiting for an action. Am I obviously missing something or ar those delegate-system wrappers not meant to be used if not at the beginning of the Promise chain?
Thanks in advance for any hint
It should work as you expect. You may check whether the returned promise is incorrectly completed.
What makes me gripes, though, is that alert.promise() should return a Promise<Int> - but the closure is explicitly typed to return a AnyPromise. So, your code should not compile.
I setup a test project myself, and indeed, the compiler complains. I used PromiseKit v3.x. Your's is likely an older version (finally and catch are deprecated).
After fixing the return type of the closure to Promise<Int>, the code compiles. But the important fact is, that the behaviour is as you described and experienced in your code - and not as it should, IMHO. So, there seems to be a bug.
Edit:
OK, it turned out that there is an issue with "overload resolution" and "type inference". Given your code in your OP, the Swift compiler resolves to an unexpected overload of the then method:
expected:
func then<U>(on q: dispatch_queue_t = dispatch_get_main_queue(), _ body: (T) throws -> Promise<U>) -> Promise<U>
actual:
func then<U>(on q: dispatch_queue_t = dispatch_get_main_queue(), _ body: (T) throws -> U) -> Promise<U>
This is caused by the succeeding finally and catch methods.
In order to solve it here in this scenario, you should either correctly fully specify the type of the closure, or let the compiler figure it out themselves by not specifying the type. I finally got this, which works as expected using PromiseKit v3.x with Swift:
import UIKit
import PromiseKit
// helper
func fooAsync() -> Promise<String> {
return Promise { fulfill, reject in
let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(1.0 * Double(NSEC_PER_SEC)))
dispatch_after(delay, dispatch_get_global_queue(0,0)) {
fulfill("OK")
}
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
fooAsync()
.then { str in
print("String: \(str)")
let alert = UIAlertView(title: "Title", message: "Msg", delegate: nil, cancelButtonTitle: "Cancel", otherButtonTitles: "Hello")
let promise: Promise<Int> = alert.promise()
return promise
}.then { (n: Int) -> Void in // notice: closure type specified!
print("Clicked")
}.ensure {
print("Finally")
}.report { error in
print("Error")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This above code may not solve it in your case, since you are using a different PromiseKit library. I would recommend to use the newest Swift version.
Nonetheless, there seem to be some subtle pitfalls here with PromiseKit. Hope you can now solve your issue.