从使用泛型的类型继承的单元测试

时间:2009-07-16 14:29:13

标签: c# unit-testing generics

我有一个由一些类实现的泛型接口。对于这些类中的每一个,都有代理类来实现接口。大致给出这段代码:

public interface ISomeInterface<T>
{
    T SomeProperty
    {
        get;
    }

    T SomeAction();
}

public interface IClassA : ISomeInterface<string>
{
    void Action();
}


public class ClassA : IClassA
{
    // Code goes here
}

public class ClassAProxy : IClassA
{
    // Code goes here
}

单元测试代码我希望看起来像这样:

public abstract class ISomeInterfaceTests<T>
{
    [TestMethod()]
    public void SomePropertyTest()
    {
        ISomeInterface<T> target;
        ISomeInterface<T> oracle;
        this.CreateInstance(out target, out oracle);

        Assert.AreEqual(oracle.SomeProperty, target.SomeProperty);
    }

    [TestMethod()]
    public void SomeActionTest()
    {
        ISomeInterface<T> target;
        ISomeInterface<T> oracle;
        this.CreateInstance(out target, out oracle);

        T oracleValue = oracle.SomeAction();
        T targetValue = target.SomeAction();

        Assert.AreEqual(oracleValue, targetValue);
    }

    // More tests

    protected abstract void CreateInstance(out ISomeInterface<T> target, out ISomeInterface<T> oracle);
}

[TestClass()]
public class ClassAProxyTests : ISomeInterfaceTests<string>
{
    // ClassAProxy specific tests

    protected override void CreateInstance(out ISomeInterface<string> target, out ISomeInterface<string> oracle)
    {
        // Create target as ClassAProxy here and oracle as ClassA
    }
}

但是这给出了错误: UTA002:无法在泛型类ISomeInterfaceTests&lt; T&gt;上定义TestClass属性。

这有什么好的解决方法吗?目前我能想到的最好的解决方案是在ClassAProxyTests中使用一个方法来调用ISomeInterfaceTests&lt; T&gt;中的不同测试方法。但是这种方法有几个问题:

  • 必须手动完成实施ISomeInterfaceTests&lt; T&gt;的每个测试。
  • 如果一个方法导致断言失败,则其余方法将不会被执行。
  • 您不能使用ExpectedException属性,并且必须在try catch语句中包装所需的代码。

但唉,一个更好的解决办法让我感到安心。

2 个答案:

答案 0 :(得分:1)

听起来您需要使用GenericTestFixture 2.5的NUnit功能。此功能允许您将[TestFixture]属性放在泛型类上,然后指定测试夹具的哪些特化适用。

您的主要测试夹具将如下所示(您也可以删除一些接口):

[TestFixture(typeof(string))]
public class ClassAProxyTests<T> : ISomeInterfaceTests<T> where T: class
{
    // Add ISomeInterfaceTests<T> methods here.
    // ISomeInterfaceTests may no longer be required as the abstraction is defined in ClassAProxyTests.

    // ClassAProxy specific tests

    protected override void CreateInstance(out ISomeInterface<T> target, out ISomeInterface<string> oracle)
    {
        // Create target as ClassAProxy here and oracle as ClassA
    }
}

答案 1 :(得分:0)

你拥有父类的唯一原因是创建你的界面实例,是吗?

请改用MOQ

修改:

  

不完全。 ISomeInterfaceTests&lt; T&gt; class用于测试实现ISomeInterface&lt; T&gt;的代理的功能。 interface - 它根据代理对象(oracle)来测试代理。 - Cornelius

嗯......这听起来像是一个集成测试。你的oracle对象是以某种方式从数据库中读取的,还是你嘲笑它们就像你应该的那样?无论如何,我一直使用Moq来获取你的实例和模拟与集成测试。

但令我感到震惊的是,您的测试一般化方法可能就是问题所在。也许是另一种方法?试试这个:

public static class GenericISomeInterfaceTests
{
    public static void SomePropertyTest<T>(ISomeInterface<T> target, ISomeInterface<T> oracle)
    {

        Assert.AreEqual(oracle.SomeProperty, target.SomeProperty);
    }

    public static void SomeActionTest<T>(ISomeInterface<T> target, ISomeInterface<T> oracle)
    {

        T oracleValue = oracle.SomeAction();
        T targetValue = target.SomeAction();

        Assert.AreEqual(oracleValue, targetValue);
    }

    // More tests
}

[TestClass()]
public class ClassAProxyTests
{
    [TestMethod]
    public void SomePropertyStringTest()
    {
        // set up instances (using MOQ, or whatever) with the string generic type. 
        // Call them target and oracle

        // then call your generic test methods
        GenericISomeInterfaceTests.SomePropertyTest<string>(target, oracle);
    }

    [TestMethod]
    public void SomeActionStringTest()
    {
        // set up instances (using MOQ, or whatever) with the string generic type. 
        // Call them target and oracle

        // then call your generic test methods
        GenericISomeInterfaceTests.SomeActionTest<string>(target, oracle);
    }
}