Mah*_*l25 26 unit-testing mstest
这个问题涉及一般的单元测试技术,可能具有非常有用的广泛适用场景.但通过一个例子更容易理解,更好地说明我的问题.
假设我想测试所有覆盖的类型都是Equals()如此正确.由于Equals()被定义为虚拟输入System.Object,因此各种类型可能会改变该行为.执行此操作的每种类型都必须进行测试,以确保新行为遵循该方法调用者的隐含期望.特别是Equals(),如果您重写该方法,新实现必须确保两个相等的对象也具有相同的哈希码,如下所定义System.Object.GetHashCode().
因此,为了强制执行此操作,将需要多个测试类,并且它们将在所有这些类型中测试相同的行为一致性.
为了避免重新键入测试这种类型所需的所有TestMethod,我改为定义一个如下所示的基本测试类,并让这些测试类都继承相同的行为测试套件:
/// <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
}
Run Code Online (Sandbox Code Playgroud)
然后,我可以为重写Equals()的每种类型重用这些TestMethod.例如,这将是测试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";
}
}
Run Code Online (Sandbox Code Playgroud)
这也可以进一步扩展.举例来说,对于重载==类型和!=运营商,第二抽象测试基类可以被定义(即EqualsOperatorsFixtureBase<T> : EqualsFixtureBase<T>),测试这些操作符的执行不仅正确的,但也与扩展定义一致Equals()和GetHashCode().
我可以使用NUnit来做到这一点,但是当使用MsTest时我会遇到问题.
a)Visual Studio 2010仅发现Foo()测试方法,而不是继承的测试方法,因此无法运行它们.似乎Visual Studio测试加载器不会遍历测试类的继承层次结构.
b)当我在TFS中检入这些类型时,TFS找到抽象的EqualsFixtureBase类型并认为它是要运行的测试类.但由于无法创建它,它无法运行它并将该类型的测试标记为不确定 - 这使测试运行失败,从而使构建(!)失败.
有没有办法绕过这个,或者这是MsTest和Visual Studio的限制?
如果是这样,在VS/TFS的路线图中修复这个问题?
这将非常有用,特别是在测试实现接口的生产类型时,或者是继承层次结构的一部分,其中某些成员具有语义"契约类型"属性或不变量 - 如果这是有意义的.
基本上,没有对此的支持会阻止我重构我的测试代码以消除重复.
谢谢
编辑:我发现这个链接到其中一个MSDN博客,它说如下
"在Whidbey中,缺少对测试类继承的支持.在Nunit中,它得到了完全支持.这将在Orcas中得到纠正."
这是三年前写的.为什么还没有添加?我没有得到它,有合理的理由有这个,在我看来这将是一个小的改变.或者我只是不在这里跳右箍?
Jac*_*ble 22
使用VS 2010我没有看到与您相同的行为.当我将2个类复制到测试项目并编译它时,我得到了输出:
UTA004: Illegal use of attribute...The TestMethodAttribute can be
defined only inside a class marked with the TestClass attribute
Run Code Online (Sandbox Code Playgroud)
所以我标记了EqualsFixutureBase:
[TestClass]
public abstract class EqualsFixtureBase<T>
{
...
}
Run Code Online (Sandbox Code Playgroud)
现在它编译时没有警告,当我为ExampleOfAnEqualsTestFixture选择运行测试时,它运行Foo和所有5个继承的equals测试.此外,当我复制ExampleOfAnEqualsTestFixture并将其用于int并运行解决方案的测试时,我看到所有5个继承的测试运行(并传递)示例字符串类和示例int类.
除了您的示例之外,您是否正在做一些可能导致问题的事情?
Mic*_*ick 10
该TestClassAttribute允许你把方法的抽象基.该IgnoreAttribute排除从测试列表的基类.如果没有IgnoreAttribute属性,则对基类和使用TestClassAttribute标记的子类执行base中的方法.
[TestClass][Ignore]
public abstract class EqualsFixtureBase<T>
{
....
Run Code Online (Sandbox Code Playgroud)
开箱即用,看起来单元测试继承仅在基本测试类与派生类在同一程序集中时才有效.对我来说,这通常会违背拥有基类的目的.我也想知道为什么在博客上没有更多关于这个的帖子,如果我可能遗漏了什么.
您可以通过将基类链接到要使用它的每个项目来解决此问题.也许将其标记为内部,因此多个副本不会相互干扰.
另外还有一个TestClassExtensionAttribute,你可以延伸到挂钩到测试执行引擎.我尝试使用它来反映测试类并加载基类的测试,但很多类都没有记录,我无法让它工作.