модульное тестирование .net с общим параметром абстрактного типа и цепочкой

#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. Издеваетесь над тем, что вы пытаетесь протестировать?