Прежде всего, я согласен с другими авторами, что этот метод делает слишком много. Лично я хотел бы, чтобы он выполнял только Db, затем мой прикладной уровень регистрировал действие и отправлял электронную почту. Тем не менее, для модульного тестирования этого метода я бы сделал следующее (в C #):
Во-первых, передайте классу, что метод существует в конструкторе, например:
public MyClass(
IRepository repository,
ILoggingService loggingService,
INotificationClient notificationClient)
... где IRepository
- это интерфейс, подобный следующему:
interface IRepository
{
Db GetDbRecord();
void UpdateDbRecord(Db record);
}
... ILoggingService
выглядит примерно так:
interface ILoggingService
{
void LogInformation(string information);
}
... и INotificationClient
выглядит примерно так:
interface INotificationClient
{
void SendNotification(Db record);
}
В теле конструктора присвойте переданные параметры частным полям, доступным только для чтения, в MyClass
.
Затем, в методе DoSomething
, получите запись Db
из IRepository
, обновите ее и сохраните обратно в IRepository
. Записать историю, используя ILoggingService
, затем вызвать SendNotification()
на INotificationClient
.
Наконец, в ваших модульных тестах используйте макет (например, Moq) для макетирования одного из каждого интерфейса. Передайте смоделированные объекты в новый экземпляр MyClass
, позвоните DoSomething()
, затем убедитесь, что ваш смоделированный IRepository
получил запрос на обновление объекта Db
, ваш смоделированный ILoggingService
попросил записать сообщение, а твою издевательскую INotificationClient
попросили SendNotification()
. То есть:
Db record = new Db();
var mockRepository = new Mock<IRepository>();
mockRepository.Setup(r => r.GetDbRecord()).Returns(record);
var mockLoggingService = new Mock<ILoggingService>();
var mockNotificationClient = new Mock<INotificationClient>();
new MyClass(
mockRepository.Object,
mockLoggingService.Object,
mockNotificationClient.Object).DoSomething();
// NUnit syntax:
Assert.That(record["ExpectedUpdatedField"], Is.EqualTo("ExpectedUpdatedValue"));
mockRepository.Verify(r => r.UpdateDbRecord(record), Times.Exactly(1));
mockLoggingService.Verify(ls => ls.LogInformation(It.IsAny<string>()), Times.Exactly(1));
mockNotificationClient.Verify(nc => nc.SendNotification(record), Time.Exactly(1));
В работающей системе вы вводите правильные реализации зависимостей MyClass
', и затем вы распределяете обязанности между более связными объектами.
Немного скучно, но вот как я это сделаю:)