2014-02-03 7 views
2

Класс, который я выполняю Unit Testing, прокручивает каждую запись таблицы DB и суммирует значение в поле с предыдущим значением. Ниже приведен класс, сведенный к кости:Предоставление данных для Mock для модульного тестирования

procedure TSumList.Sum; 
var 
    FSum:integer; 
begin 
    FSum:=0; 
    FDB.First; 
    while not FDB.EOF do 
    begin 
    FSum:=FSum+FDB.GetAmount; 
    FDB.Next; 
    end; 
end; 

FDB относится к интерфейсу DB mock под названием IIDBTable.

Ниже БД насмешка для инъекций в зависимости:

IIDBTable = interface 
    ['{A299D1D6-93AF-45CC-8DE2-9A4EE188C352}'] 
    procedure First; 
    procedure Next; 
    function EOF : boolean; 
    function GetAmount:integer; 
    end; 

    TMockDBTable = class (TInterfacedObject,IDBTable) 
    procedure First; 
    procedure Next; 
    function EOF : boolean; 
    function GetAmount:integer; 
    end; 

Проблема я не знаю, как предоставить данные в макет для теста. Конечно, я могу добавить дополнительную процедуру, например AddValues ​​(aAmount: integer), которая выполняет эту работу, но в этом случае я тоже закончил бы эту дополнительную процедуру в производстве, и мне она не нужна.

Какова наилучшая практика для этого?

Я использую Spring для основы Delphi

+0

Не хватает информации: как TSumList знает о IIDBTable? Кроме того: дополнительный метод может быть на TMockDBTable, а не на интерфейсе. Или вы можете просто передать данные в конструкторе TMockDBTable –

+2

Вам нужно реализовать издевательскую функциональность в 'TMockDBTable'. Поскольку 'TMockDBTable' предоставляет реализацию' IDBTable', которая используется при тестировании, вы должны сделать так, чтобы 'TMockDBTable' получал макет данных. Я не понимаю, почему вы думаете, что вам нужно что-то добавить к вашему производственному коду. Вы не ставили «TMockDBTable» в свой производственный код? –

ответ

2

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

Я подозреваю, вы считали, что код должен быть в производстве, потому что вы имеете дело с макетным объектом исключительно через интерфейс. Дополнительный метод будет существовать в интерфейсе и поэтому должен быть реализован в производственной версии класса, хотя он там и не принадлежит.

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

Другим вариантом является передача данных в класс с помощью параметра, который вы добавляете к его конструктору. Тогда вам даже не нужен дополнительный метод, и поэтому нет соблазна определить этот метод на интерфейсе или на производственном классе. Нет причин, по которым конструктор вашего mock-класса должен выглядеть как конструктор производственного класса, поскольку классы вообще не связаны. Конструктор не является частью определения интерфейса.

4

Вы также можете использовать DSharp mocks (или Delphi Mocks).

Это будет настроечный код для DSharp (Delphi Mocks должен быть похож)

var 
    mockDBTable: Mock<IIDBTable>; 
begin 
    mockDBTable.Setup.WillExecute.Once.WhenCalling.First; 
    mockDBTable.Setup.WillReturn(False).Exactly(5).WhenCalling.EOF; 
    mockDBTable.Setup.WillReturn(True).Once.WhenCalling.EOF; 
    mockDBTable.Setup.WillReturn(5).Once.WhenCalling.GetAmount; 
    mockDBTable.Setup.WillReturn(4).Once.WhenCalling.GetAmount; 
    mockDBTable.Setup.WillReturn(3).Once.WhenCalling.GetAmount; 
    mockDBTable.Setup.WillReturn(2).Once.WhenCalling.GetAmount; 
    mockDBTable.Setup.WillReturn(1).Once.WhenCalling.GetAmount; 
    mockDBTable.Setup.WillExecute.Exactly(5).WhenCalling.Next; 

Что вы делаете здесь указать, что вы ожидаете назвать и то, что должны быть возвращены. Это избавляет вас от ручного написания макетного класса и подачи его данными.

+0

Возможно, это только я, но я считаю такой код более мешающим, чем помощь. Я не уверен, что вы спасли от себя, но я думаю, что я буду выбирать вручную каждый раз, когда вы делаете макет! –

+0

Я согласен с тем, что в этом случае установочный код немного подробный, и делать это, как это может быть, не лучший подход, потому что ваш макет в этом случае больше ориентирован на данные. Таким образом, использование «реального» экземпляра IIDBTable с тестовыми данными может быть лучшим подходом. Но я бы не сказал, что писать макетные классы каждый раз - лучший способ. Вы, очевидно, не использовали автоматическую насмешку. В противном случае вы можете подумать иначе. Также неясно в этом примере, потому что вы используете каждый метод интерфейса, но у вас могут быть случаи, когда у вас есть несколько методов для реализации (даже если они пусты), но только используют один. –

+1

Да, вы правы, «каждый раз» слишком силен. Я уверен, что на самом деле это не так. Я рассматривал этот один пример в изоляции. Ценность данных делает автоматическое насмешливое отношение менее привлекательным здесь, но я также борюсь с беглому стилю. Опять же, может быть, из-за незнания. Мне нравится писать тесты NUnit, но когда это приводит меня к беглому стилю, мне очень трудно читать то, что я пишу. Во всяком случае, +1, это хороший ответ. –

Смежные вопросы