我有一个类似的方法:
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;
}
我刚学习TDD /单元测试。我的问题是,如何编写测试以确保如果GetData返回一个空列表我将ErrorMessage设置为某个东西,然后返回一个空列表?
答案 0 :(得分:3)
单元测试不是你添加到现有方法中间的东西,它是关于测试与系统其余部分隔离的小代码单元,这样你就可以确信单元的行为应该如此。
所以,你应该编写第二个类,其中唯一的责任是测试DoSomething
所居住的类(让我们称之为此类Daddy
和测试类DaddyTests
)的行为如下你期待它。然后,您可以编写一个调用DoSomething
的测试方法,并确保ErrorMessage
设置得恰当(ErrorMessage
也应该是out
参数,而不是ref
,除非您也传递了一个值。)
为方便此测试,您需要确保GetData
不返回任何数据。通过在假提供程序中传入一组空数据,可以做到这一点,但在更复杂的情况下,可能必须将整个类替换为伪/模拟等价物:使用接口和依赖注入使得此任务非常简单。 (通常,提供程序在Daddy
构造期间设置,而不是作为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",
"bob@example.com",
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));
}
}
}
答案 1 :(得分:2)
开发时你应该留下一个“入口点”进行测试:
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;
}
在单元测试中,您应该mock
IDataProvider
它被称为依赖注入(DI)或控制反转(IOC)
该列表取自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
//...
}
答案 2 :(得分:1)
[TestMethod]
public void MyDoSomethingTest()
{
string errorMessage = string.Empty;
var actual = myClass.DoSomething(..., ref errorMessage)
Assert.AreEqual("MyErrorMessage", errorMessage);
Assert.AreEqual(0, FormattedData.Count);
}
我假设如果没有任何数据格式,格式化程序将返回一个空列表。
由于你想验证方法的最终结果,我不会试图找出从GetData函数返回的内容,因为它是你要验证它是空列表的actaul返回值,也许是FormatData没有'崩溃。
如果你想尽快退出这个函数你可以检查是否有任何参数是空的och为空,在这种情况下只做
errorMessage = "Empty parameters are not allowed";
return new List<MyClass>();
答案 3 :(得分:1)
IMO,行
List<MyClass> Data = GetData(Name, Address, Email);
应该在课外。方法签名更改为
public List<MyClass> DoSomething(List<MyClass> data, ref string ErrorMessage)
这种方法变得更容易测试,因为您可以轻松改变输入以测试所有可能的边缘情况。
另一种方法是让模拟依赖项公开GetData方法,然后设置它以返回各种结果。所以你的班级现在看起来像:
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;
}
}