【问题标题】:Unit Test a method that returns a void单元测试返回 void 的方法
【发布时间】:2012-12-11 12:51:44
【问题描述】:

想要对以下类中的方法进行单元测试

public class DeviceAuthorisationService : IDeviceAuthorisationService
{
    private DeviceDetailsDTO deviceDetailsDTO = null;
    private IDeviceAuthorisationRepositiory deviceAuthorisationRepositiory;

    public DeviceAuthorisationService(IDeviceAuthorisationRepositioryService paramDeviceAuthorisationRepository)
    {
        deviceAuthorisationRepositiory = paramDeviceAuthorisationRepository;
    }

    public void AuthoriseDeviceProfile(long paramUserID, string paramClientMakeModel)
    {
        if (deviceDetailsDTO == null)
            GetCellPhoneDetails(userID);

        if (deviceDetailsDTO.IsDeviceSelected == false)
            throw new SomeCustomExceptionA();

        if (deviceDetailsDTO.CellPhoneMakeModel.ToLower() != paramClientMakeModel.ToLower())
            throw new SomeCustomExceptionB;
    }

    public void UpdateDeviceStatusToActive(long userID)
    {
        if (deviceDetailsDTO == null)
            throw new InvalidOperationException("UnAuthorised Device Profile Found Exception");

        if (deviceDetailsDTO.PhoneStatus != (short)Status.Active.GetHashCode())
            deviceAuthorisationRepositiory.UpdatePhoneStatusToActive(deviceDetailsDTO.DeviceID);
    }

    private void GetCellPhoneDetails(long userID)
    {
        deviceDetailsDTO = deviceAuthorisationRepositiory.GetSelectedPhoneDetails(userID);

        if (deviceDetailsDTO == null)
            throw new SomeCustomException()
    }

}

注意:

  • 方法名称 = AuthoriseDeviceProfile 返回 void
  • 该方法检查 userSentMakeModel 与存储在 db 匹配中的那个
  • 如果匹配 - 它只是返回(即不改变任何状态)

我们将如何对这个方法进行单元测试?

  • 嘲笑了回购
  • 已涵盖“抛出异常”的场景
  • 问题是如何对 ALL WENT WELL 的场景进行单元测试,即 user;s makeModel 与 repository;s makeModel 匹配

欢迎提出任何使其可测试的设计建议 提前致谢。

【问题讨论】:

    标签: c# unit-testing dependency-injection


    【解决方案1】:

    由于您的方法返回 void,它可能有一些副作用,您可以对其进行测试/断言。

    在您的情况下,一个选项是提供IDeviceAuthorisationRepositioryService 的模拟实例。然后,您可以检查是否发生了对 UpdatePhoneStatusToActive 的呼叫。这是使用Moq的解决方案:

    var mock = new Mock<IDeviceAuthorisationRepositioryService>();
    
    var service = new DeviceAuthorisationService(mock.Object);
    service.UpdateDeviceStatusToActive(....);
    
    mock.Verify(x => service.UpdatePhoneStatusToActive(), Times.Never());
    

    【讨论】:

    • 感谢 alexn - 一直在使用 Moq,发现它很有帮助
    • 如果注入的类没有接口怎么办,如果是密封类怎么办……在这种情况下你能做什么?
    【解决方案2】:

    如果一个方法是无效的,那么它应该有一些可观察到的副作用 - 否则它是没有意义的。因此,不是测试返回值,而是测试副作用。在这种情况下,看起来可能是在哪些情况下引发了哪些异常。

    (这里,“抛出异常”被认为是一种副作用;您当然也可以将其视为一种隐式的返回值......)

    【讨论】:

      【解决方案3】:

      注入一个模拟存储库。测试是否调用了存储库中的某些方法。

      【讨论】:

        【解决方案4】:

        您可以在单元测试中设置异常预期。在 nUnit 中是这样的:

        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TestAuthoriseFail()
        {
            // do something that should make the tested method throw the exception
        }
        

        【讨论】:

          【解决方案5】:

          即使你的方法返回 void,它也必须做一些对你有用的事情(否则它会是一个毫无意义的方法)。

          根据您的代码,我猜AuthoriseDeviceProfile 方法正在做的基本上有两种“有用”的东西:

          • IDeviceAuthorisationRepositiory 上调用GetSelectedPhoneDetails 方法
          • 根据特定条件抛出各种异常

          因此,要对方法进行单元测试,您应该做两件事与此相对应:

          • 注入一个模拟 IDeviceAuthorisationRepositiory 并让它记录和/或断言是否调用了 GetSelectedPhoneDetails
          • 练习引发各种异常的测试方法,并在抛出异常时捕获它们以验证:
            • 实际上抛出了一个异常
            • 引发的异常适用于每个场景

          【讨论】:

          • 谢谢 Chamila_c。通过发送模拟存储库来覆盖单元测试中的“抛出异常”。我们如何涵盖“一切顺利的情况”
          • 设置测试场景,这样您就不会期望抛出异常,然后检查您没有在“catch”子句中发现任何异常。这可以像预先设置一个布尔值一样简单,并且只有代码在“catch”中切换它。那么如果在测试结束时布尔值保留了它的初始值,你就知道没有抛出异常
          • 在测试中的 try catch 不被视为测试气味吗?
          • 一般来说,是的,测试中的 try-catch 是个坏主意,当然还有更复杂的异常测试方法(例如,参见 Phil Gan 的回答)。但是,如果您不能使用更复杂的机制,专门测试异常,并且小心地只捕获您正在测试的异常类型,那么我认为这是一个不错的方法。
          【解决方案6】:
            [TestMethod]
                  public void AuthoriseDeviceProfileTest()
                  {
                      long paramUserID=1, string paramClientMakeModel=test";
                      IDeviceAuthorisationService DeviceAuthorisationService= new DeviceAuthorisationService();
          
                      try
                      {
          
                          DeviceAuthorisationService.AuthoriseDeviceProfile(paramUserID, paramClientMakeModel);
                          Assert.IsNull(paramUserID);
          
                      }
                      catch (Exception e)
                      {
                          Assert.AreNotEqual("Exception of type was thrown", e.Message);
                      }
                  }
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-05-10
            • 2019-10-27
            • 2014-04-16
            • 2014-11-08
            • 1970-01-01
            • 2017-05-11
            • 2011-06-25
            相关资源
            最近更新 更多