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

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

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

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

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

  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 берет в качестве входных параметров информационную службу и брокера. Вместо того, чтобы передать в фактической информационной службе и объектах брокера, передайте в макетах stubDataService и spyBroker.

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))
Interactive verification passed.

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

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

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

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

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

Тестирование Класса на Функцию торговца

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

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.13921    [1×1 struct]
    'TraderTest/doesNotBuyStockWhenIncreases'    true      false     false         0.066186    [1×1 struct]