views:

59

answers:

4

I have a method that takes 5 parameters. This method is used to take a bunch of gathered information and send it to my server.

I am writing a unit test for this method, but I am hitting a bit of a snag. Several of the parameters are Lists<> of classes that take some doing to setup correctly. I have methods that set them up correctly in other units (production code units). But if I call those then I am kind of breaking the whole idea of a unit test (to only hit one "unit").

So.... what do I do? Do I duplicate the code that sets up these objects in my Test Project (in a helper method) or do I start calling production code to setup these objects?

Here is hypothetical example to try and make this clearer:

File: UserDemographics.cs

class UserDemographics
{
     // A bunch of user demographic here
     // and values that get set as a user gets added to a group.
}

File: UserGroups.cs

class UserGroups
{
     // A bunch of variables that change based on 
     //  the demographics of the users put into them.
     public AddUserDemographicsToGroup(UserDemographcis userDemographics)
     {}
}

File: UserSetupEvent.cs

class UserSetupEvent
{
     // An event to record the registering of a user
     // Is highly dependant on UserDemographics and semi dependant on UserGroups
     public SetupUserEvent(List<UserDemographics> userDemographics, 
                           List<UserGroup> userGroups)
     {}
}

file: Communications.cs

class Communications
{
     public SendUserInfoToServer(SendingEvent sendingEvent, 
                                 List<UserDemographics> userDemographics,
                                 List<UserGroup> userGroups, 
                                 List<UserSetupEvent> userSetupEvents)
     {}
}

So the question is: To unit test SendUserInfoToServer should I duplicate SetupUserEvent and AddUserDemographicsToGroup in my test project, or should I just call them to help me setup some "real" parameters?

A: 

Sounds like your units are too tightly coupled (at least from a quick view at your problem). What makes me curious is for instance the fact that your UserGroups takes a UserDemographics and your UserSetupEvent takes a list of UserGroup including a list of UserDemographics (again). Shouldn't the List<UserGroup> already include the ÙserDemographics passed in it's constructor or am I misunderstanding it?

Somehow it seems like a design problem of your class model which in turn makes it difficult to unit test. Difficult setup procedures are a code smell indicating high coupling :)

Juri
A: 

Bringing in interfaces is what I would prefer. Then you can mock the used classes and you don't have to duplicate code (which violates the Don't Repeat Yourself principle) and you don't have to use the original implementations in the unit tests for the Communications class.

Jeroen
+1  A: 

You need test duplicates.

You're correct that unit tests should not call out to other methods, so you need to "fake" the dependencies. This can be done one of two ways:

  1. Manually written test duplicates
  2. Mocking

Test duplicates allow you to isolate your method under test from its dependencies.

I use Moq for mocking. Your unit test should send in "dummy" parameter values, or statically defined values you can use to test control flow:

public class MyTestObject
{
       public List<Thingie> GetTestThingies()
       {
             yield return new Thingie() {id = 1};
             yield return new Thingie() {id = 2};
             yield return new Thingie() {id = 3};
       } 
}

If the method calls out to any other classes/methods, use mocks (aka "fakes"). Mocks are dynamically-generated objects based on virtual methods or interfaces:

Mock<IRepository> repMock = new Mock<IRepository>();
MyPage obj = new MyPage() //let's pretend this is ASP.NET
obj.IRepository = repMock.Object;
repMock.Setup(r => r.FindById(1)).Returns(MyTestObject.GetThingies().First());
var thingie = MyPage.GetThingie(1);

The Mock object above uses the Setup method to return the same result for the call defined in the r => r.FindById(1) lambda. This is called an expecation. This allows you to test only the code in your method, without actually calling out to any dependent classes.

Once you've set up your test this way, you can use Moq's features to confirm that everything happened the way it was supposed to:

//did we get the instance we expected?
Assert.AreEqual(thingie.Id, MyTestObject.GetThingies().First().Id); 
//was a method called?
repMock.Verify(r => r.FindById(1));

The Verify method allows you to test whether a method was called. Together, these facilities allow you focus your unit tests on a single method at a time.

Dave Swersky
A: 

You should use mock objects, basically your unit test should probably just generate some fake data that looks like real data instead of calling into the real code, this way you can isolate the test and have predictable test results.

Alex