views:

116

answers:

3

I'm trying to follow good practices when writing my testing suites. Halfway through i realised that iam spending alot (Most) of my time on the Fake Objects...Most of my tests does something like this

public interface ITemplateRepository
{
  string get GetGenericTemplate {get;}
}


public FakeTemplateRepository : ITemplateRepository
{
   public string GetGenericTemplate ()
   {
     return "<xml>Complex</xml>";
   }
}


[Test]
public void CanGetGenericTemplate()
{
  ITemplateRepository rep = new FakeTemplateRepository();
  Assert.IsNotNull(rep.GetGenericTemplate());
}


[Test]
public void GetGenericTemplateContains()
{  
ITemplateRepository rep = new FakeTemplateRepository();
Assert.IsTrue(rep.GetGenericTemplate().StartsWith("<xml>"));
}

I actually found myself spending alot of time on the FakeTemplateRepository, making sure it returned the actual content i was expecting. Is this a bad thing?

Is this still a valid Unit Test? Unit testing should be fast and simple, no? But honestly i'm not really sure, one thing for sure is that it got my thinking about the shape and content of my data. The content in the FakeRepository will more or less reflect my production content, albeit reading from the file system, instead of in-memory.

If in fact what i'm doing are Integration Tests, then how can should i use mocks for my unit tests?

It doesnt make sense to me (if using Mocks) that i set the expectation up so that it calls the method and returns a string? Am i missing something but i don't see much value there? The code wont even compile if i set an invalid method name!

I am really really confused about the whole thing and my perception of unit testing is totally blurred now with all these concepts.

Can any demonstrate with a super super simple example of how fakes and mocks fit into a suite? e.g What should be a unit test, What should be an integration test?

Thanks

+2  A: 

Once again... another person falls into a Mock Anti-pattern. You're "testing a Mock" or Fake object - which isn't of any use at all. Mocks need to be use to abstract away the dependencies or collaborators of your test subject.. not the test subject itself.

First I'd say search and read Martin Fowler's paper "Mocks aren't stubs". If you'd like to return values or setup expectations for specific arguments, go for a mocking framework over Roll-your-own fakes. Fakes are generally used to stub out collaborators... where you don't care for the collaborator.. e.g. stub out network or file IO.
Next read this similar SO question and the answers to it..

Gishu
+1  A: 

First of all, learn an isolation (mocking) framework, like Moq or Rhino Mocks. This will save you a lot of time.

Second, avoid creating mocks whenever possible and stick to stubs. Stubs just return values; you don't assert against them. Instead you assert that the state of the class under test is what you expected it to be. Use stubs to stub out injected dependencies.

This is a complex subject, and you'll need to do some reading. Osherove's Art of Unit Testing is cheap in its e-book form, and does a pretty good job of explaining this. You'll also want to read up on Dependency Injection.

Remember that you don't need fakes, mocks, or stubs to test many classes. The point is to test the public behavior of the actual class in isolation. Fakes are simply one tool to help you isolate the class from any dependencies.

Your examples concentrate on fakes, interfaces, and tests. Concentrate on the class under test and its desired behavior.

Integration tests have value too; you can wire everything up to a test database and make sure that it all works. They tend to be slow, as opposed to unit tests.

TrueWill
+2  A: 

In your example, you are testing your fake. You can't make sense out of this, because it doesn't make sense.

Here's an example of how you should use a fake. You want to unit test the DocumentGenerator class. This class happens to use the services of a ITemplateRepository. However, to properly unit test DocumentGenerator you want to make sure it doesn't depend on any external services. So you provide DocumentGenerator with a fake ITemplateRepository that will return canned results.

Here is what the code looks like.

public class DocumentGenerator
{
  ITemplateRepository _repo;

  DocumentGenerator( ITemplateRepository repo )
  {
    _repo = repo;
  }

  Doc GenerateDoc()
  {
   ....
   _repo.GetGenericTemplate();
    ....
  }
}


[Test]
public void GenerateDocTest()
{
  ITemplateRepository rep = new FakeTemplateRepository();
  DocumentGenerator docGenerator = new DocumentGenerator( rep );
  Assert.IsNotNull(docGenerator.GenerateDoc());
}

The gist of the example is that you testing something that uses a fake. You are not testing the fake itself. The fake exists so that the thing you are testing is not dependent on something external.

Phillip Ngan
What if the DocumentGenartor does not accept a repository but instead a string value of something that is returned from the repository? i.e.DocumentGenerator(string template). If i had to do something like this then does it suggest that i got the design wrong?
If DocumentGenartor only takes a String then you don't need the fake object at all. You only need to create fake objects for collaborators the unit under test actually uses.
mlk