Unit Testing asynchronous functions called by various didSets in Swift - swift

I'm trying to unit test a property in a ViewModel (in Swift) that is dependent on a series of other properties being set, and having trouble doing so.
I have a viewModel which includes a timeLength, a timePeriod, and a listOfObjects that fits my time period.
These values depend on the previous one,
timeLength and timePeriod are both stored in the user's profile, and my code follows this flow:
Search for a timeLength in the profile.
If none is found, allow the user to select a timeLength
Once the timeLength is set, a didSet triggers a flow that searches for a matching timePeriod, (in a mock repository for time periods) or creates one if one doesn't exist.
Once the timePeriod exists, a didSet triggers a flow that searches a database to compile a listOfObjects that match that timePeriod (using a separate mockRepository for objects).
I'm trying to create a unit test that checks the list of objects once that code flow is completed, but every time I do so, the XCTAssertEqual method completes before the code flow is finished. I tried using XCTestExpectation, as described in Hacking with Swift, but I don't have a specific asynchronous method to call, since this is all triggered by a series of didSet calls.
I could create a timePeriod in the repository, which would trigger the listOfObjects to be set in the ViewModel, but then I'd be missing out on the full flow of this. Is there a way to just have the test complete after a few seconds?
(Or is this a bad test since I'm relying on the system to do multiple things at once in order to pass the test?)
Here's the specific test (right now it won't do anything since there's nothing to fulfill the asynchronous wait)
func testSeasonObjectsVM_loadData() {
// given
let exp = expectation(description: "loading Data")
sut.timeLength = .quarter
waitForExpectations(timeout: 20)
// when
let count = sut.listOfObjects.count
// assert
XCTAssertEqual(count, 2)
}

You can fire a notification from the final place that you know the process / series of didSets has been completed like following.
public extension Notification.Name {
static let UserProfileViewModelListOfObjectsLoaded =
Notification.Name(rawValue: "UserProfileViewModelListOfObjectsLoaded")
}
// Your viewModel's final didSet
var listOfObjects: [Any] {
didSet {
NotificationCenter.default.post(name: .UserProfileViewModelListOfObjectsLoaded, object: nil)
}
}
// In your test
self.expectation(forNotification: .UserProfileViewModelListOfObjectsLoaded, object: nil) { (notification) -> Bool in
return (sut.listOfObjects.count == 2)
}

Related

`sleep(_:)` vs `asyncAfter(deadline:execute:)`

I have a value type for which a relatively long calculation is require to produce it (> 1s). I wrap this value type in an enumeration that expresses whether it is currently being calculated or is available:
enum Calculatable<T> {
case calculated(T), calculating
}
The problem is this value is persisted. This means after the long running calculation, when I have the updated value, I try to persist it first before changing the property visible to the business logic. If persistence succeeds, the property is updated, if it fails, I want to wait and then try to persist again; with a catch — if any other values have changed that the calculated one depends on, we throw away the previously calculated value — stop trying to persist it — and submit a new Calculation operation to the queue — starting all over again. My initial attempt at that looked something like this:
/// Wraps a value that takes a long time to be calculated.
public class CalculatedValueWrapper<T> {
/// The last submitted `Calculation` to the queue.
private weak var latestCalculation: Optional<Operation>
/// The long running calculation that produces the up to date, wrapped value.
private let longCalculation: (_ cancelIf: () -> Bool) -> T
/// Calculation queue.
private let queue: OperationQueue
/// The calculated value.
private var wrappedValue: Calculatable<T>
/// Update the wrapped value.
public func update() {
// Don't set if already being calculated.
if case .calculated(_)=self.wrappedValue { self.wrappedValue = .calculating }
// Initiate new calculation.
self.latestCalculation?.cancel()
self.latestCalculation={
let calc: Calculation = .init(self, self.longCalculation)
self.queue.addOperation(calc)
return calc
}()
}
}
/// Executes a long running calculation and persists the result once complete.
class Calculation<T>: Operation {
/// The calculation.
private let calculation: (_ cancelIf: () -> Bool) -> T
/// The owner of the wrapped value we're calculating.
private weak var owner: Optional<CalculatedValueWrapper<T>>
func main() {
let result=self.calculation(cancelIf: { [unowned self] in self.isCancelled })
let persist: () -> Bool={
// In case the persistence fails.
var didPersist=false
// Persist the result on the main thread.
DispatchQueue.main.sync { [unowned self] in
// The owner may have been deallocated between the time this dispatch item was submitted and the time it began executing.
guard let owner=self.owner else { self.cancel(); return }
// May have been cancelled.
guard !self.isCancelled else { return }
// Attempt to persist the calculated result.
if let _=try? owner.persist(result) {
didPersist=true
}
}
// Done.
return didPersist
}
// Persist the new result. If it fails, and we're not cancelled, keep trying until it succeeds.
while !self.isCancelled && !self.persist() {
usleep(500_000)
}
}
}
I would have stuck with this, but after further research I noticed a prevailing sentiment that it was bad practice to sleep the thread in DispatchQueue items and Operations. The alternative that seems to be considered better, in the case of DispatchQueue for example, was to initiate another DispatchQueue item using asyncAfter(deadline:execute:).
That solution appears more complicated in my case as being able to cancel a single operation that encapsulates everything that needs to be done makes cancelling easy. I can hold a reference to the last Calculation operation executed, and if another one is submitted on the main thread while one is in progress, I cancel the old one, add the new one, and with that I know the old value won't be persisted because it is done on the main thread after cancellation has already been performed; and a Calculation must not be cancelled in order for it to persist its result.
Is there something about DispatchQueues or OperationQueues that would make the alternative solution more straightforward to implement in this case? Or is sleeping here totally fine?
Make a RetryPersist NSOperation. Just before adding it to the queue, set its isReady to false. Have it do a dispatchAfter weakly capturing itself to set its isReady to true. Now you can cancel any RetryPersist operations in the queue, if the count of them where not ready, not cancelled is not zero you know there’s one waiting to go etc.

XCTest - Unable to understand / implement expectations in unit tests (to test aysnc code)

(NOTE - I'm developing for macOS, so please ... iOS-specific advice won't help me)
What I'm trying to do:
I have an app component that performs a short task on a background thread, and then, if certain conditions are met, asynchronously sends out a notification on the main thread.
NOTE - I am not using NSNotification in my app code. I am using my own custom notification mechanism. So, any solution related to NSNotification is not applicable to me.
I'm writing a unit test for the above mentioned app component, and simply want to check if that notification was indeed sent or not. My test has to be able to wait a second or so to give the notification time to reach its subscriber/observer, before performing an assertion.
I want to be able to test both possible cases in my tests: Both are normal scenarios.
Notification was sent.
Notification was not sent.
After hours of reading several docs and code samples, I don't understand how to achieve this with expectations.
I just want to wait one second in my test. Is it really this complicated ?
sleep() doesn't work
DispatchQueue.main.asyncAfter(time) doesn't work
Timer doesn't work
Here's the app component that needs to be tested, and its unit test:
In the below code, where do I put expectation.fulfill() ???
class ComponentBeingTested {
func methodBeingTested() {
doSomeWork()
if certainConditionsAreMet {
DispatchQueue.main.async {sendOutNotification()}
}
}
}
...
class UnitTestForComponentBeingTested: XCTestCase {
let objectBeingTested = ComponentBeingTested()
func testMethodBeingTested() {
let expectation = self.expectation(description: "Notification was sent")
// Call the code being tested
objectBeingTested.methodBeingTested()
// How do I do this with expectations ??? Where does expectation.fulfill() go ?
waitForOneSecond()
XCTAssertTrue(notificationSent) // Assume the value of notificationSent is available
}
}
Here is an approach
func testMethodBeingTested() {
// create expectation
let expectation = self.expectation(description: "Notification was sent")
// set expectation condition
var notificationSent = false
let observer = NotificationCenter.default
.addObserver(forName: _Your_Notification_Name, object: nil, queue: nil) { _ in
notificationSent = true
expectation.fulfill()
}
// Call the code being tested
objectBeingTested.methodBeingTested()
// wait for expectation
self.wait(for: [expectation], timeout: 5)
XCTAssertTrue(notificationSent)
}
Check out XCTNSNotificationExpectation, which becomes fulfilled when a matching notification is posted. Different initializers are available, depending on how restrictive you want to be on the fulfilment of the expectation.
To check that the notification is not sent, set isInverted to true on the expectation object.
Then just add a call to waitForExpectations(timeout:handler:) at the end of your test.
Ok, after a lot of trial and error, this works great for me:
Description: I basically created a helper function in my test case class that contains all the boilerplate expectation/wait code. It does the following:
1 - Creates an expectation (i.e. XCTestExpectation) as a formality.
2 - Calls my (arbitrary) test case assertion code (passed in as a closure) on some global queue thread after the intended delay period. Once this assertion code has completed, the expectation is fulfilled (again, a formality).
3 - Waits on the expectation by calling XCTestCase.wait(timeout). This ensures that the main thread / run loop is kept alive while my assertion code completes on this other thread.
Then, in my test case, I simply invoke that helper function, providing it with a wait period and some code to execute (i.e. my assertions).
This way, I have a simple and expressive reusable function that hides all the excessive ugliness of expectations which I never thought necessary in the first place.
I can put this helper in a base class like MyAppTestCase: XCTestCase, so that it is available to all my test case classes.
NOTE - This solution can be enhanced and made even more generic/reusable, but as of now, this is quite sufficient for the purposes of the originally posted problem.
Solution:
class ComponentBeingTested {
func methodBeingTested() {
doSomeWork()
if certainConditionsAreMet {
DispatchQueue.main.async {sendOutNotification()}
}
}
}
...
class UnitTestForComponentBeingTested: XCTestCase {
let objectBeingTested = ComponentBeingTested()
// Helper function that uses expectation/wait to execute arbitrary
// test code (passed in as a closure) after some delay period.
func executeAfter(_ timeSeconds: Double, _ work: (#escaping () -> Void)) {
let theExpectation = expectation(description: "some expectation")
// Execute work() after timeSeconds seconds
DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + timeSeconds) {
// The call to work() will execute my test assertions
work()
// As a formality, fulfill the expectation
theExpectation.fulfill()
}
// Wait for (timeSeconds + 1) seconds to give the work() call
// some time to perform the assertions
wait(for: [theExpectation], timeout: timeSeconds + 1)
}
func testMethodBeingTested() {
// Call the code being tested
objectBeingTested.methodBeingTested()
// Call the helper function above, to do the waiting before executing
// the assertions
executeAfter(0.5) {
// Assume the value of notificationSent is computed elsewhere
// and is available to assert at this point
XCTAssertTrue(notificationSent)
}
}
}

Testing a class which preserves its state in private variables

I am writing unit tests for my class. This class preserves its state in some private variables (which I don't want to expose publicly). So the scenario is:
If I call a method, the first time it will keep that state in private properties and call a delegate method with some result.
When I call the same method a second time, the output will be different on the basis of the previous input.
I want to cover all the cases in my tests.
One easy way is to change my private properties to public so that I can mock the previous input in unit test.
The other way is to call the same method with different inputs in the same test twice. Where the first call will keep the state and the next call will be the actual test.
But both these ways seem awkward to me, and I am not sure of the best one.
What is the best way to write unit test for this class?
protocol ZoneUpdateDetectorOutput: class {
func updateZoneState(_ state: ZoneState)
}
class ZoneUpdateDetector {
var zoneChangeTimer: TimerProtocol?
weak var delegate: ZoneUpdateDetectorOutput?
private var previousZoneState: ZoneState?
private var expectedZoneState: ZoneState?
private func updateZoneState() {
// If `expectedZoneState` is not equal to `previousZoneState` then `delegate` will be called
// Otherwise it will just skip
if expectedZoneState != previousZoneState {
delegate?.updateZoneState(expectedZoneState!)
previousZoneState = expectedZoneState
}
}
private func runNotifyZoneStateTimer() {
guard zoneChangeTimer?.isValid() == false else {
return
}
zoneChangeTimer?.start(timeInterval: 5,
onFire: { [weak self] in
guard let strongSelf = self else {
return
}
// On timer fire, it will try to update the state
strongSelf.updateZoneState()
})
}
// When zone changes, this method is invoked
// I basically want to test this method
func zoneStateChanged(_ state: ZoneState) {
expectedZoneState = state
if state != .inZone {
runNotifyZoneStateTimer()
} else {
zoneChangeTimer?.stop()
}
}
}
You should never be testing internal state; you should only test externally (publically) visible behaviour. That way, you can change implementation details of your class without breaking any contracts, and thus without breaking any tests.
So the second option is the preferred one.
After researching and discussing with some experts, I come up with the solution that if we want to test a class which preserve it's state then the functionality which is preserving the state should go under a separate class. Which will serve the same purpose as setting the variables as private. So, ZoneUpdateDetector should have a dependency for example: ZoneUpdateStatePreserver and it should keep the state which was previously inside ZoneUpdateDetector

How to test a function that gets into the main thread in Swift with RxSwift and XCTest?

I came across this problem when testing my View:
In my ViewModel I call to an asynchronous operation and when the response arrives, I use a PublishSubject to produce a change in my View. In my View, I call DispatchQueue.main.async in order to hide or show a button.
ViewModel
let refreshButtons = PublishSubject<Bool>(true)
refreshButtons.onNext(true)
View
model.refreshButtons.asObservable()
.subscribe(onNext: {
[unowned self] success in
self.updateButtons(success)
})
.addDisposableTo(disposable)
private func updateButtons(_ show:Bool) {
DispatchQueue.main.async{
button.isHidden = !show
}
}
Now I don't know how to unit test that refreshButtons.onNext(true) will hide or show my button.
The solutions I can think of are:
Overriding the method and having an async expectation, but for that I need to make the method public, what I don't want, or
Dispatching the main queue in my ViewModel and not in the view, what it sounds odd to me, but might me ok.
How can I solve this?
Thank you in advance.
You could use an async expectation based on a predicate in your unit test to wait an see if the button is not hidden anymore.
func testButtonIsHidden() {
// Setup your objects
let view = ...
let viewModel = ...
// Define an NSPredicate to test your expectation
let predicate = NSPredicate(block: { input, _ in
guard let _view = input as? MyView else { return false }
return _view.button.isHidden == true
})
// Create an expectation that will periodically evaluate the predicate
// to decided whether it's fulfilled or not
_ = expectation(for: predicate, evaluatedWith: view, handler: .none)
// Call the method that should generate the behaviour you are expecting.
viewModel.methodThatShouldResultInButtonBeingHidden()
// Wait for the
waitForExpectationsWithTimeout(1) { error in
if let error = error {
XCTFail("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
Something worth noting is that the value you pass to the NSPredicate should be a class. That is because classes are passed by reference, so value inside the predicate block will be the same as the one touched by your view model. If you were to pass a struct or enum though, which are passed by copy, the predicate block would receive a copy of the value as it is at the time of running the setup code, and it will always fail.
If instead you prefer to use UI tests as suggested by #Randall Wang in his answer, then this post might be useful for you: "How to test UI changes in Xcode 7". Full disclosure, I wrote that post.
First of all, You don't need test private method
If you want to test if the button is hidden or not,try UI testing
here is the WWDC of UI testing.
https://developer.apple.com/videos/play/wwdc2015/406/

Returning data after async task

I am uploading an image with using a library. This library is working async.
My function:
func upload() -> String {
let imageData:NSData = UIImageJPEGRepresentation(pureImage!, 100)!
var picture=""
SRWebClient.POST("http://domain.com/upload.php")
.data(imageData, fieldName:"image_field", data: ["username":"test","key":"test"])
.send({(response:AnyObject!, status:Int) -> Void in
if status == 200 {
let responseJSON = response! as! Dictionary<String, AnyObject>
let s_status=responseJSON["status"] as! Int
if s_status == 1 {
picture=responseJSON["picture"] as! String
print(picture)
}
}
},failure:{(error:NSError!) -> Void in
picture=""
})
return picture
}
As you can see, I have to return picture name. But now it is always returning empty string because upload process is async. How can I return the picture name after upload process?
Obviously you cannot return the picture name as function result, not unless you want to wait till the async task is done and waiting would make it a synchronous task again.
There are three very common ways to make async tasks deliver results:
Pass the task a callback (either a callback function or a completion block if you need to capture state or references). Once the task is done, it calls the callback. In your case, the callback could get the image name as argument and the callback code then needs to decide what to do with it.
If the task is encapsulated in an object, allow the object to have a delegate. Once the task is done, a delegate method is called. Either the method gets the image name as argument or can query the image name from the object it is delegate of (usually you'd pass the object itself as an argument to the delegate, that is common practice and good coding style according to Apple).
Send a notification that an image was uploaded. The image name can be the object of the notification; or some object that encapsulates the image name and possibly other properties. Whoever is interested to know when an upload task completed can register for that notification.
Some notes regarding the options above:
I'd use notifications with care. While they are easy to use and very useful if a lot of components spread across a huge project need to be informed about events, they are hard to debug (you cannot follow the code flow easily in a debugger) and they create a very lose coupling (which may or may not be desirable), yet a strong coupling to the notification itself. Also notifications cannot return a value in case that is every required.
A delegate is always a great option, but it forces users to create a class that implements the delegate protocol. This usually only pays off if you need more than just a single callback method or when you plan to call the delegate methods very frequently. Delegates are great for unit testing.
A callback is like a tiny delegate with just a single callback method. If you commonly make "fire and forget" tasks on the go and there is only a single callback required that will be called in case of success and in case of failure; and it will only be called once and there is no need to ever recycle it, then a callback is often preferable to a delegate. It has all the advantages of a delegate but it is more lightweight.
This is the sort of problem that Promises were designed for. You could implement callbacks but it quickly becomes unmanageable if you have more than a few of them to deal with.
Do yourself a big favor and import PromiseKit into you code. Take the half-hour to learn how to use it.
You will end up with something like
func upload() -> Promise<String>
you can use blocks to get a call back
func upload(completionHandler : (pictureName : NSString?)-> Void){
let imageData:NSData = UIImageJPEGRepresentation(pureImage!, 100)!
var picture=""
SRWebClient.POST("http://domain.com/upload.php")
.data(imageData, fieldName:"image_field", data: ["username":"test","key":"test"])
.send({(response:AnyObject!, status:Int) -> Void in
if status == 200 {
let responseJSON = response! as! Dictionary<String, AnyObject>
let s_status=responseJSON["status"] as! Int
if s_status == 1 {
picture=responseJSON["picture"] as! String
print(picture)
completionHandler(pictureName: picture)
}
}
},failure:{(error:NSError!) -> Void in
picture=""
completionHandler(pictureName: nil)
})
}