如何Mock构造函数内初始化的非传入对象?优化代码可测试性
问题解答
1. 当前代码无法Mock GetData的原因
你在MynewClass的构造函数里,明明参数接收了IHttpRestClient实例,却硬编码实例化了HttpRestClient具体类,完全忽略了传入的抽象实例。这种写法直接把依赖钉死在具体实现上,单元测试时根本无法替换成Mock对象。
2. 怎么Mock GetData方法?
核心是放弃在类内部实例化HttpRestClient,直接使用构造函数传入的IHttpRestClient实例。修改MynewClass的构造函数:
class MynewClass: IMynewInterface { private readonly IService1 _service1; private readonly IService2 _service2; private readonly IHttpRestClient _client; public MynewClass(IService1 service1, IService2 service2, IHttpRestClient restclient) { _service1 = service1; _service2 = service2; // 直接用传入的抽象实例,不再自己new _client = restclient; } public void MyMethod() { // Validation logic var data = _client.GetData(url); // 后续处理 } }
之后用Mock框架(比如Moq)就能轻松模拟GetData的行为,示例代码:
// 创建IHttpRestClient的Mock对象 var mockRestClient = new Mock<IHttpRestClient>(); // 模拟GetData方法的返回值 mockRestClient.Setup(c => c.GetData(It.IsAny<string>())) .Returns("模拟的返回数据"); // 准备其他依赖的Mock对象 var mockService1 = new Mock<IService1>(); var mockService2 = new Mock<IService2>(); // 实例化MynewClass,传入所有Mock对象 var myClass = new MynewClass(mockService1.Object, mockService2.Object, mockRestClient.Object); // 调用测试方法 myClass.MyMethod(); // 验证GetData是否被正确调用 mockRestClient.Verify(c => c.GetData(It.IsAny<string>()), Times.Once);
3. 更易于单元测试的代码写法
遵循依赖反转原则和构造函数注入是关键:
- 永远依赖抽象(比如
IHttpRestClient)而非具体实现(HttpRestClient) - 所有外部依赖都通过构造函数传入,不在类内部手动实例化
- 把具体类的实例化工作交给DI容器(比如ASP.NET Core内置容器):在容器中注册
IHttpRestClient与HttpRestClient的映射,以及IService1、IService2等依赖,运行时由容器自动注入所有实例。
这样不仅MynewClass的单元测试变得简单,整个项目的依赖关系也更清晰,扩展性更强。
内容的提问来源于stack exchange,提问作者Programmerzzz
相关产品推荐
相关产品推荐

