Mock 公共方法 - 新关键字

问题描述 投票:0回答:1

我有以下课程,它使用 HTML Agility Pack 下载文件。 GetImageUrls 方法有一些逻辑,我想通过 Moq (https://github.com/moq/moq) 进行单元测试:

public class AgilityPackHtmlDownloadService : IHtmlDownloadService
{
    private HtmlWeb _htmlWeb;

    public AgilityPackHtmlDownloadService() : this(new HtmlWeb())
    {            
    }

    public AgilityPackHtmlDownloadService(HtmlWeb htmlWeb)
    {
        _htmlWeb = htmlWeb;
    }

    public IEnumerable<string> GetImageUrls(string pageUrl)
    {
        if (string.IsNullOrWhiteSpace(pageUrl))
        {
            throw new System.Exception("The Page URL cannot be blank");
        }
        
        var document = _htmlWeb.Load(pageUrl);
                    
        var result = new List<string>();
                    
        foreach (var element in document.DocumentNode.Descendants("img"))
        {
            var src = element.GetAttributeValue("src", null);
            var dataSrc = element.GetAttributeValue("data-src", null);
            var imageUrl = src ?? dataSrc;

            if (!string.IsNullOrWhiteSpace(imageUrl))
            {
                result.Add(imageUrl);
            }                
        }

        return result;
    }
}

HtmlWeb 类不基于接口,因此有两个构造函数的原因——我想通过第二个构造函数注入 HtmlWeb 的模拟实例。

需要mock HtmlWeb的Load方法,定义为“public”,但不是virtual,Moq无法处理

因此我创建了这个类:

public class HtmlWebEx : HtmlWeb
{
    public virtual new HtmlDocument Load(string url)
    {
        return base.Load(url);
    }
}

因为这个子类有一个名为 Load 的虚方法,它可以通过 Moq 像这样模拟:

var mockHtmlWeb = new Mock<HtmlWebEx>();
mockHtmlWeb.Setup(x => x.Load(It.IsAny<string>())).Returns(mockHtmlDocument);
            
IHtmlDownloadService htmlDownloadService = new AgilityPackHtmlDownloadService(mockHtmlWeb.Object);

因为被测类在构造函数中需要一个 HtmlWeb 参数,而不是 HtmlWebEx,所以 _htmlWeb.Load 执行基类的 Load 方法,绕过我的 Mock 实现。

(如果我使用标准的虚拟/override 方法,将调用子类的 Load 方法,但我没有覆盖,只是隐藏)。

如何让代码在运行单元测试时调用子类的模拟 Load 方法?

我为构造函数尝试了以下代码,即更改参数的类型,这确实有效,但现在我修改了代码只是为了使单元测试工作,这似乎不正确:

public AgilityPackHtmlDownloadService() : this(new HtmlWebEx()) { //... }

public AgilityPackHtmlDownloadService(HtmlWebEx htmlWeb)
{
    _htmlWeb = htmlWeb;
}
c# unit-testing moq
1个回答
2
投票

创建界面:

public interface IHtmlWeb
{
    HtmlDocument Load(string url);
}

创建一个继承 HtmlWeb 类的类,并声明它实现了接口:

public class HtmlWebInterfaced : HtmlWeb, IHtmlWeb { }

注入接口/模拟的实现:

public class AgilityPackHtmlDownloadService : IHtmlDownloadService
{
    private readonly IHtmlWeb _htmlWeb;

    public AgilityPackHtmlDownloadService(IHtmlWeb htmlWeb)
    {
        _htmlWeb = htmlWeb;
    }
    
    // rest of class removed for brevity...
}

在使用中,对 Load 方法的调用将由基类上的方法处理。

© www.soinside.com 2019 - 2024. All rights reserved.