如何对基于SelectPdf的GetPdf方法进行单元测试?
如何对基于SelectPdf的GetPdf方法进行单元测试?
嘿,我来给你捋捋怎么搞定这个单元测试的问题。首先先看看你原来的方法代码:
public PdfDocument GetPdf(string requestUrl) { var converter = new HtmlToPdf(); var response = converter.ConvertUrl(requestUrl); return response; }
这个方法直接在内部new了SelectPdf的HtmlToPdf类,还直接调用了它的ConvertUrl方法——这样写的话,单元测试可就麻烦了:不仅会真的发起网络请求去拉取目标URL,还会实打实调用第三方库的逻辑,这根本不是我们要的单元测试(单元测试应该只测自己写的逻辑,隔离所有外部依赖)。
那怎么改呢?核心思路就是把依赖抽象出来,用依赖注入替换掉硬编码的实例化,具体步骤是这样的:
第一步:抽象第三方依赖的接口
先定义一个接口,把我们需要用到的ConvertUrl方法暴露出来,这样不管底层用的是SelectPdf还是其他PDF转换库,我们的业务代码都不用改:
public interface IHtmlToPdfConverter { PdfDocument ConvertUrl(string requestUrl); }
第二步:写一个适配类包装SelectPdf的实现
这个类负责把SelectPdf的真实逻辑包装成我们定义的接口,相当于做个中间层:
public class SelectPdfConverterAdapter : IHtmlToPdfConverter { public PdfDocument ConvertUrl(string requestUrl) { var converter = new HtmlToPdf(); return converter.ConvertUrl(requestUrl); } }
第三步:重构原来的GetPdf方法,注入依赖
把原来直接new的逻辑换成通过构造函数注入我们的接口,这样外部可以传入不同的实现(真实的或者测试用的模拟实现):
// 假设这个方法所在的类叫PdfGenerator private readonly IHtmlToPdfConverter _converter; // 构造函数注入依赖 public PdfGenerator(IHtmlToPdfConverter converter) { _converter = converter ?? throw new ArgumentNullException(nameof(converter)); } public PdfDocument GetPdf(string requestUrl) { if (string.IsNullOrEmpty(requestUrl)) { throw new ArgumentException("请求URL不能为空", nameof(requestUrl)); } var response = _converter.ConvertUrl(requestUrl); return response; }
第四步:写单元测试(用Mock框架举例,比如Moq)
现在我们可以完全模拟IHtmlToPdfConverter的行为,不用调用真实的SelectPdf,也不用发起网络请求。比如用xUnit+Moq写测试用例:
using Xunit; using Moq; public class PdfGeneratorTests { [Fact] public void GetPdf_WithValidUrl_ShouldCallConvertUrlOnceAndReturnResult() { // 准备工作 var mockConverter = new Mock<IHtmlToPdfConverter>(); var testUrl = "https://test.example.com/page"; var mockPdfDoc = new Mock<PdfDocument>(); // 如果PdfDocument是密封类,这里可以用null或者自定义的测试对象 // 设置:当调用ConvertUrl传入testUrl时,返回我们的模拟PDF文档 mockConverter.Setup(c => c.ConvertUrl(testUrl)).Returns(mockPdfDoc.Object); // 实例化要测试的类,传入模拟的转换器 var pdfGenerator = new PdfGenerator(mockConverter.Object); // 执行测试方法 var result = pdfGenerator.GetPdf(testUrl); // 验证:确认ConvertUrl确实被调用了一次,而且参数正确 mockConverter.Verify(c => c.ConvertUrl(testUrl), Times.Once); // 验证返回的结果和我们模拟的一致 Assert.Equal(mockPdfDoc.Object, result); } [Fact] public void GetPdf_WithEmptyUrl_ShouldThrowArgumentException() { // 准备工作 var mockConverter = new Mock<IHtmlToPdfConverter>(); var pdfGenerator = new PdfGenerator(mockConverter.Object); // 执行并验证异常 var exception = Assert.Throws<ArgumentException>(() => pdfGenerator.GetPdf("")); Assert.Equal("请求URL不能为空", exception.Message); } }
小补充:如果不用Mock框架怎么办?
你也可以自己写一个“假的”转换器实现,专门用于测试:
public class FakeHtmlToPdfConverter : IHtmlToPdfConverter { public PdfDocument ConvertUrl(string requestUrl) { // 这里可以返回一个预先构造的PDF文档,或者根据测试需求返回特定值 return new PdfDocument(); } }
测试的时候直接用这个Fake类注入就行,逻辑一样简单。
这样一来,我们的单元测试就完全隔离了外部依赖,只验证我们自己写的逻辑:比如URL参数是否正确传递,空参数是否会抛出异常,是否正确返回转换器的结果——这些才是我们要测的内容。
内容来源于stack exchange
相关产品推荐
相关产品推荐

