Write Mockup Classes For Unit Testing Of UserDefaults, Core Data & UrlSessions In Swift 3 & XCode 8

URLSession

Source Code

public protocol CLSession {
func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
}

extension URLSession: CLSession { }

public final class URLSessionMock: CLSession {

var url: URL?
var request: URLRequest?
private let dataTaskMock: URLSessionDataTaskMock

public convenience init?(jsonDict: [String: Any], response: URLResponse? = nil, error: Error? = nil) {
guard let data = try? JSONSerialization.data(withJSONObject: jsonDict, options: []) else { return nil }
self.init(data: data, response: response, error: error)
}

public init(data: Data? = nil, response: URLResponse? = nil, error: Error? = nil) {
dataTaskMock = URLSessionDataTaskMock()
dataTaskMock.taskResponse = (data, response, error)
}

public func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
self.url = url
self.dataTaskMock.completionHandler = completionHandler
return self.dataTaskMock
}

public func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
self.request = request
self.dataTaskMock.completionHandler = completionHandler
return self.dataTaskMock
}

final private class URLSessionDataTaskMock : URLSessionDataTask {

typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void
var completionHandler: CompletionHandler?
var taskResponse: (Data?, URLResponse?, Error?)?

override func resume() {
DispatchQueue.main.async {
self.completionHandler?(self.taskResponse?.0, self.taskResponse?.1, self.taskResponse?.2)
}
}
}

}

Images
Use

UserDefaults

Source Code

class MockUserDefaults: UserDefaults {
var loggedInUser = 0
override func set(_ value: Any?, forKey defaultName: String) {
if defaultName == “loggedInUser” {
loggedInUser += 1
}
}
}

Images

Use

CoreData

Source Code

// MARK: – Variables
//Core Data variables
var storeCoordinator: NSPersistentStoreCoordinator!
var managedObjectContext: NSManagedObjectContext!
var managedObjectModel: NSManagedObjectModel!
var store: NSPersistentStore!

//Managers
var apiManager: APIManager!
var dataManager: DataManager!

// MARK: – XCTest Methods

override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
//Set Managers
apiManager = APIManager.shared
dataManager = DataManager.shared

/* Core Data Mock Object Configuration */
// ——– Start ——-
managedObjectModel = NSManagedObjectModel.mergedModel(from: nil)
storeCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)

do {
store = try storeCoordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: nil)
} catch {
print(error.localizedDescription)
}

managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = storeCoordinator
apiManager.context = managedObjectContext
dataManager.context = managedObjectContext
(UIApplication.shared.delegate as! AppDelegate).mockContext = managedObjectContext
// ——– End ——-
}

override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
managedObjectContext = nil
apiManager = nil
dataManager = nil
(UIApplication.shared.delegate as! AppDelegate).mockContext = nil

//Check Store Removal
do {
try storeCoordinator.remove(store)
} catch {
XCTFail(“couldn’t remove persistant store: \(error)”)
}

super.tearDown()
}

func testThatStoreIsSetUp() {
XCTAssertNotNil(store, “no persitant store”)
}

Images

Use

Advertisements
Write Mockup Classes For Unit Testing Of UserDefaults, Core Data & UrlSessions In Swift 3 & XCode 8

Five Important Aspects Which Should Be Considered While Writing Swift Code

1. Forced Unwrapping and Downcasting

Once you get used to optionals, you come to appreciate their value. Not only are optionals making your code safer, they also make your code more readable. An optional carries a message that says “I may not have a value. Be careful.

It doesn’t matter how certain you are, an optional should be treated with caution. Forced unwrapping or forced downcasting is asking for trouble. There are situations in which you can use the ! operator (outlets, for example), but do not force unwrap an optional to avoid an if statement or a few extra lines of code.

The if statement tells the developer what we expect, but, at the same time, it hints that we may not get what we expected.

If you find yourself using a ! instead of an ?, then stop for a moment and ask yourself whether there is a safer alternative.

2. Monster Classes

Some of us apply the MVC (Model-View-Controller) pattern a bit too strictly. It’s fine to create classes that are not models, views, or controllers. Really.

Some developers even argue that you should create fat models and skinny controllers. While that may be a bit too much responsibility for the model layer, I agree that controllers should be lean and lightweight. Of course, it’s easy to stuff business logic in controllers and it takes a bit of extra work to refactor code that can belong in a separate class. But it’s often the right choice.

By isolating functionality, you put the view controllers of your project on a diet and make code easier to reuse. Why not extract a piece of functionality in an Objective-C category or a Swift extension.

If the implementation of a class exceeds 1000 lines of code, It’s a warning bells. Can you load 1000 lines of code in your working memory? That’s what it takes to work with a class. If you make changes to a class, you need to know how other parts are affected and that’s only possible if you know what the various parts of the class do.

3. Ignoring Errors

It’s great to see that error handling is tightly integrated into Swift. In Objective-C, it’s easy to ignore errors, too easy. Have you ever seen something like this?

[managedObjectContext save:nil];

I don’t know anyone who enjoys error handling. But if you want to write code that works and that can recover when things start to go wrong, then you need to accept that error handling is part of it. Ignoring errors also makes debugging more difficult. Putting your head in the sand isn’t the solution when things go wrong.

4. Singletons

Singletons are great. They are so useful that less experienced developers tend to overuse them. Been there, done that. It’s true that singletons are great. Even though I don’t consider the singleton pattern an anti-pattern, sometimes it seems as if developers have forgotten how to pass objects by reference.

One of the negative side effects of singletons is tight coupling. Over the years, I have come across projects that were littered with singletons, singletons referencing other singletons. This can make refactoring a nightmare.

Another benefit of keeping singletons to a minimum is testing. Singletons make testing more difficult.

There is nothing wrong with passing objects by reference. Whenever you are about to create a singleton, ask yourself whether passing the object by reference is an option. That should be your first choice.

5. String Literals

String literals are smelly by default. They are great in playgrounds, but how often do you (or should you) use string literals in a project? The only valid use cases I can think of are localization, constants, and logging.

I agree that creating constants or enumerations is tedious, but it pays off in the long run. You avoid typos, autocompletion kicks in, and it significantly improves maintainability.

Five Important Aspects Which Should Be Considered While Writing Swift Code