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