Как написать модульные тесты для методов в swift, которые возвращают обещания с ответом, полученным при вызове методов mocked class? - PullRequest
0 голосов
/ 26 января 2020

Я пишу юнит-тесты для методов класса в swift. Эти методы вызывают методы класса, которые макетируются для модульного теста. Методы возвращают обещания с ответом, полученным из макетированных статических / классовых методов.

NetworkAuthProtocol: -

protocol NetworkAuthProtocol {
      static func httpGet(_ url: String, completionHandler complete: @escaping(Any?, Error?) -> Void)
      static func httpDelete(_ url: String, completionHandler complete: @escaping (Any?, Error?) -> Void)
}

Ниже приведен макет класса для модульного теста с методами класса: -

class NetworkAuthMock: NetworkAuthProtocol {

    class func httpGet(_ url: String, completionHandler complete: @escaping(Any?, Error?) -> Void) {
        complete(nil, Error)
    }

    class func httpDelete(_ url: String, completionHandler complete: @escaping (Any?, Error?) -> Void) {
        complete(nil, Error)
    }
}

Ниже приведен класс, для которого я пишу юнит-тесты: -

class FetchDataAPIService {

    var networkAuthDelegate: NetworkAuthProtocol.Type = NetworkAuth.self

    func fetchUserData(userUrl: String) -> Promise<Any> {
        return Promise<Any> { seal in
            networkAuthDelegate.httpGet(userUrl) { (result, error) in
                if error == nil {
                    seal.fulfill(result!)
                } else {
                    seal.reject(error!)
                }
            }
        }
    }

   func deleteUserData(userUrl: String) -> Promise<Any> {
        return Promise<Any> { seal in
            networkAuthDelegate.httpDelete(userUrl) { (_, error) in
                if error == nil {
                    seal.fulfill(())
                } else {
                    seal.reject(error!)
                }
            }
        }
    }
}

Юнит-тесты, которые я написал, добавив их ниже: -

class FetchDataAPIServiceTests: XCTestCase {
    var fetchDataAPIService: FetchDataAPIService!
    var result: String?
    var error: Error?

    override func setUp() {
        super.setUp()
        self.fetchDataAPIService = FetchDataAPIService()
        self.fetchDataAPIService.networkAuthDelegate = NetworkAuthMock.self
        self.error = nil
    }

    override func tearDown() {
        super.tearDown()
    }

    func testFetchUserData() {

        let expectation = XCTestExpectation(description: "test fetchUserData")
        firstly {
            self.fetchDataAPIService.fetchUserData(userUrl: "url")
        }.done { (_) in
            XCTFail("test failed")
        }.catch { (error) in
            XCTAssertNotNil(error)
            expectation.fulfill()
        }

        wait(for: [expectation], timeout: 10.0)
    }

    func testDeleteUserData() {

        let expectation = XCTestExpectation(description: "test deleteUserData")
        firstly {
            self.fetchDataAPIService.deleteUserData(userUrl: "url")
        }.done { (_) in
            XCTFail("test failed")
        }.catch { (error) in
            XCTAssertNotNil(error)
            expectation.fulfill()
        }

        wait(for: [expectation], timeout: 10.0)
    }
}

С ожиданиями, пройти тест в изоляции, но вместе он не проходит. Я пробовал другие методы, такие как добавление DispatchQueue.main.asyncAfter вне оператора XCTAssert. Также я добавил точки останова в метод httpGet и httpDelete класса Mock, контроль там никогда не достигается. Ничто не похоже на работу. Покрытие кода работает только тогда, когда тесты выполняются изолированно, но иногда это тоже, а также происходит сбой сборки теста в файле main.m. Как я могу сделать все тесты успешными, а также покрыть тестовое покрытие для всех методов?

Ответы [ 2 ]

1 голос
/ 26 января 2020

Видите, это одна из проблем, связанных с функциями c - вам нужно убедиться, что вы выполняете надлежащую очистку перед / после каждого теста, чтобы убедиться, что вы не накладываете неявные зависимости между тестами.

Я бы рекомендовал вместо этого переключиться на более OOP подход с использованием методов экземпляра:

protocol NetworkAuthProtocol {

    func httpGet(_ url: String, completionHandler complete: @escaping(Any?, Error?) -> Void)

    func httpDelete(_ url: String, completionHandler complete: @escaping (Any?, Error?) -> Void)
}


class FetchDataAPIService {

    let networkAuthDelegate: NetworkAuthProtocol

    init(networkAuth: NetworkAuthProtocol) {
        self.networkAuthDelegate = networkAuth
    }

, и в ваших тестах добавьте поддержку для настройки ответа на отправку:

class TestsNetworkAuth {
    var httpGetResult: (Any?, Error?) = (nil, nil)
    var httpDeleteResult: (Any?, Error?) = (nil, nil)

    func httpGet(_ url: String, completionHandler complete: @escaping(Any?, Error?) -> Void) {
        complete(httpGetResult.0, httpGetResult.1)
    }

    func httpDelete(_ url: String, completionHandler complete: @escaping (Any?, Error?) -> Void) {
        complete(httpDeleteResult.0, httpDeleteResult.1)
    }
}

class FetchDataAPIServiceTests: XCTestCase {
    var testsNetworkAuth = TestsNetworkAuth()
    var fetchDataAPIService: FetchDataAPIService!

    override func setUp() {
        super.setUp()
        fetchDataAPIService = FetchDataAPIService(networkAuth: testsNetworkAuth)
    }

С помощью вышеуказанного подхода вы можете добавлять все виды тестов, имитирующих различные данные результатов и / или ошибки. И тесты не будут мешать друг другу, так как у каждого есть свой экземпляр TestsNetworkAuth. В первоначальном подходе из-за stati c характера методов тесты обменивались данными.

0 голосов
/ 27 января 2020

Поскольку блок done / catch и одновременно waitForExpectation выполняются в главной очереди, блок done / catch ожидает завершения метода waitForExpectation, и наоборот, что приводит к взаимоблокировке. Поэтому, запустив done и перехватить блок в фоновой очереди, как показано ниже, тесты должны пройти успешно.

func testFetchUserData() {

        let expectation = XCTestExpectation(description: "test fetchUserData")
        firstly {
            self.fetchDataAPIService.fetchUserData(userUrl: "url")
        }.done(on: .global(qos: .background)) { (_) in
            XCTFail("test failed")
        }.catch(on: .global(qos: .background)) { (error) in
            XCTAssertNotNil(error)
            expectation.fulfill()
        }

        wait(for: [expectation], timeout: 10.0)
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...