Как тестировать абстрактные классы: дополнить заглушками?

В C переменные передаются по значению - копия указателя передается функции. Вместо этого используйте другой указатель на указатель:

void change(int **p, int *someOtherAddress)
{
    *p = someOtherAddress;
}

int a = 1, b = 2;
int *p = &a;

printf("*p = %d\n", *p);
change(&p, &b);
printf("*p = %d\n", *p);

Отправляет

*p = 1
*p = 2
422
задан Arpit 21 December 2017 в 06:48
поделиться

11 ответов

Запишите Фиктивный объект и используйте их только для тестирования. Они обычно очень очень очень минимальны (наследуйтесь абстрактному классу), и не больше. Затем в Вашем Модульном тесте можно назвать абстрактный метод, который Вы хотите протестировать.

необходимо протестировать абстрактный класс, которые содержат некоторую логику как все другие классы, которые Вы имеете.

251
ответ дан mezoid 21 December 2017 в 06:48
поделиться
  • 1
    It' s стоящий замечания, что прием умножения эвристики константой сделает эвристические недопустимые, и результаты как таковые в поиске, больше не являющемся оптимальным. – Andrew Walker 1 March 2012 в 12:10

Если конкретные методы вызывают какой-либо из абстрактных методов, что стратегия не будет работать, и Вы хотели бы протестировать каждое поведение дочернего класса отдельно. Иначе расширение его и блокирование абстрактных методов, как Вы описали, должны быть прекрасными, снова если методы бетона абстрактного класса разъединяются от дочерних классов.

3
ответ дан Jeb 21 December 2017 в 06:48
поделиться
  • 1
    У меня есть предупреждение: " NSDateComponents не может ответить на compare" – CrazyDev 6 June 2011 в 13:42

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

2
ответ дан Ace 21 December 2017 в 06:48
поделиться

Это - шаблон, за которым я обычно следую при установке ремня безопасности для тестирования абстрактного класса:

public abstract class MyBase{
  /*...*/
  public abstract void VoidMethod(object param1);
  public abstract object MethodWithReturn(object param1);
  /*,,,*/
}

И версия я использую под тестом:

public class MyBaseHarness : MyBase{
  /*...*/
  public Action<object> VoidMethodFunction;
  public override void VoidMethod(object param1){
    VoidMethodFunction(param1);
  }
  public Func<object, object> MethodWithReturnFunction;
  public override object MethodWithReturn(object param1){
    return MethodWihtReturnFunction(param1);
  }
  /*,,,*/
}

, Если абстрактные методы называют, когда я не ожидаю его, тестовый сбой. При расположении тестов я могу легко погасить абстрактные методы с лямбдами, которые работают, утверждает, выдайте исключения, возвратите различные значения, и т.д.

4
ответ дан Will 21 December 2017 в 06:48
поделиться

один путь состоит в том, чтобы записать абстрактный тестовый сценарий, который соответствует Вашему абстрактному классу, затем запишите конкретные тестовые сценарии, которые разделяют Ваш абстрактный тестовый сценарий на подклассы. сделайте это для каждого конкретного подкласса Вашего исходного абстрактного класса (т.е. Ваша иерархия тестового сценария зеркально отражает Вашу иерархию классов). посмотрите Тест интерфейс в junit recipies книга: http://safari.informit.com/9781932394238/ch02lev1sec6 .

также посмотрите Суперкласс Тестового сценария в xUnit шаблонах: http://xunitpatterns.com/Testcase%20Superclass.html

6
ответ дан Ray Tayek 21 December 2017 в 06:48
поделиться
  • 1
    можно ли показать код редактированием на OP? – vikingosegundo 6 June 2011 в 13:44

Я привел бы доводы против "абстрактных" тестов. Я думаю, что тест является конкретной идеей и не имеет абстракции. Если Вы имеете общие элементы, помещаете их во вспомогательные методы или классы для всех для использования.

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

4
ответ дан casademora 21 December 2017 в 06:48
поделиться
  • 1
    прекрасный!!! Спасибо!!!:) в моем приложении после рытья через API, смотрящий на Ваш пример, я использовал $ (" #YourGrid") .data () .kendoGrid.dataSource.data () и $ (" #YourGrid") .data () .kendoGrid.dataSource.at (индекс):) –  1 May 2012 в 20:14

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

