简体   繁体   English

单元测试/如何断言非模拟方法被称为/ C#/ Rhino Mocks / NUnit

[英]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. 我有一个MailService,允许我发送实现以下接口的电子邮件。

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. 函数SendRegisteringEmail应该调用CreateRegisteringMailMessage,然后将返回MailMessage交给SendMail函数,而SendRegisteringEmail应该返回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). 我正在使用NUnit和Rhino Mocks进行测试,我是测试的新手(1周),并且正在练习TDD(至少我尝试过)。 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. 我的问题是我不知道如何在我调用SendRegisteringEmail时断言CreateRegisteringMailMessage已被调用,因为MailService不是模拟对象。 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. 启动测试时收到以下错误:FtpWeb.Tests.MailServiceTests.SendRegisteringEmail_ShouldCallCreateRegisteringMailMessage:System.InvalidOperationException:对象'FtpWeb.Models.MailService'不是模拟对象。

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. 我不确定您是否应该测试是否调用了CreateRegisteringMailMessage You should just be checking the outcome of calling SendRegisteringEmail . 您应该只检查调用SendRegisteringEmail结果 What should that do with your MailService implementation? 这与您的MailService实现有什么关系? 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 . 方法CreateRegisteringMailMessageSendMail抽象级别似乎低于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. 您可以考虑创建一个包含SendRegisteringEmail的更高级别的类,并且该类将使用IMailService,您可以照常进行模拟和断言。

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. 如果不是第一种解决方案,那么您应该将SendRegisteringEmail作为一个整体来对待,因此,有效的方法是您必须在一个测试中测试CreateRegisteringMailMessageSendMail副作用(有点代码味道,因此我建议提取另一个间接级别)-参见乔恩的答案。

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? CreateRegisteringMailMessage方法有什么作用? Does it affect the content of the RegisterModel instance passed in during the call? 它会影响在调用过程中传递的RegisterModel实例的内容吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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