我有一些遗留代码,并针对我对该代码所做的改进编写了测试。我有一个类 SiteSession 并提取了一个接口(interface) ISiteSession 以便可以在调用类中注入(inject)依赖项。

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

    //...
}

调用类有一个构造函数,其中依赖项被注入(inject)到正在测试的 Controller 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);

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

现在我的测试方法已经模拟了依赖关系,我对为此 Controller 或代码的任何其他部分创建的任何测试都没有问题。但是当调用这个 Controller CustomerDetails 上的测试时,实际的构造函数调用是对 SiteSession 类进行的,我无法注入(inject)模拟并中断真正的调用。我的测试代码如下:
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

最佳答案

这是一个设计问题。通过在 Controller 中手动创建实例, Controller 与实现紧密耦合,这使得模拟变得非常困难。查看您的设计选择,因为当前示例在最终目标方面不是很清楚。

提供适当单元测试的难度应该是设计问题的直接指标。

话虽如此,根据当前的设计,如果您可以控制 Controller ,则可能需要 session 提供程序

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

Controller 将明确依赖于哪个
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);
}

关于c# - 如何模拟在被测方法内实例化的对象,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/50465386/

10-12 04:08
查看更多