简体   繁体   中英

Unit testing/How to assert a non-mocked method was called/C#/Rhino Mocks/NUnit

I have a MailService which permit me to send Email which implement the following Interface.

public interface IMailService
  {
    bool SendRegisteringEmail(RegisterModel registerModel);
    bool SendMail(MailMessage mailMessage);
    MailMessage CreateRegisteringMailMessage(RegisterModel registerModel);

    IAppSettingsRepository AppSettingsRepository { get; set; }

    ISmtpClient SmtpClient { get; set; }
  }

The function SendRegisteringEmail should call CreateRegisteringMailMessage then give the return MailMessage to the SendMail function and SendRegisteringEmail should return the return Boolean of SendMail.

I'm using NUnit and Rhino Mocks to do my test, I'm new to testing (1week) and I'm practicing TDD (At least I try). My problem is I don't know how to assert that CreateRegisteringMailMessage was called when I call SendRegisteringEmail, because MailService isn't a Mocked Object. Here is my test:

[Test]
public void SendRegisteringEmail_ShouldCallCreateRegisteringMailMessage()
{
  //Arrange
  //MailMessage mailMessage = new MailMessage();
  IMailService mailService = new MailService { AppSettingsRepository = CreateAppSettingsMockReposotory() };
  //ISmtpClient smtpClientStub = MockRepository.GenerateStub<ISmtpClient>();
  //mailService.SmtpClient = smtpClientStub;
  //smtpClientStub.Stub(f => f.Send(mailMessage)).Return(true);


  //Act
  mailService.SendRegisteringEmail(ValidRegisterModel);

  //Assert
  mailService.AssertWasCalled(f => f.CreateRegisteringMailMessage(ValidRegisterModel));
}

I get the following error when I launch my test: FtpWeb.Tests.MailServiceTests.SendRegisteringEmail_ShouldCallCreateRegisteringMailMessage: System.InvalidOperationException : The object 'FtpWeb.Models.MailService' is not a mocked object.

I understand why I'm getting this error but now how to test my call. Since it's the same object I can't mock it to test it. If anybody can give me some lead to resolve this.

Thanks.

I'm not sure you should be testing that CreateRegisteringMailMessage is called. You should just be checking the outcome of calling SendRegisteringEmail . What should that do with your MailService implementation? Presumably it will have some effect on another service - so test that.

It would be an odd interface design which insisted that calling one method would require another method in the interface to be called - quite often you might make both methods call one private, common method for example.

The methods CreateRegisteringMailMessage and SendMail seem on a lower abstraction level than SendRegisteringEmail . You could consider creating a higher level class containing SendRegisteringEmail , and this class would use IMailService, which you could mock and assert as usual.

If the first solution is not an option, then you should treat SendRegisteringEmail as a whole, so effectively you have to test the side effects of both CreateRegisteringMailMessage and SendMail in a single test (a bit of a code smell, hence my suggestion to extract another level of indirection) - see Jon's answer.

This is often referred to an 'sensing' within a class under test: you have to find a way to tell whether a method has been called. These kinds of things are covered well in the book ' Working Effectively with Legacy Code ' which everyone should have (disclaimer: I have no affiliation with the book's author, I just think it's a good book to have).

What does the CreateRegisteringMailMessage method do? Does it affect the content of the RegisterModel instance passed in during the call?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM