Как смоделировать объект, созданный внутри тестируемого метода - PullRequest
0 голосов
/ 22 мая 2018

У меня есть устаревший код и тесты для написания улучшений, которые я сделал в этом коде.У меня есть класс SiteSession и я извлек интерфейс ISiteSession, чтобы зависимость могла быть введена в вызывающий класс.

public class SiteSession : ISiteSession
{
    public SiteSession(string userName, string externalRef, IePermitProAdapterClient ePermitService)
    {
        //.......
    }

    //...
}

В вызывающем классе есть конструктор, в котором зависимость вводится в контроллер CustomerDetails, который тестируется.

private readonly ICustomerDetails _customerDetails;
private ISiteSession _siteSession;

public SsoController(ICustomerDetails customerDetails, ISiteSession siteSession)
{
    _customerDetails = customerDetails;

    _siteSession = siteSession;
}

public ActionResult CustomerDetails(CustomerDetails customerDetails)
{
    //.....  
    //...
    //...    

    _siteSession = new SiteSession(customer.Username, customer.CustomerRef, ePermitService);

    //.....
    //...
    //...
}

Теперь мой метод тестирования смоделировал зависимости, и у меня нет проблем ни с одним из тестов, созданных для этого контроллера или для любой другой части кода.Но когда тесты на этом контроллере CustomerDetails вызваны, фактический вызов конструктора выполняется для класса SiteSession, и я не могу ввести макет и прервать реальный вызов.Мой тестовый код выглядит следующим образом:

private Mock<ISiteSession> _siteSession;

В методе настройки теста: _siteSession = new Mock<ISiteSession>();

И в методе теста: _siteSession.Setup(x => x.Token).Returns("TestToken");

Я пробовал что-то вроде:

 _siteSession = new Mock<SiteSession>(_customer.Object.Username, _customer.Object.CustomerRef, null);

Что, очевидно, неверно из-за разных типов преобразования, и я не могу придумать, как я могу смоделировать класс SiteSession, чтобы фактический конструктор не вызывался.Я использую NInject, NUnit и Moq

1 Ответ

0 голосов
/ 22 мая 2018

Это проблема дизайна.Путем ручного создания экземпляра в контроллере контроллер тесно связан с реализацией, что делает его очень трудным.Проверьте ваш выбор дизайна, так как текущий пример не очень ясен с точки зрения конечной цели.

Сложность в обеспечении надлежащего модульного теста должна быть прямым индикатором проблем дизайна.

При этом, основываясь на текущем дизайне, и если у вас есть контроль над контроллером, вам может потребоваться поставщик сеансов

public interface ISiteSessionProvider {
    ISiteSession CreateSiteSession(CustomerDetails customerDetails);
}

, от которого контроллер будет явно зависеть от

public class SsoController: Controller {
    private readonly ICustomerDetails _customerDetails;
    private readonly ISiteSessionProvider siteSessionProvider;

    public SsoController(ICustomerDetails customerDetails, ISiteSessionProvider siteSessionProvider) {
        _customerDetails = customerDetails;
        this.siteSessionProvider = siteSessionProvider;
    }

    public ActionResult CustomerDetails(CustomerDetails customerDetails) {
        //...

        ISiteSession siteSession = siteSessionProvider.CreateSiteSession(customerDetails);

        //...
    }
}

Модульный тест теперь потребовал бы, чтобы вы смоделировали желаемое поведение

//Arrange
var sessionMock = new Mock<ISiteSession>();
sessionMock.Setup(_ => _.Token).Returns("TestToken");

var providerMock = new Mock<ISiteSessionProvider>();
providerMock
    .Setup(_ => _.CreateSiteSession(It.IsAny<CustomerDetails>()))
    .Returns(sessionMock.Object);

var controller = new SsoController(Mock.Of<ICustomerDetails>(), providerMock.Object);

//Act
var result = controller.CustomerDetails(...);

//Assert
//...

Реализация провайдера может затем решить проблемы реализации

public ISiteSession CreateSiteSession(CustomerDetails customerDetails) {
    //...

    return new SiteSession(customer.Username, customer.CustomerRef, ePermitService);
}
...