Создание фиктивного объекта

Когда модульное тестирование, вы часто интересуетесь тестированием фрагмента полной системы, изолированный от компонентов это зависит от. Чтобы протестировать фрагмент системы, мы можем заменить фиктивными объектами, чтобы заменить зависевший - на компонентах. Фиктивный объект реализует, по крайней мере, часть того же интерфейса как производственный объект, но часто способом, который прост, эффективен, предсказуем, и управляем. Когда вы используете платформу для мокинга, компонент под тестом не знает, является ли его сотрудник "действительным" объектом или фиктивным объектом.

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

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

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

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

  2. Задайте поведения макетов. Например, задайте выходные параметры, которые возвращают дразнивший метод или свойство, когда это вызвано определенным набором входных параметров.

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

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

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

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

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

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

В производственном коде могло быть несколько конкретных реализаций DataService класс, такой как BloombergDataService класс. Этот класс использует Datafeed Toolbox™. Однако, поскольку мы создаем макет для 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

Фиктивные объекты и объекты поведения

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

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

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

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]