views:

119

answers:

2

I have an interface with generics that is implemented by some classes. For each of these classes there is proxy class that implement the interface as well. Giving roughly this code:

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
}

The unit tests code I would want to look something like this:

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
    }
}

But this gives the error: UTA002: TestClass attribute cannot be defined on generic class ISomeInterfaceTests<T>.

Is there some nice workaround to this? Currently the best solution I can think of is to have a method in ClassAProxyTests that calls the different test methods in ISomeInterfaceTests<T>. There are several problems to that approach though:

  • It has to be done manually for each test implementing ISomeInterfaceTests<T>.
  • Should one method result in an assertion that fails then the remaining methods will not be executed.
  • You cannot use the ExpectedException attribute and would have to wrap the required code in try catch statements.

but alas a better solution escapes me.

A: 

The only reason you have the parent class is to create instances of your interface, yes?

Use MOQ instead.

Edit:

Not quite. The ISomeInterfaceTests<T> class is used to test the functionality of the proxies implementing the ISomeInterface<T> interface - it tests the proxies against the objects they are a proxy for (the oracle). – Cornelius

Hmm... that sounds like an integration test. Are your oracle objects read from the database somehow, or are you mocking them like you should be? Anyway, I stand by using Moq to get your instances and mocking vs integration testing in general.

But it strikes me that your approach for generalization of your tests may be the issue. Perhaps a different approach? Try this on:

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);
    }
}
Randolpho
Not quite. The ISomeInterfaceTests<T> class is used to test the functionality of the proxies implementing the ISomeInterface<T> interface - it tests the proxies against the objects they are a proxy for (the oracle).
Cornelius
The oracle is a mockup object and does no database work.The approach you suggested will still require that the test methods be implemented for all the different proxy tests. Thus ClassBProxyTests and ClassCProxyTests will have to explicitly state all the test methods and the associated attributes (TestMethod, ExpectedException) as well. Granted it would only have a call to a GenericISomeInterfaceTests (which is much better than reimplementing the test each time) but a solution where it would not be needed to do that would be preferable.
Cornelius
I don't think such a solution will exist. You'll need TestMethod on all of your test methods, period.
Randolpho
A: 

Sounds like you need to use the GenericTestFixture feature of NUnit 2.5. This feature allows you to place the [TestFixture] attribute on a generic class, then specify which specializations of the test fixture apply.

Your main test fixture will look as follows (and you may be able to remove some interfaces too):

[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
    }
}
Steve Guidi
Unfortunately using NUnit is not an option at this stage, as it will require quite some red tape to get approval.
Cornelius