#c# #unit-testing #generics #testing #chaining
#c# #модульное тестирование #общие #тестирование #цепочка
Вопрос:
Каков был бы правильный способ модульного тестирования абстрактного класса, который принимает общий параметр типа, где тип также является абстрактным классом? Например)
public abstract class BaseClass<T> where T : BaseT { ... }
Мне нужно будет создать тестовый класс, который расширяет BaseT, верно??
и что произойдет, если базовый класс поддерживает цепочку методов, такую, что
public abstract class BaseClass<T> where T : BaseClass<T> { ... } ??
Спасибо.
обновленный пример конкретного класса для 2-го случая:
public class ConcreteClass : BaseClass<ConcreteClass>
{
public ConcreteClass Method1()
{
return this as ConcreteClass;
}
}
Ответ №1:
Вы можете сделать это без реализации фиктивного класса реализации, используя Rhino Mocks: http://www.ayende.com/wiki/Rhino Mocks Partial Mocks.ashx
К сожалению, Rhino Mocks требует, чтобы вы использовали синтаксис записи / воспроизведения с частичными mocks вместо синтаксиса AAA.
Учитывая следующие абстрактные классы:
public abstract class BaseAbstract
{
public abstract int Increment();
}
public abstract class AbstractClass<T> where T : BaseAbstract
{
private int inc;
public abstract T Abstract();
public virtual int Concrete()
{
return inc = Abstract().Increment();
}
}
Вы можете протестировать реализацию Concrete следующим образом:
[Test]
public void mock_an_abstract_class_with_generic()
{
var mocks = new MockRepository();
var baseAbstract = mocks.StrictMock<BaseAbstract>();
var abstractClass = mocks.PartialMock<AbstractClass<BaseAbstract>>();
using (mocks.Record())
{
baseAbstract.Stub(a => a.Increment()).Return(5);
abstractClass.Stub(a => a.Abstract()).Return(baseAbstract);
}
using (mocks.Playback())
{
abstractClass.Concrete().ShouldEqual(5);
abstractClass.Concrete().ShouldEqual(10);
}
}
По сути, вы создаете частичный макет для базового класса, устанавливаете ожидания для абстрактных методов, затем вызываете тестируемый конкретный метод. Общий — это просто еще один макет, еще один частичный, если необходимо.
Недостатком этого, очевидно, является требование к синтаксису записи / воспроизведения. Я не знаю, могут ли другие фреймворки-имитаторы помочь вам здесь больше, но в целом я обнаружил, что Rhino Mocks — это фреймворк-имитатор, к которому вы обращаетесь, если у вас есть расширенный вариант использования, подобный этому.
Комментарии:
1. Я понимаю, о чем вы говорите — да, это намного сложнее. Я собираюсь разобраться с этим сейчас, но я чувствую, что вы застряли, создавая фиктивный конкретный класс для тестирования.
2. Я только что понял, можете ли вы привести пример конкретной реализации общедоступного абстрактного класса BaseClass<T>, где T : BaseClass<T>? Если я не буду слишком плотным, я не думаю, что возможно предоставить конкретную реализацию этой сигнатуры класса.
3. Я обновил пример, включив в него общие элементы. Я, наконец, понял, что вы имеете в виду под вторым примером (public class ImplementingClass : BaseClass<ImplementingClass>). Я собираюсь поработать с этим, когда у меня будет больше свободного времени, но, надеюсь, это по сути то же самое решение.
4. Я ни за что на свете не могу понять, как издеваться над общим ограничением, связывающим методы. Я даже попробовал погрузиться в тяжелое размышление, и это просто оказалось слишком сложным. Я думаю, что для этой структуры вы застряли на реализации фиктивного класса. Это проще, чем все остальное, что я смог придумать. Может быть, кто-то другой придумает умный способ облегчить это.
5. большое спасибо mrdowns, прямо сейчас я использую фреймворк Moq. Похоже, я застряну, создав фиктивный конкретный класс для 2-го случая. В любом случае, обновленный пример выше..
Ответ №2:
Не зная более подробной информации, я предполагаю, что вы пытаетесь протестировать какой-то код, который реализован в абстрактном универсальном классе. Например, вы можете протестировать общедоступный метод или свойство, которое реализовано в базовом классе и не помечено как абстрактное.
Итак, вам нужно будет создать класс, расширяющий общий базовый класс BaseClass< T>, и другой класс, расширяющий BaseT. Давайте назовем эти ConcreteClass< T> и ConcreteT соответственно.
Затем создайте экземпляр класса типа ConcreteClass<конкретный класс>. Теперь вы можете запускать тесты на этом экземпляре, что позволяет протестировать логику абстрактного класса.
Ответ №3:
Нет способа протестировать абстрактный класс, вы не можете создать экземпляр 🙂
Комментарии:
1. Ну, вы можете создать вспомогательный класс, который наследует его. Имея это в виду, у вас есть реальный ответ для OP, или это просто комментарий?
2. Это не будет одно и то же, не так ли? Если он абстрактный, у него есть некоторые абстрактные методы, поэтому требуется реализация. Я не верю, что есть способ…
3. Без параметров общего типа его можно протестировать, имитируя их.
4. Издеваетесь над тем, что вы пытаетесь протестировать?