С точки зрения поблочного тестирования, существует две вещи рассмотреть:

  1. Взаимодействие Вашего абстрактного класса со связанные классы . Используя ложную среду тестирования идеально для этого сценария, поскольку он показывает, что Ваш абстрактный класс играет хорошо с другими.

  2. Функциональность производных классов . Если у Вас есть пользовательская логика, которую Вы записали для своих производных классов, необходимо протестировать те классы в изоляции.

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

2
ответ дан bryanbcook 21 December 2017 в 06:48
поделиться

Для создания модульного теста конкретно на абстрактном классе необходимо получить его для тестирования цели, протестировать base.method () результаты и предназначенное поведение при наследовании.

Вы тестируете метод путем вызова его, так протестируйте абстрактный класс путем реализации его...

8
ответ дан 21 December 2017 в 06:48
поделиться

Что я делаю для абстрактных классов, и интерфейсы следующее: Я пишу тест, который использует объект, поскольку это конкретно. Но переменная типа X (X абстрактный класс) не установлена в тесте. Этот тестовый класс не добавляется к набору тестов, но подклассам его, которые имеют метод установки, которые устанавливают переменную на конкретную реализацию X. Тем путем я не копирую тестовый код. Подклассы не используемого теста могут добавить больше методов тестирования в случае необходимости.

11
ответ дан Mnementh 21 December 2017 в 06:48
поделиться

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

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

Однако я думаю, что важно, чтобы эти тесты сфокусировались на конкретные реализации реальной бизнес-логики [только 115]; Вы не были должны модульный тест детали реализации из абстрактного класса, потому что Вы закончите с хрупкими тестами.

8
ответ дан Seth Petry-Johnson 21 December 2017 в 06:48
поделиться
  • 1
    Это было бы корректным решением Obj-C.:) – Jonathan Grynspan 6 June 2011 в 14:01

Существует два способа использования абстрактных базовых классов.

  1. Вы специализируете свой абстрактный объект, но все клиенты будут использовать производный класс через его базовый интерфейс.

  2. Вы используете абстрактный базовый класс для устранения дублирования внутри объектов в вашем проекте, а клиенты используют конкретные реализации через свои собственные интерфейсы.!


Решение для 1 - паттерн стратегии

Option1

Если у вас первая ситуация, то у вас фактически есть интерфейс, определенный виртуальными методами абстрактного класса, которые реализуют ваши производные классы.

Вы должны рассмотреть возможность сделать этот интерфейс реальным, изменить ваш абстрактный класс на конкретный и взять экземпляр этого интерфейса в его конструкторе. Тогда ваши производные классы станут реализациями этого нового интерфейса.

IMotor

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


Решение для 2

Если у вас вторая ситуация, то ваш абстрактный класс работает как класс-помощник.

AbstractHelper

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

Motor Helper

Это снова приводит к конкретным классам, которые просты и легко тестируются.


Как правило

Предпочитайте сложную сеть простых объектов простой сети сложных объектов.

Ключ к расширяемому тестируемому коду - небольшие строительные блоки и независимое подключение.


Обновлено: Как работать со смесями того и другого?

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

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


Обновление 2: Абстрактные классы как ступенька (2014/06/12)

На днях у меня была ситуация, когда я использовал абстрактные классы, поэтому я хотел бы выяснить, почему.

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

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

Я мог бы написать "SettingsFileParser", в который были бы обернуты все три класса, а затем делегировать базовому классу методы доступа к данным. Я решил не делать этого пока, поскольку это привело бы к появлению 3 производных классов с большим количеством делегирования кода в них, чем что-либо еще.

Однако... по мере развития этого кода и прояснения потребителей каждого из этих классов настроек. Каждый потребитель настроек будет запрашивать некоторые настройки и преобразовывать их определенным образом (поскольку настройки являются текстом, они могут обернуть их в объекты, преобразовать их в числа и т.д.). По мере того, как это будет происходить, я начну извлекать эту логику в методы манипулирования данными и переносить их обратно в сильно типизированные классы настроек. Это приведет к созданию интерфейса более высокого уровня для каждого набора настроек, который в конечном итоге перестанет осознавать, что имеет дело с "настройками".

В этот момент сильно типизированные классы настроек больше не будут нуждаться в методах "getter", которые раскрывают базовую реализацию 'settings'.

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

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

Я считаю, что это справедливо для любого правила... например, "никаких статических методов" или "никаких приватных методов". Они указывают на запах в коде... и это хорошо. Это заставляет вас искать абстракцию, которую вы упустили... и позволяет вам в это время продолжать предоставлять ценность вашему клиенту.

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

431
ответ дан 22 November 2019 в 23:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: