我创建了以下测试夹具模板,以测试自定义类的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);
}
}
现在,我正在寻找使用模板的最佳方法。
可以继承模板并使用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
实现定义一个单独的测试治具类。
另一种方法是直接将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
}
生成的代码更加简洁,也可以使用。但是我想进一步减少代码。我相信可以只指定一次测试用例源。
在下一步中,我尝试通过TestFixtureAttribute
的参数将源类型传递给夹具构造器来标识测试数据源。想法是使用Fixture字段或属性存储类型,然后在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
表达式。
然后,我尝试使用中间参数化来源,该来源选择具体来源并从其中获取测试用例。它与先前的解决方案类似,但是使用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
实现定义一个新的夹具,也不必在几个地方指定相同的测试用例源。我希望有可能,但是我坚持要实现它。请让我知道您是否有解决问题的建议或建议。
[Test]
public void Test1() {
var x = new SomeType1()
AssertReflexivity(x);
}
[Test]
public void Test2() {
var x = new SomeType1();
var y = new SomeType2();
AssertReflexivity(x, y);
}
几点如果大约是同一类型您可以在课堂上使用一种类型
class TestHelpers { public static void AssertReflexivity<T>(T x) { bool isEqual = x.Equals(x); Assert.IsTrue("Add a good message here, otherwise test output may be hard to read"); } public static AssertSymmetry<T>(T x, T y)
- 如果您比较两个对象的所有属性都设置为null怎么办?他们反身吗?
如果这涉及不同类型
- 它取决于
Equals
是如何被覆盖/实现的-这些测试在许多与类型之间的关系无关,而与数据设置没有太大关系的不同情况下可以通过/失败。反身应该使用不同的类型吗?
如果您比较两个对象的所有属性都设置为null怎么办?他们反身吗?