Jon
Jon

Reputation: 40062

Unit Testing return value of method called from inside another method

I have a method similar to this:

public List<MyClass> DoSomething(string Name, string Address, string Email, ref string ErrorMessage)
{
  //Check for empty string parameters etc now go and get some data   
  List<MyClass> Data = GetData(Name, Address, Email);

  /*************************************************************    
  //How do I unit test that the data variable might be empty???    
  *************************************************************/

  List<MyClass> FormattedData = FormatData(Data);
  return FormattedData;
}

I'm just learning TDD/Unit Testing. My question is, how do I write a test to ensure that if GetData returns an empty list I set ErrorMessage to something and then return a empty list?

Upvotes: 5

Views: 23455

Answers (4)

Mharlin
Mharlin

Reputation: 1705

[TestMethod]
public void MyDoSomethingTest()
{
   string errorMessage = string.Empty;
   var actual = myClass.DoSomething(..., ref errorMessage)
   Assert.AreEqual("MyErrorMessage", errorMessage);
   Assert.AreEqual(0, FormattedData.Count);
}

I'm assuming that if there isn't any datato format the formatter will return an empty list.

Since you want to verify the final outcome of the method I would not try to find out what's returned from the GetData function since it's the actaul return value that you want to verify that it's an empty list and maybe that the FormatData doesn't crash.

If you want to exit the function as quickly as possible you could check if any of the parameters are null och empty and in that case just do

errorMessage = "Empty parameters are not allowed";
return new List<MyClass>();

Upvotes: 1

Barracoder
Barracoder

Reputation: 3764

IMO, the line

List<MyClass> Data = GetData(Name, Address, Email);

should be outside the class. With a method signature changed to

public List<MyClass> DoSomething(List<MyClass> data, ref string ErrorMessage)

this method becomes much easier to test as you can easily vary the inputs to test all the possible edge cases.

The other alternative is to have the GetData method being exposed by a mock dependency, which you can then setup to return various results. So your class would now look like:

class ThisClass
{
   [Import]
   public IDataService DataService {get; set;}

   public List<MyClass> DoSomething(string Name, string Address, string Email, ref string ErrorMessage)
   {
     //Check for empty string parameters etc now go and get some data   
     List<MyClass> Data = IDataService.GetData(Name, Address, Email); // using dependency

     List<MyClass> FormattedData = FormatData(Data);
     return FormattedData;
   }
}

Upvotes: 1

Paul Ruane
Paul Ruane

Reputation: 38600

Unit testing is not something you add to the middle of existing methods, it is about testing small units of code in isolation from the rest of the system such that you have confidence that the unit is behaving as it should.

So, you should write a second class that's sole responsibility is to test that the class in which DoSomething lives (let's call this class Daddy and the test class DaddyTests) behaves as you expect it to. You can then write a test method that calls DoSomething and ensures that ErrorMessage is set appropriately (also ErrorMessage should be an out param, not ref unless you are also passing a value in).

To facilitate this test you will need to make sure that GetData returns no data. Trivially you can do this by passing in an empty set of data in a fake provider but in more complex scenarios whole classes may have to be swapped out for fake/mock equivalents: the use of interfaces and dependency-injection makes this task very simple. (Typically the provider is set during Daddy's construction, not as a parameter in the call to DoSomething.)

public class Daddy {
    public List<MyClass> DoSomething(string Name, string Address,                  string Email, out string ErrorMessage, IDataProvider provider)
    {
      //Check for empty string parameters etc now go and get some data   
      List<MyClass> Data = provider.GetData(Name, Address, Email);

      if (Data.Count == 0)
      {
          ErrorMessage = "Oh noes";
          return Enumerable.Empty<MyClass>();
      }

      List<MyClass> formattedData = FormatData(Data);
      return formattedData;
    }
}

[TestClass]
public class DaddyTest {
    [TestMethod]
    public void DoSomethingHandlesEmptyDataSet() {
        // set-up
        Daddy daddy = new Daddy();

        // test
        IList<MyClass> result = daddy.DoSomething("blah",
                                                  "101 Dalmation Road",
                                                  "[email protected]",
                                                  out error,
                                                  new FakeProvider(new Enumerable.Empty<AcmeData>())); // a class we've written to act in lieu of the real provider

        // validate
        Assert.NotNull(result); // most testing frameworks provides Assert functionality
        Assert.IsTrue(result.Count == 0);
        Assert.IsFalse(String.IsNullOrEmpty(error));
    }
}

}

Upvotes: 3

gdoron
gdoron

Reputation: 150273

When developing you should have left an "entrance point" for testing like this:

public List<MyClass> DoSomething(string Name, string Address,
              string Email, ref string ErrorMessage, IDataProvider provider)
{
  //Check for empty string parameters etc now go and get some data   
  List<MyClass> Data = provider.GetData(Name, Address, Email);

  List<MyClass> FormattedData = FormatData(Data);
  return FormattedData;
}

And in the Unit Testing you should mock the IDataProvider

It's called Dependency Injection (DI) or Inversion Of Control (IOC)

common mocking library:

The list was taken from here

Wikipedia articles:

Dependency Injection
Inversion Of Control


NUnit pseudo code:

[Test]
public void When_user_forgot_password_should_save_user()
{
    // Arrange
    var errorMessage = string.Empty;
    var dataProvider = MockRepository.GenerateStub<IDataProvider>();
    dataProvider.Stub(x => x.GetData("x", "y", "z", ref errorMessage )).Return(null);

    // Act
    var result DoSomething("x","y","z", ref errorMessage, dataProvider);

    // Assert

    //...
}

Upvotes: 4

Related Questions