Dependency Injection for Static Functions for Unit Test in Swift - ios

I know this looks like a common question but after reading 10-15 tutorial and looking how can I write test for my service class. I can't solve moving static functions to protocol or etc.. for dependency injection
I have a network layer like below image. All my function classes (like fetch users, news, media etc..) calls "Service Caller" class and after that If response is error; calls "Service Error" class to handle error and If not error, decode the JSON.
My problem is that I'm calling service class as a static function like "ServiceCaller.performRequest" and If It gets error I'm also calling error class as static like "ServiceError.handle". Also It calls URLCache class to get path of request url. I'm not sure how can I make them dependency inject and mock in test class. As I find in tutorials, I should write it like;
protocol MyProtocol{
func myfunction() -> Void
}
class A{
let testProtocol = MyProtocol!
init(pro: MyProtocol){
testProtocol = pro
}
}
and in setup function in test class it probably;
myMockProtocol = ...
myTestclass = A.init(pro: myMockProtocol)
but I can't find how can I get ride of static calls like ServiceCaller.performRequest or ServiceError.handle..; (Simplified version in the bottom part of question)
class AppInitService{
static func initAppRequest(_ completion: #escaping (_ appInitRecevingModel: Result<AppInitRecevingModel>) -> Void) {
let sendingModel = AppInitSendingModel(cmsVersion: AppDefaults.instance.getCMSVersion())
let route = ServiceRouter(method: .post, path: URLCache.instance.getServiceURL(key: URLKeys.initApp), parameters: (sendingModel.getJSONData()), timeoutSec: 1)
ServiceCaller.performRequest(route: route) { (result) in
if let error = result.error{
if let statusCode = result.response?.statusCode{
completion(.error(ServiceError.handle(error: error, statusCode: statusCode)))
}else{
completion(.error(ServiceError.handle(error: error, statusCode: error._code)))
}
}else{
if let data = result.data{
do{
var responseJson = JSON(data)
responseJson["idleTimeoutInMinutes"] = 10
let input = try AppInitRecevingModel(data: responseJson.rawData())
completion(.success(input))
}catch let error{
completion(.error(ServiceError.handle(error: error, statusCode: -1002)))
}
}
}}
}
}
My Test class:
class MyProjectAppInitTests: XCTestCase {
var appInitTest: AppInitService!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
appInitTest = AppInitService.init()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
appInitTest = nil
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
let testParamater = ["string":"test"]
let route = ServiceRouter(method: .post, path: "/testPath", parameters: testParamater.getJSONData(), timeoutSec: 10)
appInitTest. //cant call anything in here
}
Tutorials I looked for Unit Test;
https://www.raywenderlich.com/150073/ios-unit-testing-and-ui-testing-tutorial
https://www.swiftbysundell.com/posts/time-traveling-in-swift-unit-tests
https://marcosantadev.com/test-doubles-swift
http://merowing.info/2017/04/using-protocol-compositon-for-dependency-injection/
EDIT: One solution maybe writing init class for whole network layer and service classes then get rid of static functions? But I'm not sure If It will be a good approach.
EDIT 2: Simplified Code;
class A{
static func b(completion:...){
let paramater = ObjectModel(somevariable: SomeClass.Singleton.getVariable()) //Data that I sent on network request
let router = ServiceRouter(somevariable: SomeClassAgain.Singleton.getsomething()) //Router class which gets parameters, http method etc..
NetworkClass.performNetworkRequest(sender: object2){ (result) in
//Result - What I want to test (Write UnitTest about)
}
}
}

