单元测试返回从另一个方法内部调用的方法的值
本文关键字:方法 返回 调用 内部 另一个 单元测试 | 更新日期: 2023-09-27 18:21:33
我有一个类似的方法:
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设置为某个值,然后返回一个空白列表?
在开发时,应该为测试留下一个"入口点":
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)
常见的嘲讽库:
- NMock
- EasyMock.NET
- TypeMock隔离器商用/付费
- 犀牛模型
- Moq
- N取代
- JustLock商业/付费
- FakeItEay
- 鼹鼠
名单取自这里
维基百科文章:
依赖项注入反转控制
NUnit伪代码:
[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
//...
}
单元测试不是在现有方法中间添加的东西,而是在与系统其他部分隔离的情况下测试小的代码单元,这样您就可以确信单元的行为是正常的。
因此,您应该编写第二个类,它的唯一职责是测试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));
}
}
}
IMO,线
List<MyClass> Data = GetData(Name, Address, Email);
应该在课堂之外。将方法签名更改为
public List<MyClass> DoSomething(List<MyClass> data, ref string ErrorMessage)
这种方法变得更容易测试,因为你可以很容易地改变输入来测试所有可能的边缘情况。
另一种选择是通过mock依赖项公开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;
}
}
[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可能不会崩溃。
如果你想尽快退出函数,你可以检查是否有任何参数为空,在这种情况下只需进行
errorMessage = "Empty parameters are not allowed";
return new List<MyClass>();