Использование Moq для проверки выполнения приватных методов
Я хочу проверить следующую логику (это, очевидно, урезанная версия моего метода):
public void myPublicMethod(params) {
if(some_condition)
privateMethod1();
else
privateMethod2();
}
У меня отключены все другие зависимости в методе, и я настроил это так, чтобы я мог гарантировать, что some_condition имеет значение true. Я хочу убедиться, что мой privateMethod1() вызывается ровно один раз, а privateMethod2() вообще не вызывается. Это возможно сделать с Moq?
Вот несколько заметок по этому вопросу:
- privateMethod1() и privateMethod2() находятся в одном классе с myPublicMethod, поэтому я не могу создать фиктивный объект для этого класса.
- Тела privateMethod1/2 содержат много, много зависимостей от класса, который содержит их и myPublicMethod, поэтому разбиение privateMethod1/2 на их собственный вспомогательный класс будет непомерно трудоемким
Какие-нибудь мысли? Заранее спасибо. Я готов признать, что это невозможно, но я хотел бы узнать так или иначе.
2 ответа
Не проверяйте частные методы. Они являются частными деталями реализации класса. Вам следует только проверить результаты выполнения открытых методов. Пока ваши результаты получаются, как и ожидалось, вам все равно, как результат получен.
Создание тестов для частных методов приведет к хрупким тестам, которые легко ломаются при рефакторинге частных реализаций (по причинам производительности или по другим причинам).
В вашем классе есть два закрытых служебных метода, которые инкапсулируют некоторое полезное поведение, и открытый метод, который вы тестируете, должен использовать это поведение. Однако, когда вы тестируете, вы не хотите нормального поведения от этих методов, вы хотите заменить тестовое поведение. То, что у вас есть, это классический случай зависимости. При тестировании зависимости внутри класса могут быть проблематичными.
Таким образом, решение такое же, как и для внешней зависимости: используйте внедрение зависимости того или иного вида, чтобы отделить метод, который вы хотите проверить, от частных методов, которые реализуют поведение. Например, два частных делегата могут быть объявлены для представления поведения:
private Action Behavior1;
private Action Behavior2;
В конструкторе класса нормальное поведение реализовано так:
public Foo (...)
{
Behavior1 = privateMethod1;
Behavior2 = privateMethod2;
...
}
В публичном методе делегат вызывается вместо фактического метода:
public void myPublicMethod(params) {
if(some_condition)
Behavior1();
else
Behavior2();
}
Таким образом, абсолютная зависимость между методами была устранена, поэтому теперь она может быть проверена.
Итак, теперь в тесте после создания экземпляра тестового объекта вы можете переопределить зависимое поведение:
Foo_Accessor testMe = new Foo_Accessor();
bool wasCalled1 = false
testMe.Behavior1 = new Action(() => wasCalled1 = true);
...
Assert.IsTrue(wasCalled1);