Use mocking.
class ServiceCallerMock: ServiceCaller {
override class func performRequest(route: ServiceRouter) -> (Any?) -> Void? {
//your implementation goes here
}
}
You could mock ServiceCaller and override the performRequest method, then change the function to:
static func initAppRequest(_ completion: #escaping (_ appInitRecevingModel: Result<AppInitRecevingModel>) -> Void, serviceCaller: ServiceCaller.Type = ServiceCaller.self) {
...
serviceCaller.performRequest(route: route) { (result) in
...
}
Then you could call the initAppRequest function using your mock implementation of ServiceCaller.

Related

Swift Test: Controlling moc api client behaviour

I am writing test for my project and declared a remote API client protolcol:
public protocol ApiClient {
func load(completion: #escaping ([Any]?, String?))
}
and defined a moc api client that confirms to ApiClient:
class MocApiClient: ApiClient {
func loadFlights(completion: #escaping ([Any]?, String?)) {
// Load a sample JSON file and return it as response
}
}
this way I am able return a response by loading a JSON file. This is the happy path of the test. After it I started to think about testing different possible response types and decided that I should be able to alter behaviour of the MocApiClient and defined this:
enum TestPath {
case success
case failure
}
and using it with MocApiClient:
class MocApiClient: ApiClient {
var path: TestPath = .success
func load(completion: #escaping ([Any]?, String?) -> Void) {
switch path {
case .success:
completion([...], nil)
case .failure:
completion(nil, "error message")
}
}
}
Doyu think this is a good solution?
Do you have any beter approachs?
Your approach seems fine if you have just a few simple tests.
However, if you have a relatively complex logic and need to test many positive and negative paths, an alternative to your solution would be creating several different mocking objects, each one having a single purpose.
That way, you'll avoid a massive all-purpose mocking object problem and also will be able to define your mocking objects right inside the tests methods that use them, thus making your tests even more focused and independent.
Something like this:
func testSuccessfulResponse() {
class MockApiClientSuccessfulResponse: ApiClient {
...
}
...
}
func testMalformedResponse() {
class MockApiClientMalformedResponse: ApiClient {
...
}
...
}
func testInconsistentData() {
class MockApiClientInconsistentData: ApiClient {
...
}
...
}
Hope this helps and happy unit testing!

Swift - How to Network disable on Unit Test

Is there any way to close Internet Connection for Unit Test? I have to check async function when the device on online or not. How can I do that ?
I know the Additional tools Package but I want to write this feature with programmatically.
Thanks in Advance
This is how I mock an API call. The steps requires
1) No internet connection
2) Mocking the API Call and data
I have created data models for the api response and this is how I am mocking it
func buildActivityList() -> ActivityResponse {
let resp = ActivityResponse(json: .null)
let userHistory = ActivityUserHistory(json: .null)
userHistory.time = "2018-02-16T07:41:54.046Z"
resp.userHistory = [userHistory]
//Add all other relevant data
return resp
}
Next you need to override the function were you are calling API and pass the above mocked data as API response.
Suppose you have a class where you call the api
class UserService {
func getUserDetails(completion: (_: ActivityResponse?, _: Error?)) {
//get RESPONSE from server
let data = ActivityResponse(object: RESPONSE)
return completion(data, nil)
}
}
Now in your test class call the buildActivityList() which we created and pass it in the mock class.
class MockUserService: UserService {
override func getUserDetails(completion: (_: ActivityResponse?, _: Error?)) {
return completion(buildActivityList(), nil)
}
}
This way whenever you test an API call, the mock data will be injected. Thus no need for internet connection.
Hope this helps

Proper use of Strategy Pattern for simple networking layer in Swift 3

Does the following code demonstrate proper use of Strategy design pattern for a simple networking layer in swift 3?
Some code smells I'm unsure about:
violates Single responsibiility principle. Each strategy class such as Find, has a method for a different type of implementation. This is because I could want to find an image, or a user, or a chatroom. which are stored at different nodes in Firebase. all these different find methods are clumped together in Find class.
At the call sight of a request, if I need to make multiple async request, I nest the next request call inside the closure of the call back. Is this Ok?
The request object allows access to every type of insert, and find method. so in my signup VC I could i have the option to download a chatroom. Is even having access to that kind of implementation bad?
I have posted the code below, and left out all the actual implementation for brevity.
Any tips or guidance is much appreciated!
// USE CASE: Would go in viewDidLoad of ViewController
func testMyRequest () {
let myRequest = Request(insert: Insert(), find: Find())
myRequest.find?.user(with: "id", handler: { (user) in
myRequest.find?.nearbyUsers(user: user, handler: { (users) in
// update collectionView datasource
})
})
}
// Is this protocol necessary?
protocol RequestProtocol {
// - Family of algorithms, related actions.
var insert: Insert? { get set }
var find: Find? { get set }
}
// ---------------------------
class Request: RequestProtocol {
var insert: Insert?
var find: Find?
init(insert: Insert?, find: Find?) {
self.insert = insert
self.find = find
}
}
// Use a singleton maybe for the classes below? Why wouldn't I?
class Insert {
init() { }
func user(_ user: User) {
// insert user to firebase implementation
}
func message(_ message: Message) -> Void {
// insert message to firebase impelmentation
}
func image(data: Data, user: User) {
// insert image to firebase impelmentation
}
}
class Find {
init() { }
func user(with id: String, handler: #escaping (_ user: User) -> Void ) {
// find user implementation
}
func allChatrooms(handler: #escaping ([Chatroom]) -> Void) {
// find all chatrooms implementation
}
func nearbyUsers(user: User, handler: #escaping ([User]) -> Void ) {
// find nearby Users relative to current User location implementation
}
// Private helper method
private func findChatPartners (currentUser: User, chatrooms: [Chatroom] ) -> Set<String> {
}
}

Mocking in Swift for XCTest

