Home > other >  Moq system under tests void method
Moq system under tests void method

Time:07-08

This is my code

public class IUserService
{
    void Register(string email, string password);
}

public class UserService
{
    private readonly IUserRepository userRepository;
    
    public UserService(IUserRepository userRepository)
    {
        this.userRepository = userRepository;
    }

    public void Register(string email, string password)
    {
        ...
        var registered = senderIdRepository.Insert(email, password).Result;
        SendRegistrationConfirmationEmail(email);
        
        return registered;
    }
    
    public void SendRegistrationConfirmationEmail(string email)
    {
        ...
    }
}

public class UserServiceTest
{
    private readonly ISenderIdService sut;
    private readonly Mock<IUserRepository> userRepository = new Mock<IUserRepository>();
    
    [Fact]
    public void User_register_test()
    {
        // arrange
        userRepository.Setup(x => x.Insert(It.IsAny<string>(), It.IsAny<string>()))
            .Returns(Task.FromResult(true));
        
        var email = "[email protected]";
        var password = "testPass@#$d";
            
        // act
        var result = sut.Register(email, password);
        
        // assert
        Assert.Equal(1, result);
    }
}

When I run test User_register_test(), SendRegistrationConfirmationEmail() is launched. I want to prevent that, because I don't want test email sending. Is it possible to do it without having SendRegistrationConfirmationEmail in IUserService?

What I would like to do is ignore whole SendRegistrationConfirmationEmail() when launching User_register_test(), but I dont want to have void SendRegistrationConfirmationEmail(string email) in IUserService interface to have it mocked. It it possible to do?

CodePudding user response:

The cleanest way to solve this is to move the mail-related methods to a separate interface and a dedicated class. On the one hand, you could mock it easily; on the other hand, it also supports the Single-Responsibility-Principle.

If you do not want to do this, there is also a hack that you can use. It will work, but it has its downsides:

  • Make SendRegistrationConfirmationEmail virtual (and maybe protected).
  • In the test project, you create a derived class that overrides SendRegistrationConfirmationEmail and replaces the send-mail-functions.
  • Instead of testing UserService directly, you test the derived class that differs only in the implementation of SendRegistrationConfirmationEmail.

As you see, you also have to do some work to create the hack (maybe more than solving it in a clean way); also the probability that this hack grows and that it is used later in ways that were not intended is high. Therefore, I'd opt for the clean way and refactor SendRegistrationConfirmationEmail into an interface of its own.

  • Related