测试一个返回void的方法的单元测试

15

我想对以下类中的一个方法进行单元测试

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 是否与存储在数据库中的匹配
  • 如果匹配-它只是返回(即不改变任何状态)

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

  • 已经模拟了Repo
  • 已覆盖“抛出异常”的情况
  • 问题是如何单元测试一切正常的情况,即用户的 makeModel 与存储库的 makeModel 匹配

欢迎提供任何设计建议以使其可测试。谢谢。

6个回答

15

由于您的方法返回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,并发现它非常有帮助。 - Venu b
如果注入的类没有接口,而且它是密封类,那么在这种情况下你该怎么办? - Rob McCabe

7
如果一个方法是void类型,那么它应该具有一些可观测的副作用-否则就毫无意义了。因此,您需要测试副作用而不是返回值。在这种情况下,看起来可能是在哪些情况下抛出异常。
(在这里,“抛出异常”被视为一种副作用;当然,您也可以将其视为一种隐式的返回值...)

6

注入模拟仓库。测试仓库上的特定方法是否被调用。


3

您可以在单元测试中设置异常期望。在nUnit中,它的代码如下:

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

2
即使您的方法返回void,它也必须做些对您有用的事情(否则它将是一个无意义的方法)。
从您的代码中可以看出,“授权设备配置文件”方法实际上有两种“有用”的东西:
- 在IDeviceAuthorizationRepositiory上调用GetSelectedPhoneDetails方法 - 基于某些条件抛出各种异常
因此,要对该方法进行单元测试,您应该执行与此相对应的两个操作:
- 注入模拟的IDeviceAuthorizationRepositiory并记录和/或断言是否调用了GetSelectedPhoneDetails。 - 执行诱发各种异常的测试方法,并在抛出异常时捕获它们以验证以下内容:
- 确实抛出了异常 - 对于每种情况抛出的异常都是适当的

感谢Chamila_c。我们通过发送模拟存储库来覆盖了单元测试中的“throws Exception”。我们如何覆盖“一切顺利的情况”? - Venu b
设置测试场景,使您不期望抛出异常,然后检查在“catch”子句中是否没有捕获任何异常。这可以简单地设置一个布尔值,并且只有在“catch”中切换代码。然后,如果在测试结束时布尔值保留其初始值,则知道没有抛出异常。 - Chamila Chulatunga
在测试中使用try catch不被认为是一种测试异味吗? - Venu b
一般来说,在测试中使用try-catch并不是一个好主意,当然还有更为复杂的测试异常的方法(例如请参考Phil Gan的回答)。但是如果你不能使用更为复杂的机制,需要特别地测试异常,并且仔细地只捕获你正在测试的异常类型,那么我认为这是可行的方法。 - Chamila Chulatunga

0
  [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);
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接