You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让单元测试调用HtmlWeb子类的Mock Load方法?

解决方案:无需修改业务代码的Mock方案

你的核心问题是new关键字隐藏基类方法不具备多态性,当被测类以HtmlWeb类型接收实例时,调用的仍是基类的Load方法。以下是两种无需修改业务代码的可行方案:

方案1:利用HtmlWeb的protected虚方法(推荐)

HtmlWeb的Load方法内部依赖protected virtual的GetResponse方法,我们可以通过Moq的Protected()扩展Mock这个方法,从而控制返回的HTML内容,完全绕过非虚的Load方法限制。

测试代码示例:

// 构建Mock的HtmlWeb实例
var mockHtmlWeb = new Mock<HtmlWeb>();

// 预设要返回的测试HTML内容
var testHtml = "<html><body><img src='img1.png'><img src='img2.png'></body></html>";

// 伪造WebResponse,返回预设的HTML流
var fakeResponse = new Mock<WebResponse>();
fakeResponse.Setup(r => r.GetResponseStream())
            .Returns(new MemoryStream(Encoding.UTF8.GetBytes(testHtml)));

// 设置HtmlWeb的protected虚方法GetResponse,返回伪造响应
mockHtmlWeb.Protected()
           .Setup<WebResponse>("GetResponse", ItExpr.IsAny<WebRequest>())
           .Returns(fakeResponse.Object);

// 注入Mock实例到被测服务
var downloadService = new AgilityPackHtmlDownloadService(mockHtmlWeb.Object);

// 执行测试并断言结果
var imageUrls = downloadService.GetImageUrls("http://dummy.url");
Assert.Equal(2, imageUrls.Count);
Assert.Contains("img1.png", imageUrls);
Assert.Contains("img2.png", imageUrls);

方案2:自定义Fake子类(无需Moq扩展)

如果不想依赖Moq的Protected扩展,可以自定义一个HtmlWeb的子类,重写GetResponse方法返回伪造内容,直接将子类实例注入被测服务:

// 自定义Fake HtmlWeb子类
public class FakeHtmlWeb : HtmlWeb
{
    private readonly string _testHtml;

    public FakeHtmlWeb(string testHtml)
    {
        _testHtml = testHtml;
    }

    protected override WebResponse GetResponse(WebRequest request)
    {
        var htmlStream = new MemoryStream(Encoding.UTF8.GetBytes(_testHtml));
        // 通过反射设置WebResponse的私有流字段
        var fakeResponse = new Mock<WebResponse>().Object;
        typeof(WebResponse).GetField("_responseStream", BindingFlags.Instance | BindingFlags.NonPublic)
                          ?.SetValue(fakeResponse, htmlStream);
        return fakeResponse;
    }
}

// 测试代码
var testHtml = "<html><body><img src='test-img.jpg'></body></html>";
var fakeHtmlWeb = new FakeHtmlWeb(testHtml);
var downloadService = new AgilityPackHtmlDownloadService(fakeHtmlWeb);

var imageUrls = downloadService.GetImageUrls("http://dummy.url");
Assert.Single(imageUrls);
Assert.Equal("test-img.jpg", imageUrls[0]);

原有方案失效原因

你用new声明的Load方法是隐藏基类方法,而非重写(override)。C#的多态机制只对override修饰的方法生效:当变量类型为基类HtmlWeb时,调用Load会直接执行基类方法,不会触发子类的new方法——只有当变量类型为子类HtmlWebEx时,才会调用子类的Load方法。

内容的提问来源于stack exchange,提问作者Howard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 09:57:44