views:

64

answers:

2

I've already checked this.. similar question but I am not convinced about the answers...

I currently have an interface that is only being implemented by one class, but that is going to change sooner or later. I currently have a test against the interface, and all tests at the beginning start with:

IFoo foo = GetConcreteFoo()

where GetConcreteFoo is something like

IFoo GetConcreteFoo()
{
   return new ConcreteFooA();
}

however, when I get more implementations of Foo, could there be a way to make all the test run against a list of all the different concrete foos?

Im thinking that if there is no way, at least I can copy/paste the test into a new file, with the name of the concreteclass, and change the return object of the GetConcreteFoo... (and changing the original file from (IFooTests to IConcreteFooATests).

I dont think this method is particularly bad.. but its not too elegant/clever, as it would be to run the same test (file) against all concrete implementations.

Is there a way to make it do that?

(Im using MSTests )

Thanks!

+3  A: 

Not sure about MSTest, but I believe you could do this in NUnit using parameterised tests, e.g. parameterising with the implementation class and using Activator.CreateInstance to instantiate it.

However, the deeper question is, would you want to? You don't say what your interface looks like, but the usual reason for having an interface is to permit different implementations. For example, if you have an IShape interface with an Area property, that will be implemented differently by Circle, Square and RorschachBlot. What could a test for the IShape.Area property reliably assert? In general, therefore, you can realistically test only classes and (concrete) methods.

Of course, if your interface is meant to imply semantic guarantees outside the interface spec (e.g. Area is always greater than 0) then you could test that for all implementations you know about. (For implementations you don't know about when you create the test, you must rely on communicating these additional requirements out of band, e.g. through documentation, and trusting implementors to obey them. When Code Contracts are released, you will be able to impose such requirements more reliably via contract classes.)

itowlson
+1 Overall a good answer, but doing this sort of integration testing can be valuable too as a regression test suite. Another thing is that if you have an interface that implies "semantic guarantees outside the interface spec" it's a prime candidate for an abstract class instead of an interface.
Mark Seemann
Yes the Interface does imply a semantic guarantee, since it is for a Repository where, independently of implementation, getAll gets all, Add adds a new element, Update will update an element, etc. The method should always have the same testable results, independently of implementation.Should I make it an abstract class? IF I do, i still got to test against all the implementations.Although talking about parameterised tests got me thinking on PEX, I am going to check that too.
Francisco Noriega
+2  A: 

Short answer, yes you can. Longer answer is it will depend on a lot of factors as to how it goes in your test suite.

Basically you can do something like this:

public void GenericIFooTest(IFoo testFoo) {
   // each of your tests against foo here...
   Assert.IsTrue(testFoo.DoesItsThing());
}

You could then generate your tests manually:

public void TestConcreteAFoo() {
   IFoo aFoo = new ConcreteAFoo(param1, param2, param3);

   GenericIFooTest(aFoo);
}
public void TestConcreteBFoo() {
   IFoo bFoo = new ConcreteBFoo();

   GenericIFooTest(bFoo);
}

Or you could use reflection to do it. This assumes you know how to instantiate each foo dynamically, if it has a default constructor that would be best:

public void TestAllFoos() {
   foreach(string assembly in Directory.GetFiles(assemblyPath, "*.dll", SearchOptions.All) {
      Assembly currentAssembly = Assembly.LoadAssembly(assembly);

      foreach(Type internalTypes in currentAssembly.GetTypes()) {
         if (internalTypes.IsAssignableFrom(IFoo) && !(internalTypes is IFoo)) {
            IFoo fooType = AppActivator.CreateInstance(internalTypes);

            GenericIFooTest(fooType);
         }
      }
   }
}

The last part is from memory of some code I was writing last week to do the same thing, its a little rough but should get you started. You could of course use LINQ to simplify it, I just dont know the syntax off the top of my head.

GrayWizardx
both your suggestions are pretty good options, Im gonna try them and the parameterised tests to see what I feel the most comfortable with :)
Francisco Noriega
You can combine them with parametrized tests to come up with a hybrid, perhaps using the parameters to load either the assemblies or the types you care about.
GrayWizardx