exponenta event banner

Создать имитационный объект

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

Test a component using mocked-up dependencies.

Например, предположим, что вы хотите протестировать алгоритм покупки запаса, но вы не хотите тестировать всю систему. Можно использовать имитационный объект для замены функциональности поиска цены акций, а другой имитационный объект - для проверки того, что трейдер приобрел акции. Тестируемый алгоритм не знает, что он работает на макетных объектах, и можно протестировать алгоритм, изолированный от остальной части системы.

С помощью имитационного объекта можно определить поведение (процесс, известный как stubbing). Например, можно указать, что объект создает предопределенные ответы на запросы. Можно также перехватывать и запоминать сообщения, отправленные из тестируемого компонента в имитационный объект (процесс, известный как шпионаж). Например, можно проверить, что был вызван конкретный метод или задано свойство.

Типичный рабочий процесс тестирования изолированного компонента выглядит следующим образом:

  1. Создайте макеты для зависимых компонентов.

  2. Определите поведение издевательств. Например, определите выходные данные, возвращаемые издевательским методом или свойством при вызове с определенным набором входных данных.

  3. Протестируйте интересующий компонент.

  4. Квалификация взаимодействий между интересующим компонентом и издевательскими компонентами. Например, убедитесь, что макетный метод был вызван с определенными входами, или что свойство установлено.

Зависит от компонентов

В этом примере тестируемый компонент представляет собой простой алгоритм дневной торговли. Это часть системы, которую требуется проверить независимо от других компонентов. Алгоритм дневной торговли имеет две зависимости: сервис данных для получения данных о цене акций и брокер для покупки акций.

В файле DataService.m в текущей рабочей папке создайте абстрактный класс, включающий lookupPrice способ.

classdef DataService
    methods (Abstract,Static)
        price = lookupPrice(ticker,date)
    end
end

В производственном коде может быть несколько конкретных реализаций DataService класс, такой как BloombergDataService класс. В этом классе используется Toolbox™ Datafeed. Однако, поскольку мы создаем макет DataService класс, вам не нужно устанавливать панель инструментов для выполнения тестов для алгоритма торговли.

classdef BloombergDataService < DataService
    methods (Static)
        function price = lookupPrice(ticker,date)
            % This method assumes you have installed and configured the
            % Bloomberg software.
            conn = blp;
            data = history(conn,ticker,'LAST_PRICE',date-1,date);
            price = data(end);
            close(conn)
        end
    end
end

В этом примере предположим, что компонент брокера еще не разработан. Как только он будет реализован, он будет иметь buy метод, который принимает символ бегущей строки и указанное количество акций для покупки, и возвращает код состояния. Макет компонента брокера использует неявный интерфейс и не является производным от суперкласса.

Тестируемый компонент

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

function trader(dataService,broker,ticker,numShares)
    yesterday = datetime('yesterday');
    priceYesterday = dataService.lookupPrice(ticker,yesterday);
    price2DaysAgo = dataService.lookupPrice(ticker,yesterday-days(1));
    
    if priceYesterday < price2DaysAgo
        broker.buy(ticker,numShares);
    end
end

Имитация объектов и объектов поведения

Объект mock является реализацией абстрактных методов и свойств интерфейса, заданных суперклассом. Можно также создать макет без суперкласса, в этом случае макет имеет неявный интерфейс. Тестируемый компонент взаимодействует с имитационным объектом, например, вызывая метод имитационного объекта или обращаясь к свойству имитационного объекта. Имитационный объект выполняет предопределенные действия в ответ на эти взаимодействия.

При создании макета также создается связанный объект поведения. Объект поведения определяет те же методы, что и имитационный объект, и управляет имитационным поведением. Используйте объект поведения для определения имитационных действий и определения взаимодействий. Например, используйте его для определения значений, возвращаемых издевательским методом, или убедитесь, что к свойству был получен доступ.

В командной строке создайте имитационный тестовый пример для интерактивного использования. Использование макета в тестовом классе вместо в командной строке представлено ниже в этом примере.

import matlab.mock.TestCase
testCase = TestCase.forInteractiveUse;

Создание заглушки для определения поведения

Создайте макет для зависимости службы данных и изучите методы для нее. Макет сервиса данных возвращает предварительно определенные значения, заменяя реализацию сервиса, предоставляющего фактические цены на акции. Поэтому он проявляет упрямое поведение.

[stubDataService,dataServiceBehavior] = createMock(testCase,?DataService);
methods(stubDataService)
Methods for class matlab.mock.classes.DataServiceMock:



Static methods:

lookupPrice  

В DataService класс, lookupPrice метод является абстрактным и статичным. Насмешливая структура реализует этот метод как конкретный и статичный.

Определите поведение макета службы данных. Для символа бегущей строки "FOO", он возвращает цену вчера как 123 доллара, а все, что было до вчерашнего дня, составляет 234 доллара. Поэтому, согласно trader функция, брокер всегда покупает акции "FOO". Для символа бегущей строки "BAR", он возвращает цену вчера в размере 765 долларов, а все, что было до вчерашнего дня, составляет 543 доллара. Поэтому брокер никогда не покупает акции "BAR".

import matlab.unittest.constraints.IsLessThan
yesterday = datetime('yesterday');

testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
    "FOO",yesterday),123);
testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
    "FOO",IsLessThan(yesterday)),234);

testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
    "BAR",yesterday),765);
testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
    "BAR",IsLessThan(yesterday)),543);

Теперь вы можете называть издевательство lookupPrice способ.