I am writing test cases for my project which is mix up with Objective C as well as Swift code. I am aware about OCMock framework which I have used previously for mocking/Stubbing for writing Test cases in Objective C.
But I googled and found that it doesn't support fully for swift, since it is based on Objective C runtime.
I am trying to write test cases in swift language. Is there way I can do mocking/Stubbing for service level layer. For eg.
func getPersonData(id:String, success: (ReponseEnum) -> Void, failure: (error: NSError) -> Void) {
let requestPara:NSDictionary = ["id": id]
let manager: MyRequestManager = MyRequestManager.sharedManager()
//MyRequestManager is nothing but AFNetworking class
let jsonRequest
/// Service request creation code here
// Service Call
manager.POST(url, parameters: jsonRequest, success: { (task: NSURLSessionDataTask!, responseObject: AnyObject!) -> () in
// Some business logic
//success block call
success (successEnum)
}) {(task: NSURLSessionDataTask!, error: NSError!) -> () in
// failure block call
failure (failureEnum)
}
}
Here how to mock post method call for dummy responseObject So I can write test cases?
You need to use dependency injection to be able to mock the POST method.
Your class, where you defined the getPersonData(id:success:failure) method, needs to accept MyRequestManager as a parameter in constructor:
class MyClass {
private let requestManager: MyRequestManager
init(requestManager: MyRequestManager) {
self.requestManager = requestManager
}
}
Then you create a mock for your request manager:
class MockMyRequestManager: MyRequestManager {
// not sure about correct method signature
override func POST(url: NSURL, parameters: [String: AnyObject], success: (() -> Void)?) {
//implement any custom logic that you want to expect when executing test
}
}
And in the tests you initialise your class with a mock:
let myClass = MyClass(requestManager: MockMyRequestManager())
You can find more details about dependency injection here:
http://martinfowler.com/articles/injection.html

Where to put reusable functions in IOS Swift?

New to IOS programming but just wondering where is the best place to put functions that I would use throughout my code. For example, I want to write a few functions to perform a POST request to a web service and return a dictionary. Maybe another function to do some calculations. Is it best to create another .swift file and put all my functions there. And what would be a good name to give the file if so?
public func postRequest() -> [String:String] {
// do a post request and return post data
return ["someData" : "someData"]
}
The best way is to create a helper class with static functions, like this:
class Helper{
static func postRequest() -> [String:String] {
// do a post request and return post data
return ["someData" : "someData"]
}
}
Now every time you need to use postRequest you can just use like so: Helper.postRequest()
I usually create a separate class if I have functions that will be used by multiple classes, especially for the ones involving network operations.
If you just have separate functions that will be used, you can simply create static functions inside that class so it is easily accessible by other classes in a static way:
class DataController {
static func getData() -> [String:String] {
// do some operations
return ["someData" : "someData"]
}
}
let data = DataController.getData() // example
However, what often has been the case for me (especially if it involves more complicated operations) was that these network operations needed to establish an initial connection beforehand or required some initial setups, and they also performed asynchronous operations that needed to be controlled. If this is the case and you will often be calling such methods, you might want to create a singleton object that you could use throughout different classes and functions. This way, you could do the initial setup or establish an initial connection just once, and then do the rest as needed with the other functions, instead of doing them every time the function gets called.
Creating a singleton object is pretty simple in Swift:
class DataController {
static let sharedInstance = DataController() // singleton object
init() {
// do initial setup or establish an initial connection
}
func getData() -> [String:String] {
// do some operations
return ["someData" : "someData"]
}
}
let data = DataController.sharedInstance.getData() // example
For the name of the class, I usually name it something like DataController or DataHelper, but anything that makes sense as a "helper" class would work.
Hope this helps :)
For reusable functions it depends what I decide to use. For this specific case I use a separate file, because posting to a backend will become more complicated when the application evolves. In my app I use a backend class, with all kinds of helper classes:
struct BackendError {
var message : String
}
struct SuccessCall {
var json : JSON
var containsError : Bool {
if let error = json["error"].string {
return true
}
else {
return false
}
}
}
typealias FailureBlock = (BackendError) -> Void
typealias SuccessBlock = (SuccessCall) -> Void
typealias AlamoFireRequest = (path: String, method: Alamofire.Method, data: [String:String]) -> Request
typealias GetFunction = (path: String , data: [String : String], failureBlock: FailureBlock, successBlock: SuccessBlock) -> Void
class Backend {
func getRequestToBackend (token: String )(path: String , data: [String : String], failureBlock: FailureBlock, successBlock:
}
For other cases I often use extensions on Swift classes. Like for getting a random element from an Array.
extension Array {
func sampleItem() -> T {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
This very old question but I would like to chirp some more points.
There are a few option, basically you can write your utility functions in Swift -
A class with static function. For example
class CommonUtility {
static func someTask() {
}
}
// uses
CommonUtility.someTask()
Also, you can have class method's as well instead of static method but those functions can be overridden by subclasses unlike static functions.
class CommonUtility {
class func someTask() {
}
}
// uses
CommonUtility.someTask()
Secondly, you can have Global functions as well, that are not part of any class and can be access anywhere from your app just by name.
func someTask() {
}
Though, selecting one over other is very subjective and I thing this is ok to make a class with static function in this particular case, where you need to achieve networking functionality but if you have some functions which perform only one task than Global function is a way to go because Global functions are more modular and separate out single tasks for a single function.
In case of static functions, if we access one of the static member, entire class gets loaded in memory. But in case of global function, only that particular function will be loaded in mem
You can create a separate swift class, might name it WebServicesManager.swift, and write all methods related to web requests in it.
You can use class methods, or singleton pattern to access the methods.

Resources