Reputation: 40062
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
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
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
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
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)
The list was taken from here
[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