单元测试返回从另一个方法内部调用的方法的值

本文关键字:方法 返回 调用 内部 另一个 单元测试 | 更新日期: 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>();