Внедрение зависимостей для статических функций для модульного теста в Swift - PullRequest
0 голосов
/ 09 мая 2018

Я знаю, что это выглядит как обычный вопрос, но после прочтения 10-15 учебника и изучения того, как я могу написать тест для своего класса обслуживания. Я не могу решить перенести статические функции в протокол или т. Д. Для внедрения зависимостей

У меня есть сетевой слой, как на картинке ниже. Все мои классы функций (например, выборочные пользователи, новости, медиа и т. Д.) Вызывают класс "Service Caller" и после этого, если ответом является ошибка; вызывает класс «Ошибка службы» для обработки ошибки, а если нет, декодируйте JSON.

Моя проблема заключается в том, что я вызываю класс обслуживания как статическую функцию, такую ​​как «ServiceCaller.performRequest», и если он получает ошибку, я также вызываю класс ошибки как статический, как «ServiceError.handle». Также он вызывает класс URLCache для получения пути URL запроса. Я не уверен, как я могу заставить их вводить зависимости и имитировать в тестовом классе. Как я нахожу в уроках, я должен написать это как;

protocol MyProtocol{
    func myfunction() -> Void
}
class A{
    let testProtocol = MyProtocol!
    init(pro: MyProtocol){
        testProtocol = pro
    }
}

и в функции настройки в тестовом классе это возможно;

myMockProtocol = ...
myTestclass = A.init(pro: myMockProtocol)

How My Network layer works

но я не могу найти, как я могу получить статические вызовы, такие как ServiceCaller.performRequest или ServiceError.handle ..; (Упрощенная версия в нижней части вопроса)

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)))
                }
            }
        }}
}
 }

Мой тестовый класс:

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
}

Уроки, которые я искал для модульного теста;

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/

РЕДАКТИРОВАТЬ: одним из решений может быть написание класса инициализации для всего сетевого уровня и классов обслуживания, а затем избавиться от статических функций? Но я не уверен, что это будет хороший подход.

РЕДАКТИРОВАТЬ 2: упрощенный код;

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)
    }
}
}

1 Ответ

0 голосов
/ 19 сентября 2018

Используйте насмешливый.

class ServiceCallerMock: ServiceCaller {
        override class func performRequest(route: ServiceRouter) -> (Any?) -> Void? {
            //your implementation goes here
        }
    }

Вы можете смоделировать ServiceCaller и переопределить метод executeRequest, а затем изменить функцию на:

static func initAppRequest(_ completion: @escaping (_ appInitRecevingModel: Result<AppInitRecevingModel>) -> Void, serviceCaller: ServiceCaller.Type = ServiceCaller.self) {
    ...
    serviceCaller.performRequest(route: route) { (result) in
    ...
}

Тогда вы могли бы вызвать функцию initAppRequest, используя вашу ложную реализацию ServiceCaller.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...