Чтобы ответить на ваш вопрос - вы бы использовали фальшивый фреймворк, такой как Moq.
Общая идея состоит в том, что Интерфейсы или абстрактные классы предоставляют "контракты" или набор стандартизированных API, которые вы можете кодировать.*
Реализация этих интерфейсов или абстрактных классов может тестироваться отдельно.Это не проблема, и на самом деле - это то, что вы должны делать на регулярной основе.
Однако сложность возникает, когда эти реализации являются зависимостями других объектов.В связи с этим - для модульного тестирования такого сложного объекта сначала необходимо создать реализацию зависимости, включить эту зависимость в экземпляр того, что вы тестируете.
Этот процесс становится довольно обременительным, потому что по мере роста цепочки зависимостей изменчивость поведения кода может быть довольно сложной.Чтобы упростить тесты, а также иметь возможность модульного тестирования нескольких условий в сложных цепочках зависимостей - мы используем макеты.
То, что предоставляет макет, - это способ «подделать» реализацию с конкретными параметрами (ввод / вывод).какими бы они ни были) и вставьте эти фальшивки в граф зависимостей.И хотя да - вы можете смоделировать конкретные объекты - гораздо проще смоделировать контракты, определенные интерфейсом или абстрактным классом.
Достойной отправной точкой для понимания этих концепций является документация по инфраструктуре moq.https://github.com/Moq/moq4/wiki/Quickstart
Редактировать:
Я вижу, что существует путаница в том, что это значит, поэтому я хотел уточнить.
Общие шаблоны проектирования (известные как SOLID) диктуют, чтообъект должен делать 1 вещь и только 1 вещь и делать это хорошо.Это известно как принцип единой ответственности.
Другая основная концепция заключается в том, что объект должен зависеть от абстракций, а не от конкретных реализаций.Эта концепция известна как принцип обращения зависимостей.
Наконец - принцип подстановки Лискова, который гласит, что объект в программе должен быть заменен экземплярами их подтипов без изменения правильности программы.Другими словами - если ваши объекты зависят от абстракций, то вы можете обеспечить различные реализации (используя преимущества наследования) для этих абстракций без существенного изменения поведения приложения.
, которое также аккуратно переходит в Open / Closedпринцип.IE - программные объекты должны быть открыты для расширения, но закрыты для модификации.(Подумайте о предоставлении различных реализаций для этих абстракций.)
Наконец - у нас есть принцип инверсии управления - сложный объект не должен отвечать за создание своих собственных зависимостей;что-то еще должно отвечать за их создание, и они должны быть «внедрены» через конструктор, метод или внедрение свойства, где бы они ни были нужны.
Так как это применимо в «отделении системного дизайна» отмодульные тесты?
Ответ очень прост.
Предположим, мы пишем программное обеспечение, которое моделирует автомобили.Автомобиль имеет кузов и колеса, а также всевозможные другие внутренние компоненты.Для простоты скажем, что объект типа Car
имеет конструктор, который принимает четыре wheel
объекта в качестве параметров:
public class Wheel {
public double Radius { get; set; }
public double RPM { get; set; }
public void Spin(){ ... }
public double GetLinearVelocity() { ... }
}
public class LinearMovement{
public double Velocity { get; set; }
}
public class Car {
private Wheel wheelOne;
private Wheel wheelTwo;
private Wheel wheelThree;
private Wheel wheelFour;
public Car(Wheel one, Wheel two, Wheel three, Wheel four){
wheelOne = one;
wheelTwo = two;
wheelThree = three;
wheelFour = four;
}
public LinearMovement Move(){
wheelOne.Spin();
wheelTwo.Spin();
wheelThree.Spin();
wheelFour.Spin();
speedOne = wheelOne.GetLinearVelocity();
speedTwo = wheelTwo.GetLinearVelocity();
speedThree = wheelThree.GetLinearVelocity();
speedFour = wheelFour.GetLinearVelocity();
return new LinearMovement(){
Velocity = (speedOne + speedTwo + speedThree + speedFour) / 4
};
}
}
Способность автомобиля двигаться определяется типом колес автомобиля.есть.Колесо может иметь мягкую резину, тем самым приклеивая автомобиль к дороге по поворотам, или оно может быть очень узким для глубокого снега, но очень медленных скоростей.
Поэтому - идея колеса становится абстракцией.Есть все виды колес, и конкретная реализация колеса не может охватить все из них.Введите принцип инверсии зависимости.
Мы делаем колесо абстракцией, используя интерфейс IWheel
, чтобы объявить основные минимальные функциональные возможности для того, что любое колесо должно быть способным для работы с нашим автомобилем.(В нашем случае оно должно вращаться как минимум ...)
public interface IWheel {
double Radius { get; set; }
double RPM { get; set; }
void Spin();
double GetLinearVelocity();
}
public class BasicWheel : IWheel {
public double Radius { get; set; }
public double RPM { get; set; }
public void Spin(){ ... }
public double GetLinearVelocity() { ... }
}
public class Car {
...
public Car(IWheel one, IWheel two, IWheel three, IWheel four){
...
}
public LinearMovement Move(){
wheelOne.Spin();
wheelTwo.Spin();
wheelThree.Spin();
wheelFour.Spin();
speedOne = wheelOne.GetLinearVelocity();
speedTwo = wheelTwo.GetLinearVelocity();
speedThree = wheelThree.GetLinearVelocity();
speedFour = wheelFour.GetLinearVelocity();
return new LinearMovement(){
Velocity = (speedOne + speedTwo + speedThree + speedFour) / 4
};
}
}
Так что это здорово, у нас есть абстракция для определения базовой функциональности колеса, и мы закодировали машину против этой абстракции.Ничего не изменилось в коде того, как машина движется - тем самым удовлетворяя принципу замены Лискова.
Так что теперь, если вместо создания автомобиля с базовыми колесами мы создадим автомобиль с помощью RacingPerformanceWheels, код, управляющий движением автомобиля, останется прежним.Это удовлетворяет принципу Open / Closed.
Однако - это создает другую проблему.Фактическая скорость автомобиля - зависит от средней линейной скорости всех 4 колес.Таким образом, в зависимости от колеса - автомобиль будет вести себя по-разному.
Как мы можем проверить поведение автомобиля, учитывая, что там может быть миллион различных типов колес?!?
Введите рамки для насмешек.Поскольку движение автомобиля зависит от абстрактного понятия колеса, определенного в интерфейсе IWheel
, теперь мы можем смоделировать различные варианты реализации такого колеса, каждое из которых имеет предопределенные параметры.
Сами бетонные реализации / объекты (BasicWheel
, RacingPerformanceWheel
и т. д.) должны проходить модульное тестирование без макетов. Причина в том, что они не имеют собственных зависимостей.Если колесо имеет зависимость в своем конструкторе - тогда для этой зависимости следует использовать mocking.
Для проверки объекта car - mock следует использовать для описания каждого экземпляра IWheel
(зависимость), который передается конструктору автомобиля. Это дает пару преимуществ - отсоединение общей конструкции системы от юнит-тестов:
1) Нам все равно, какие есть колесав системе.Их может быть 1 миллион.
2) Мы заботимся о том, чтобы для конкретных размеров колес при заданной угловой скорости (об / мин) автомобиль достигал очень определенной линейной скорости.
Макет IWheel
для требований№ 2 скажет нам, если наш автомобиль работает нормально, а если нет - мы могли бы изменить наш код, чтобы исправить ошибку.