Unit Test a method that returns a void

2019-03-24 14:44发布

Wanted to Unit Test a method in the following Class

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()
    }

}

Note:

  • Method Name = AuthoriseDeviceProfile returns void
  • The method checks userSentMakeModel against the one stored in the db match
  • If it matches - it simply returns (ie does not change any state)

How will we unit test this method?

  • Have mocked the Repo
  • Have covered scenario of "THROWS EXCEPTION"
  • Question is how to unit test the scenario of ALL WENT WELL ie user;s makeModel matched with repository;s makeModel

Any design suggestions to make this testable is most welcome Thanks in advance.

6条回答
小情绪 Triste *
2楼-- · 2019-03-24 14:57

You can set exception expectancies on your unit tests. In nUnit it looks like this:

[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void TestAuthoriseFail()
{
    // do something that should make the tested method throw the exception
}
查看更多
干净又极端
3楼-- · 2019-03-24 15:03

Even if your method returns void, it must be doing something that is useful for you (otherwise it would be a pointless method to have).

From your code, I'm guessing there are essentially 2 flavours of 'useful' things that the AuthoriseDeviceProfile method is doing:

  • calling the GetSelectedPhoneDetails method on the IDeviceAuthorisationRepositiory
  • throwing various exceptions based on certain conditions

Therefore to unit test the method, you should do two things that correspond to this:

  • Inject a mock IDeviceAuthorisationRepositiory and have it record and/or assert whether GetSelectedPhoneDetails is called
  • Exercise test methods that induce the various exceptions, and capture them as they are thrown to verify that:
    • an exception is in fact thrown
    • the exception that is thrown is the appropriate one for each scenario
查看更多
贪生不怕死
4楼-- · 2019-03-24 15:06

Since your method returns void, it probably has some side-effect that you can test/assert on.

In your case, an option would be to provide a mock instance of IDeviceAuthorisationRepositioryService. You can then check if a call to UpdatePhoneStatusToActive has happened. Here is a solution using Moq:

var mock = new Mock<IDeviceAuthorisationRepositioryService>();

var service = new DeviceAuthorisationService(mock.Object);
service.UpdateDeviceStatusToActive(....);

mock.Verify(x => service.UpdatePhoneStatusToActive(), Times.Never());
查看更多
Melony?
5楼-- · 2019-03-24 15:08
  [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);
            }
        }
    }
查看更多
干净又极端
6楼-- · 2019-03-24 15:09

Inject a mocked repository. Test if certain methods on the repository are called.

查看更多
做个烂人
7楼-- · 2019-03-24 15:22

If a method is void, then it should have some observable side-effect - otherwise it's pointless. So instead of testing the return value, you test the side-effects. In this case, it looks like those a probably around which exceptions are thrown in which situations.

(Here, "throws an exception" is deemed a side-effect; you could also think of it as an implicit kind of return value of course...)

查看更多
登录 后发表回答