Дразнить со Свифтом

Рассмотрим следующий класс с именем SomeClass, написанный на Swift:

@objc class SomeClass: NSObject
{
    var shouldCallBar = false

    func foo()
    {
        if (shouldCallBar == true)
        {
            bar()
        }
    }

    func bar()
    {
    }
}

Для тестирования описанного выше метода класса foo() (и подобных сценариев, в основном написанных на Objective-C), я использовал OCMock, например:

- (void) testFooBarShouldBeCalledWhenShouldCallBarIsTrue
{
    SomeClass * someClass = [SomeClass new];

    // Create mocks.
    id mockSomeClass = OCMPartialMock(someClass);

    // Expect.
    [[mockSomeClass expect] bar];

    // Stub.
    someClass.shouldCallBar = YES;

    // Run code under test.
    [someClass foo];

    // Verify.
    [mockSomeClass verify];

    // Stop mocking.
    [mockSomeClass stopMocking];
}

Но приведенный выше тест завершается неудачно с кодом Swift, поскольку OCMock не будет хорошо работать со Swift.

Итак, я рассматриваю что-то вроде целиком в Swift:

class SomeClassTests: XCTestCase
{
    class MockSomeClass: SomeClass
    {
        var isBarCalled = false

        override func bar()
        {
            isBarCalled = true
        }
    }

    func testBarShouldBeCalledWhenTrue()
    {
        let someClass = MockSomeClass()
        someClass.shouldCallBar = true

        someClass.foo()

        XCTAssertTrue(someClass.isBarCalled == true)
    }
}

Обратите внимание, что здесь я создаю подкласс для тестируемого исходного класса и переопределяю bar (). Я совсем не касаюсь реализации foo().

Но недостатком является то, что я использую экземпляр MockSomeClass для тестирования foo() SomeClass. Это то, что мне не нравится и не рекомендуется.

Есть ли лучшее решение проблемы выше?

Заметки:

  • Я не говорю о внедрении зависимости здесь. Внедрение зависимостей - это совершенно другой подход.
  • Я сталкиваюсь с такими проблемами при тестировании кода пользовательского интерфейса в UIViewController.
  • Я думал о программировании на основе протокола, но не смог найти решение проблемы выше.

1 ответ

Решение

Итак, вы хотите проверить этот метод (foo) вызывает или не вызывает другой метод (bar). foo метод является тестируемым, а bar В широком смысле метод является зависимым компонентом.

Если вызов bar Имеет длительные побочные эффекты, вы можете избежать тестирования, что побочный эффект есть / не присутствует, возможно, запросив свойство или подобное. В этом случае вам не нужно издеваться или подобное.

Если побочных эффектов нет, вы должны заменить зависимость. Для этого вам нужен шов, в который вы помещаете код, который может сообщить тесту, был ли вызван метод или нет. Для этого я вижу только два варианта, которые Джон уже обсуждал в вопросе, на который вы ссылаетесь.

Вы либо помещаете два метода в отдельные классы, и в этом случае границей класса является шов. Используя протокол или просто неофициальное соглашение, вы можете полностью заменить класс, который реализует bar, Инъекция зависимости здесь пригодится.

Если два метода должны оставаться в одном и том же классе, то вы должны использовать границу подкласса в качестве шва, т.е. вы используете тот факт, что вы можете переопределить методы и реализовать специфичный для теста подкласс. Это проще всего, когда вы можете использовать макет фреймворка. Если это не вариант, вы должны написать код самостоятельно, очень похоже на то, что вы описываете в своем вопросе.

Другие вопросы по тегам