p1 = stubDataService.lookupPrice("FOO",yesterday)
p2 = stubDataService.lookupPrice("BAR",yesterday-days(5))
p1 =

   123


p2 =

   543

Пока assignOutputsWhen метод на testCase удобно указывать поведение, есть больше функциональных возможностей, если вы используете AssignOutputs действия. Дополнительные сведения см. в разделе Определение поведения имитационного объекта.

Создать шпион для перехвата сообщений

Создайте макет для зависимости брокера и изучите методы для нее. Поскольку макет брокера используется для проверки взаимодействия с тестируемым компонентом ( trader функция), она проявляет шпионское поведение. Макет брокера имеет неявный интерфейс. Пока buy метод в настоящее время не реализован, с его помощью можно создать макет.

[spyBroker,brokerBehavior] = createMock(testCase,'AddedMethods',{'buy'});
methods(spyBroker)
Methods for class matlab.mock.classes.Mock:

buy  

Позвоните в buy способ макета. По умолчанию он возвращается пустым.

s1 = spyBroker.buy
s2 = spyBroker.buy("inputs",[13 42])
s1 =

     []


s2 =

     []

С момента trader функция не использует код возврата состояния, допустимо модельное поведение возврата пустого по умолчанию. Макет брокера - чистый шпион, и не нуждается в реализации какого-либо упрямого поведения.

Вызов тестируемого компонента

Позвоните в trader функция. В дополнение к символу бегущей строки и количеству акций для покупки, trader функция принимает в качестве входных данных службу данных и брокера. Вместо передачи в фактическую службу данных и объекты брокера, передайте в spyBroker и stubDataService издевается.

trader(stubDataService,spyBroker,"FOO",100)
trader(stubDataService,spyBroker,"FOO",75)
trader(stubDataService,spyBroker,"BAR",100)

Проверка взаимодействия функций

Используйте объект поведения брокера (шпиона), чтобы проверить, что trader функция вызывает buy способ, как и ожидалось.

Используйте TestCase.verifyCalled для проверки того, что trader функция проинструктирована buy способ покупки 100 акций FOO акции.

import matlab.mock.constraints.WasCalled;
testCase.verifyCalled(brokerBehavior.buy("FOO",100))
Verification passed.

Убедитесь, что FOO акции приобретались два раза, независимо от указанного количества акций. Пока verifyCalled метод удобен для указания поведения, есть больше функциональных возможностей, если вы используете WasCalled ограничение. Например, можно проверить, что издевательский метод вызывался указанное количество раз.

import matlab.unittest.constraints.IsAnything
testCase.verifyThat(brokerBehavior.buy("FOO",IsAnything), ...
    WasCalled('WithCount',2))
Verification passed.

Убедитесь, что buy метод не был вызван, запрашивая 100 акций BAR акции.

testCase.verifyNotCalled(brokerBehavior.buy("BAR",100))
Verification passed.

Хотя trader была вызвана функция, запрашивающая 100 акций BAR акции, тупик, определенный вчерашней ценой для BAR чтобы вернуть более высокое значение, чем за все дни до вчерашнего дня. Поэтому брокер никогда не покупает акции "BAR".

Класс тестирования для trader Функция

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

classdef TraderTest < matlab.mock.TestCase
    methods(Test)
        function buysStockWhenDrops(testCase)
            import matlab.unittest.constraints.IsLessThan
            import matlab.unittest.constraints.IsAnything
            import matlab.mock.constraints.WasCalled
            yesterday = datetime('yesterday');
            
            % Create mocks
            [stubDataService,dataServiceBehavior] = createMock(testCase,...
                ?DataService);
            [spyBroker,brokerBehavior] = createMock(testCase,...
                'AddedMethods',{'buy'});
            
            % Set up behavior
            testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
                "FOO",yesterday),123);
            testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
                "FOO",IsLessThan(yesterday)),234);
            
            % Call function under test
            trader(stubDataService,spyBroker,"FOO",100)
            trader(stubDataService,spyBroker,"FOO",75)
            
            % Verify interactions
            testCase.verifyCalled(brokerBehavior.buy("FOO",100))
            testCase.verifyThat(brokerBehavior.buy("FOO",IsAnything),...
                WasCalled('WithCount',2))
        end
        function doesNotBuyStockWhenIncreases(testCase)
            import matlab.unittest.constraints.IsLessThan
            yesterday = datetime('yesterday');
            
            % Create mocks
            [stubDataService,dataServiceBehavior] = createMock(testCase,...
                ?DataService);
            [spyBroker,brokerBehavior] = createMock(testCase, ...
                'AddedMethods',{'buy'});
            
            % Set up behavior
            testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
                "BAR",yesterday),765);
            testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
                "BAR",IsLessThan(yesterday)),543);
            
            % Call function under test
            trader(stubDataService,spyBroker,"BAR",100)
            
            % Verify interactions
            testCase.verifyNotCalled(brokerBehavior.buy("BAR",100))
        end
    end
end

Выполните тесты и просмотрите таблицу результатов.

results = runtests('TraderTest');
table(results)
Running TraderTest
..
Done TraderTest
__________


ans =

  2×6 table

                      Name                       Passed    Failed    Incomplete    Duration      Details   
    _________________________________________    ______    ______    __________    ________    ____________

    'TraderTest/buysStockWhenDrops'              true      false       false        0.24223    [1×1 struct]
    'TraderTest/doesNotBuyStockWhenIncreases'    true      false       false       0.073614    [1×1 struct]