Подход Unit test для общих классов/методов

Каков рекомендуемый способ покрытия модульных тестов общих классов/методов?

Например (ссылаясь на мой примерный код ниже). Будет ли это случай, когда у вас в 2 или 3 раза больше тестов для тестирования методов с несколькими типами классов TKey, TNode? Или достаточно одного класса?

public class TopologyBase<TKey, TNode, TRelationship> 
    where TNode : NodeBase<TKey>, new() 
    where TRelationship : RelationshipBase<TKey>, new()

{
    // Properties
    public Dictionary<TKey, NodeBase<TKey>> Nodes { get; private set; }
    public List<RelationshipBase<TKey>> Relationships { get; private set; }

    // Constructors
    protected TopologyBase()
    {
        Nodes = new Dictionary<TKey, NodeBase<TKey>>();
        Relationships = new List<RelationshipBase<TKey>>();
    }

    // Methods
    public TNode CreateNode(TKey key)
    {
        var node = new TNode {Key = key};
        Nodes.Add(node.Key, node);
        return node;
    }

    public void CreateRelationship(NodeBase<TKey> parent, NodeBase<TKey> child) {
    .
    .
    .

Ответ 1

Обычно я создаю DummyClass для тестирования в качестве общего аргумента (в вашем случае вы должны создать 3 класса), и я тестирую класс (TopologyBase) один раз.

Тестирование с использованием разных типов не имеет смысла, поскольку общий тип не должен разбивать класс ToopologyBase.

Ответ 2

Это действительно зависит от вашего кода, но есть о чем подумать, по крайней мере, две вещи:

  • Частное/публичное: если ваша реализация использует или может когда-нибудь использовать отражение или DLR (прямо или через динамическое ключевое слово на С#) для некоторых операций, вы должны протестировать, по крайней мере, с одним типом, который не отображается из внедряющей сборки.
  • ValueType/ReferenceType: различие между ними может потребоваться, например, в некоторых случаях
    • Метод Calling Object для типов значений, например ToString или Equals, всегда правильный, но не со ссылками, поскольку они могут быть нулевыми.
    • Если вы выполняете тестирование производительности, могут возникнуть последствия для прохождения типов значений, особенно если они большие (не должно быть, но между рекомендациями и реальностью иногда есть небольшой пробел...)

Ответ 3

Вы можете использовать фальшивую фреймворк для проверки ожидаемого взаимодействия между вашим классом и родовым типом. Я использую Rhino Mocks для этого.

Ответ 4

В unit test открытый тип производства создайте тип тестового кода, который выводится из открытого типа - затем проверьте этот тип.

public class TestingTopologyBase : TopologyBase<KeyType, NodeType, RelationshipType> ...

В классе TestingTopologyBase укажите базовую реализацию любых абстрактных методов или чего-либо еще, что является обязательным.

Эти реализации Testing [ProductionType] часто являются отличными местами для вашего кода unit test, чтобы понять, что действительно делает типичный тип теста. Например, вы можете хранить информацию, которая впоследствии может использоваться вашим кодом unit test, чтобы проверить, что произошло во время теста.

Затем в ваших методах unit test создайте экземпляры класса TestingTopologyBase. Таким образом, вы проверяете общий тип в изоляции от любых типов производства, которые вытекают из него.

Пример:

[TestClass]
public class TopologyBaseFixture {

    [TestMethod]
    public void SomeTestMethod() {
       var foo = new TestingTopologyBase(...);
       ...test foo here

Ответ 5

Многое зависит от ваших общих ограничений. Если для одного или нескольких параметров типа требуется ограничение интерфейса или базового класса, теперь существует зависимость от контракта интерфейса. Так как логика вашего класса может частично зависеть от поведения класса, реализующего интерфейс, вам может потребоваться несколько раз издеваться над интерфейсом для реализации всех ваших логических путей. Например, если у вас есть T: IEquatable<T>, вам нужно будет использовать тип со значимым поведением равенства, например, int.