XCTest ожидает вызов метода с быстрым

Как написать тест, ожидающий вызова метода, используя swift и XCTest?

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


person Rodrigo Ruiz    schedule 26.09.2014    source источник
comment
возможный дубликат Mocking in Swift   -  person ColinE    schedule 26.09.2014
comment
Отличие моего вопроса от этого в том, что я хочу ожидать вызова метода, а не просто издеваться над методом.   -  person Rodrigo Ruiz    schedule 02.10.2014


Ответы (1)


Как вы сказали, OCMock не поддерживает Swift (ни OCMockito), поэтому на данный момент единственный способ, который я вижу, - это создать ручной макет. В Swift это немного менее болезненно, так как вы можете создавать внутренние классы внутри метода, но все же не так удобно, как mocking framework.

Вот вам пример. Код не требует пояснений, единственное, что мне нужно было сделать (см. РЕДАКТИРОВАТЬ 1 ниже), чтобы заставить его работать, - это объявить классы и методы, которые я хочу использовать в тесте, как общедоступные (кажется, что тестовые классы не принадлежат тот же модуль кода приложения - постараюсь найти решение этой проблемы).

РЕДАКТИРОВАТЬ 1 2016/4/27: Объявление классов, которые вы хотите протестировать, как общедоступных, больше не требуется, поскольку вы можете использовать функцию «@testable import ModuleName».

Тест:

import XCTest
import SwiftMockingPoC

class MyClassTests: XCTestCase {

    func test__myMethod() {
        // prepare
        class MyServiceMock : MyService {

            var doSomethingWasCalled = false

            override func doSomething(){
                doSomethingWasCalled = true
            }

        }
        let myServiceMock = MyServiceMock()

        let sut = MyClass(myService: myServiceMock)

        // test
        sut.myMethod()

        // verify
        XCTAssertTrue(myServiceMock.doSomethingWasCalled)
    }

}

MyClass.swift

public class MyClass {

    let myService: MyService

    public init(myService: MyService) {
        self.myService = myService
    }

    public func myMethod() {
        myService.doSomething()
    }

}

MyService.swift

public class MyService {

    public init() {

    }

    public func doSomething() {

    }

}
person e1985    schedule 01.10.2014
comment
Я бы не хотел создавать переменную только для doSomethingWasCalled внутри фиктивного класса, а затем иметь утверждение для каждого метода, который, как я ожидаю, будет вызван. Мне хотелось чего-то большего, похожего на объявление переменной вне фиктивного класса с помощью expectationWithDescription, выполнение ее ожидания в методе фиктивного класса, а затем вызов в тесте waitForExpectationsWithTimeout. Но это не работает, тест не компилируется (или выдает ошибку SourceKitService Crashed, я действительно не помню и не могу протестировать прямо сейчас, потому что мой код не компилируется из-за чего-то еще, что я реализую) . - person Rodrigo Ruiz; 02.10.2014
comment
Также я не могу переопределить метод внутри теста, как вы. Та же ошибка, сбой SourceKitService. - person Rodrigo Ruiz; 02.10.2014
comment
AFAIK XCTestExpectation предназначен для асинхронного тестирования, я не вижу причин использовать его, если вы этого не хотите. Что касается переопределения этого метода, убедитесь, что вы объявляете общедоступными методы и классы, которые хотите использовать в своем тесте - код моего ответа работает для меня в Xcode. - person e1985; 02.10.2014
comment
Я все еще пытаюсь устранить ошибку с импортом FacebookSDK и тестированием, поэтому я еще раз протестирую ваш код позже, но я почти уверен, что получаю сбой SourceKitService, как и эта проблема stackoverflow.com/questions/24006206/ - person Rodrigo Ruiz; 02.10.2014
comment
И что бы вы еще сделали, чтобы протестировать вызов метода, не создавая переменную для каждого другого макета? - person Rodrigo Ruiz; 02.10.2014
comment
И если вы тестируете метод, который получает блок, разве это не считается асинхронным? - person Rodrigo Ruiz; 02.10.2014
comment
Я также получал эту ошибку несколько раз, когда писал этот код, если я хорошо помню, это было, когда я объявлял doSomethingWasCalled как локальную переменную в test__myMethod, а не как свойство в MyServiceMock. Кроме того, не забудьте объявить свои классы и методы общедоступными. О создании переменной для каждого другого макета (не уверен, что вы имеете в виду здесь), это решение подразумевает создание переменной для каждого ожидания. Если вы хотите использовать один и тот же фиктивный класс более чем в одном методе, вы можете определить его в своем тестовом классе, а не в тестовом методе. - person e1985; 02.10.2014
comment
Что касается синхронизации/асинхронности, получение блока не означает, что ваш тест должен быть асинхронным, это зависит от функциональности, которую вы хотите протестировать. - person e1985; 02.10.2014
comment
Давайте продолжим обсуждение в чате. - person e1985; 02.10.2014