Я создал следующий шаблон тестовых приборов для проверки Equals
метода пользовательского класса.
class TestTemplate<X, Y>
{
public virtual void Reflexivity(X x)
{
bool isEqual = x.Equals(x);
Assert.IsTrue(isEqual);
}
public virtual void Symmetry(X x, Y y)
{
bool xy = x.Equals(y);
bool yx = y.Equals(x);
Assert.AreEqual(xy, yx);
}
public virtual void Transitivity(X x, Y y, Y z)
{
bool xy = x.Equals(y);
bool yz = y.Equals(z);
bool xz = x.Equals(z);
Assert.IsTrue(!(xy && yz) || xz);
}
public virtual void NullTest(X x)
{
bool isEqual = x.Equals(null);
Assert.IsFalse(isEqual);
}
public virtual void EqualTest(X x, Y equivalent)
{
bool isEqual = x.Equals(equivalent);
Assert.IsTrue(isEqual);
}
public virtual void InqualTest(X x, Y nonequivalent)
{
bool isEqual = x.Equals(nonequivalent);
Assert.IsFalse(isEqual);
}
}
Теперь я ищу лучший способ использования шаблона.
1 Производное тестовое приспособление
Возможно наследовать шаблон и предоставлять набор тестовых случаевиспользование TestCaseSourceAttribute
.
[TestFixture]
class ConcreteTest : TestTemplate<MyClass, MyClass>
{
[TestCaseSource(typeof(ConcreteTestSource), "ReflexivitySet")]
public override void Reflexivity(MyClass x) => base.Reflexivity(x);
[TestCaseSource(typeof(ConcreteTestSource), "EqualitySet")]
[TestCaseSource(typeof(ConcreteTestSource), "InequalitySet")]
public override void Symmetry(MyClass x, MyClass y) => base.Symmetry(x, y);
[TestCaseSource(typeof(ConcreteTestSource), "TransitivitySet")]
public override void Transitivity(MyClass x, MyClass y, MyClass z) => base.Transitivity(x, y, z);
[TestCaseSource(typeof(ConcreteTestSource), "ReflexivitySet")]
public override void NullTest(MyClass x) => base.NullTest(x);
[TestCaseSource(typeof(ConcreteTestSource), "EqualitySet")]
public override void EqualTest(MyClass x, MyClass equivalent) => base.EqualTest(x, equivalent);
[TestCaseSource(typeof(ConcreteTestSource), "InqualitySet")]
public override void InqualTest(MyClass x, MyClass nonequivalent) => base.InqualTest(x, nonequivalent);
}
Работает, но требует определения отдельного класса тестовых приборов для каждой реализации Equals
.
2 Использование одного класса приборов
Другой способ - применить TestCaseSourceAttribute
непосредственно к методам базовых приборов.
[TestFixture]
class EqualityTests
{
[TestCaseSource(typeof(Test1Source), "ReflexivitySet")]
[TestCaseSource(typeof(Test2Source), "ReflexivitySet")]
[TestCaseSource(typeof(Test3Source), "ReflexivitySet")]
public virtual void Reflexivity(object x)
{
bool isEqual = x.Equals(x);
Assert.IsTrue(isEqual);
}
// Symmetry, Transitivity, EqualTest, InqualTest and NullTest methods definition here
}
Полученный код более лаконичен, и он тоже работает. Но я хочу уменьшить код еще больше. Я верю, что можно указать источник тестового набора только один раз.
3 Попытка задать источник тестовых данных с помощью конструктора фикстур
На следующем шаге я попытался определить источник тестовых данных, передав тип источникаконструктору прибора через аргументы TestFixtureAttribute
. Идея состояла в том, чтобы сохранить тип, используя поле или свойство фикстуры, а затем использовать его в sourceType
аргументе TestCaseSourceAttribute
.
[TestFixture(typeof(Test1Source), TypeArgs = new Type[] { typeof(MyClass), typeof(MyClass) })]
[TestFixture(typeof(Test2Source), TypeArgs = new Type[] { typeof(ClassA), typeof(ClassA) })]
[TestFixture(typeof(Test3Source), TypeArgs = new Type[] { typeof(ClassA), typeof(ClassB) })]
class EqualityTests<X, Y>
{
private Type _sourceType;
public EqualityTests(Type sourceType)
{
_sourceType = sourceType;
}
[TestCaseSource(_sourceType, "SymmetrySet")]
public virtual void Symmetry(X x, Y y)
{
bool xy = x.Equals(y);
bool yx = y.Equals(x);
Assert.AreEqual(xy, yx);
}
// Reflexivity, Transitivity, EqualTest, InqualTest and NullTest methods definition here
}
Конечно, код не работает, потому что атрибут sourceType
из TestCaseSourceAttribute
требуется постоянное или typeof
выражение.
4 Попытка использовать параметризованный источник тестового набора
Затем я попытался использовать промежуточный параметризованный источник, который выбирает конкретный источник и получает тестовые примеры изодин. Это похоже на предыдущее решение, но использует methodParams
аргумент TestCaseSourceAttribute
.
[TestFixture(typeof(Test1Source), TypeArgs = new Type[] { typeof(MyClass), typeof(MyClass) })]
[TestFixture(typeof(Test2Source), TypeArgs = new Type[] { typeof(ClassA), typeof(ClassA) })]
[TestFixture(typeof(Test3Source), TypeArgs = new Type[] { typeof(ClassA), typeof(ClassB) })]
class EqualityTests<X, Y>
{
private Type _sourceType;
public EqualityTests(Type sourceType)
{
_sourceType = sourceType;
}
[TestCaseSource("GetSymmetryTestSet", new object[] { _sourceType })]
public virtual void Symmetry(X x, Y y)
{
bool xy = x.Equals(y);
bool yx = y.Equals(x);
Assert.AreEqual(xy, yx);
}
public static IEnumerable<TestCaseData> GetSymmetryTestSet(Type sourceType)
{
// return Symmetry member value of the sourceType
}
// Reflexivity, Transitivity, EqualTest, InqualTest and NullTest methods definition here
}
Но решение также не работает по той же причине: невозможно использовать нестатический элемент прибораэкземпляр в methodParams
аргументе TestCaseSourceAttribute
.
Вопрос
Итак, я хочу улучшить возможность повторного использования и масштабируемости рабочего решения (2). Он не должен требовать определения нового прибора для каждой реализации Equals
или указывать один и тот же источник тестового примера в нескольких местах. Я надеюсь, что это возможно, но я застрял, чтобы реализовать это. Пожалуйста, дайте мне знать, если у вас есть предложение или опыт для решения проблемы.