Когда модульное тестирование, вы часто интересуетесь тестированием фрагмента полной системы, изолированный от компонентов это зависит от. Чтобы протестировать фрагмент системы, мы можем заменить фиктивными объектами, чтобы заменить зависевший - на компонентах. Фиктивный объект реализует, по крайней мере, часть того же интерфейса как производственный объект, но часто способом, который прост, эффективен, предсказуем, и управляем. Когда вы используете платформу для мокинга, компонент под тестом не знает, является ли его сотрудник "действительным" объектом или фиктивным объектом.
Например, предположите, что вы хотите протестировать алгоритм на покупку запаса, но вы не хотите тестировать целую систему. Вы могли использовать фиктивный объект, чтобы заменить функциональность поиска курса акций и другого фиктивного объекта, чтобы проверить, что торговец купил запас. Алгоритм, который вы тестируете, не знает, что работает с фиктивными объектами, и можно протестировать алгоритм, изолированный от остальной части системы.
Используя фиктивный объект, можно задать поведение (процесс, известный блокирующий). Например, можно указать, что объект производит предопределенные ответы на запросы. Можно также прервать и помнить сообщения, отправленные от компонента под тестом к фиктивному объекту (процесс, известный шпионящий). Например, можно проверить, что конкретный метод был назван, или свойство было установлено.
Типичный рабочий процесс, чтобы протестировать компонент в изоляции следующие:
Создайте макеты для зависевшего - на компонентах.
Задайте поведения макетов. Например, задайте выходные параметры, которые возвращают дразнивший метод или свойство, когда это вызвано определенным набором входных параметров.
Протестируйте компонент интереса.
Квалифицируйте взаимодействия между компонентом интереса и дразнившими компонентами. Например, проверьте, что дразнивший метод был вызван конкретными входными параметрами, или что свойство было установлено.
В этом примере компонент под тестом является простым торгующим днем алгоритмом. Это - часть системы, которую вы хотите протестировать независимый от других компонентов. Торгующий днем алгоритм имеет две зависимости: информационная служба, чтобы получить данные о курсе акций и брокера, чтобы купить запас.
В файле 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]