views:

159

answers:

2

Related to yesterday's question. I implemented the solution proposed by Mehrdad Afshari but this caused another problem. To recap: I have a class containing a dictionary of Type->IList<Type> e.g. Cat->{cat1, cat2}, Zebra->{zebra1, zebra2} where Cat and Zebra are subclasses of Animal. Now Mehrdad proposed the following method to retrieve the all animals of a certain type:

IList<T> GetAnimalsOfType<T>() where T : Animal {
    return dictionary[typeof(T)].OfType<T>().ToList();
}

This works but breaks my unit test. The reason is that Animal is an abstract class and so I'm using Rhino Mocks to stub it (using animal = MockRepository.GenerateStub<Animal>();). My unit test for this class tries to create a new animal and then see if it's included in the dictionary.

zoo.AddAnimal(animal);  
IList<Animal> animals= zoo.GetAnimalsOfType<Animal>();  
Assert.That(animals[0], Is.EqualTo(animal));  

Unfortunately the type of animal created by Rhino Mocks is an animal proxy and I'm asking for Animal, which breaks my test. Any suggestions on how to correct the situation?

Update: thanks to all for the solutions.

+1  A: 

As you can't use this due to the compiler needing to know the type in advance:

zoo.AddAnimal(animal);  
IList<Animal> animals= zoo.GetAnimalsOfType<typeof(animal)>();  
Assert.That(animals[0], Is.EqualTo(animal));

I think you'll have to roll your own mock:

class MockAnimal : Animal
{
}

zoo.AddAnimal(new MockAnimal());  
IList<Animal> animals= zoo.GetAnimalsOfType<MockAnimal>();  
Assert.That(animals[0], Is.EqualTo(animal));

also do you not want to check that the instance returned is not the same instance that was added, rather than just equal to? (not certain of the syntax, still you the Asset.AreSame() here)

Assert.That(animals[0], Is.SameAs(animal));

Its not surprising the other doesn't as you want the GetAnimalsOfType to only return animals of the exact type don't you, not types that are derived from that? If you did this:

class Tiger : Animal
{
}

zoo.AddAnimal(new Tiger());  
IList<Animal> animals= zoo.GetAnimalsOfType<Animal>();

would you expect this to pass:

Assert.AreEqual(1, animals.Count);

I assume not. If you want to do what you outlined I think you will have to create an actual Animal not a mock.

Sam Holder
Actually your first suggestion wouldn't work since the compiler has to know before execution which type I'm requesting in `zoo.GetAnimalsOfType<Animal>()` and using `zoo.GetAnimalsOfType<typeof(animal)>()` the type cannot be known beforehand.
Johnny
then I think you are stuck with using a real animal or rolling your own mock and using the type of that. I'll edit the answer.
Sam Holder
+2  A: 

You could ask for the specific type you have just inserted. You have to create a helper function:

T Get<T>(T parameterOnlyToInferTheType)
{
    IList<Animal> animals= zoo.GetAnimalsOfType<T>();  
    return animals[0];
}

animal = MockRepository.GenerateStub<Animal>();
zoo.AddAnimal(animal);  
Animal expected = Get(animal);
Assert.That(expected, Is.EqualTo(animal)); 

Looks a bit dodgy still, but should work.

In general I tend to avoid keying collections on type, so I don't have these problems (e.g. I have a property on the class that returns an enum, etc).

Grzenio