Определение TestMethod в базовых классах теста не поддерживается MsTest?

Этот вопрос касается общей техники модульного тестирования с потенциально очень полезным широким спектром применимых сценариев. Но это легче понять на примере, чтобы лучше проиллюстрировать мой вопрос.

Допустим, я хочу проверить, что все типы, которые переопределяют Equals () , делают это правильно. Поскольку Equals () определен как виртуальный в System.Object , это поведение может быть изменено широким диапазоном типов. Каждый тип, который это делает, должен иметь тесты, чтобы убедиться, что новое поведение соответствует неявным ожиданиям вызывающего этого метода. В частности, для Equals () , если вы переопределите этот метод, новая реализация должна убедиться, что два равных объекта также имеют одинаковые хэш-коды, как определено в System.Object.GetHashCode () .

Таким образом, чтобы обеспечить это, потребуется несколько тестовых классов, и все они будут проверять одинаковую согласованность поведения для всех этих типов.

Чтобы избежать повторного ввода всех TestMethods, необходимых для тестирования такого типа, я вместо этого определяю базовый тестовый класс, который выглядит, как показано ниже, и все эти тестовые классы наследуют один и тот же набор тестов поведения:

/// <summary>
/// Test fixture base class for testing types that overrides Object.Equals()
/// </summary>
/// <typeparam name="T">The production type under test</typeparam>
public abstract class EqualsFixtureBase<T>
{
    #region Equals tests

    protected static void CompareInstances(T inst1, T inst2, bool expectedEquals)
    {
        Assert.AreEqual(expectedEquals, inst1.Equals((T)inst2));
        Assert.AreEqual(expectedEquals, inst1.Equals((object)inst2));
        if (expectedEquals)
        {
            // equal instances MUST have identical hash codes
            // this is a part of the .NET Equals contract
            Assert.AreEqual(inst1.GetHashCode(), inst2.GetHashCode());
        }
        else
        {
            if (inst2 != null)
            {
                Assert.AreNotEqual(inst1.GetHashCode(), inst2.GetHashCode());
            }
        }
    }

    /// <summary>
    /// Creates version 1 instance of the type under test, not 'Equal' to instance 2.
    /// </summary>
    /// <returns>An instance created with properties 1.</returns>
    protected abstract T CreateInstance1();

    /// <summary>
    /// Creates version 2 instance of the type under test, not 'Equal' to instance 1.
    /// </summary>
    /// <returns>An instance created with properties 2.</returns>
    protected abstract T CreateInstance2();

    /// <summary>
    /// Creates an instance equal to the version 1 instance, but not the identical
    /// same object.
    /// </summary>
    /// <returns>An instance created with properties equal to instance 1.</returns>
    protected abstract T CreateInstanceThatEqualsInstance1();

    [TestMethod]
    public void Equals_NullOrDefaultValueTypeInstance()
    {
        T instance = CreateInstance1();
        CompareInstances(instance, default(T), false);
    }

    [TestMethod]
    public void Equals_InstanceOfAnotherType()
    {
        T instance = CreateInstance1();
        Assert.IsFalse(instance.Equals(new object()));
    }

    [TestMethod]
    public void Equals_SameInstance()
    {
        T slot1 = CreateInstance1();
        CompareInstances(slot1, slot1, true);
    }

    [TestMethod]
    public void Equals_EqualInstances()
    {
        T slot1 = CreateInstance1();
        T slot2 = CreateInstanceThatEqualsInstance1();
        CompareInstances(slot1, slot2, true);
        CompareInstances(slot2, slot1, true);
    }

    [TestMethod]
    public void Equals_NonEqualInstances()
    {
        T slot1 = CreateInstance1();
        T slot2 = CreateInstance2();
        CompareInstances(slot1, slot2, false);
        CompareInstances(slot2, slot1, false);
    }

    #endregion Equals tests
}

Затем я могу повторно используйте эти TestMethods для каждого типа, переопределяя Equals ().Например, это будет определение тестового класса для проверки того, что тип System.String правильно реализует Equals () .

[TestClass]
public class ExampleOfAnEqualsTestFixture : EqualsFixtureBase<string>
{
    [TestMethod]
    public void Foo()
    {
        Assert.IsTrue(true);
    }

    protected override string CreateInstance1()
    {
        return "FirstString";
    }

    protected override string CreateInstance2()
    {
        return "SecondString";
    }

    protected override string CreateInstanceThatEqualsInstance1()
    {
        return "FirstString";
    }
}

Это также может быть расширено. Например, для типов, которые перегружают операторы == и! =, Может быть определен второй базовый класс абстрактного теста (например, EqualsOperatorsFixtureBase : EqualsFixtureBase ), который проверяет, что реализация этих операторов не только верны, но и согласуются с расширенными определениями Equals () и GetHashCode () .

Я могу сделать это с помощью NUnit, но при использовании MsTest возникают проблемы.

a) Visual Studio 2010 обнаруживает только тестовый метод Foo () , но не унаследованные тестовые методы, поэтому он не может их запустить. Кажется, что загрузчик тестов Visual Studio не проходит иерархию наследования тестового класса.

б) Когда я регистрирую эти типы в TFS, TFS находит абстрактный тип EqualsFixtureBase и считает, что это тестовый класс, который нужно запустить. Но поскольку он не может быть создан, он не может его запустить и помечает тесты этого типа как неубедительные, что не позволяет выполнить тестовый прогон и, следовательно, сборку (!).

Есть ли способ обойти это, или это ограничение MsTest и Visual Studio?

Если да, исправляет ли это в дорожной карте для VS / TFS ??

Это было бы очень полезно, особенно при тестировании производственных типов, которые реализуют интерфейс или являются частью иерархии наследования, где определенные элементы имеют семантические свойства или инварианты типа контракта - если это имеет смысл.

По сути, отсутствие поддержки этого не позволяет мне провести рефакторинг моего тестового кода, чтобы удалить дублирование.

Спасибо

РЕДАКТИРОВАТЬ: Я нашел эту ссылку на один из блогов MSDN, в ней говорится следующее

«В Whidbey поддержка наследования тестовых классов отсутствовала. В Nunit это полностью поддерживается. Это будет исправлено в косатках »

. Это было написано более трех лет назад. Почему этого еще не добавили? Я не понимаю, для этого есть законные причины, и, на мой взгляд, это будет незначительное изменение. Или я просто не прыгаю по правильному пути?

26
задан Mahol25 29 August 2010 в 14:27
поделиться