无法注入Mock对象时,如何测试MyMethod返回false并正确重构类?
解决方法:重构以实现可测试性(无需全局依赖注入)
针对你的问题,有两种轻量的重构方案,既不需要引入全局依赖注入,也能完全隔离外部服务器依赖,实现对searchResult == null场景的测试:
方案一:提取连接创建逻辑为受保护虚拟方法
这是改动最小的方案,通过将创建LdapConnection的逻辑封装成受保护的虚拟方法,让测试项目可以通过子类重写来替换连接实例:
1. 重构原类
public class MyConnectionHandler { public bool MyMethod() { var myConnection = CreateLdapConnection(); var searchResult = myConnection.Search(...); if (searchResult == null) return false; return true; } // 封装连接创建逻辑,供测试重写 protected virtual LdapConnection CreateLdapConnection() { return new LdapConnection(...); } }
2. 创建测试用子类
在测试项目中定义子类,重写CreateLdapConnection方法返回Mock的连接实例:
public class TestableMyConnectionHandler : MyConnectionHandler { private readonly LdapConnection _mockConnection; public TestableMyConnectionHandler(LdapConnection mockConnection) { _mockConnection = mockConnection; } protected override LdapConnection CreateLdapConnection() { return _mockConnection; } }
3. 编写单元测试
使用Mock框架(如Moq)让Search方法返回null,验证MyMethod返回false:
[Test] public void MyMethod_WhenSearchResultIsNull_ReturnsFalse() { // Arrange var mockLdap = new Mock<LdapConnection>(); // 替换为实际的Search方法参数 mockLdap.Setup(c => c.Search(It.IsAny<string>(), It.IsAny<SearchScope>(), ...)) .Returns(null); var handler = new TestableMyConnectionHandler(mockLdap.Object); // Act var result = handler.MyMethod(); // Assert Assert.IsFalse(result); }
方案二:使用内部工厂接口
如果更倾向于遵循依赖倒置原则,可通过内部工厂接口封装连接创建逻辑,同时限制接口的可见范围(仅当前程序集和测试项目可见):
1. 重构原类及添加工厂
在MyConnectionHandler所在的程序集中添加内部接口和默认实现:
// 仅当前程序集可见的工厂接口 internal interface ILdapConnectionFactory { LdapConnection Create(); } // 默认工厂实现 internal class DefaultLdapConnectionFactory : ILdapConnectionFactory { public LdapConnection Create() { return new LdapConnection(...); } } public class MyConnectionHandler { private readonly ILdapConnectionFactory _connectionFactory; // 对外公开的构造函数,使用默认工厂 public MyConnectionHandler() : this(new DefaultLdapConnectionFactory()) { } // 内部构造函数,仅测试项目可访问 internal MyConnectionHandler(ILdapConnectionFactory connectionFactory) { _connectionFactory = connectionFactory; } public bool MyMethod() { var myConnection = _connectionFactory.Create(); var searchResult = myConnection.Search(...); if (searchResult == null) return false; return true; } }
2. 配置测试项目访问内部成员
在原程序集的AssemblyInfo.cs中添加以下代码,允许测试项目访问内部成员:
[assembly: InternalsVisibleTo("YourTestProjectName")]
3. 编写单元测试
直接Mock工厂和连接实例,注入到MyConnectionHandler中:
[Test] public void MyMethod_WhenSearchResultIsNull_ReturnsFalse() { // Arrange var mockLdap = new Mock<LdapConnection>(); mockLdap.Setup(c => c.Search(...)).Returns(null); var mockFactory = new Mock<ILdapConnectionFactory>(); mockFactory.Setup(f => f.Create()).Returns(mockLdap.Object); var handler = new MyConnectionHandler(mockFactory.Object); // Act var result = handler.MyMethod(); // Assert Assert.IsFalse(result); }
方案对比
- 方案一:代码改动极少,适合简单场景,无需额外类型定义,但依赖子类重写虚拟方法。
- 方案二:更符合面向设计原则,扩展性更强,若后续需修改连接创建逻辑,无需改动
MyConnectionHandler核心代码,但需要额外定义接口和工厂类。
两种方案都完全避免了依赖外部服务器,且无需引入全局依赖注入容器,仅在测试场景下替换连接实例。
内容的提问来源于stack exchange,提问作者HasQuestionsAndAnswers
相关产品推荐
相关产品推荐

