写代码时,谁还没碰上过那种改一处、崩一片的情况?尤其是做安全软件开发,逻辑复杂、模块交错,测一个功能得先把数据库连上、网络通了、配置文件配好,光准备环境就得半小时。这时候,依赖注入(DI)加单元测试的组合就派上大用场了。
为什么依赖注入让测试更轻松
想象一下你在写一个用户登录模块,里面直接 new 了一个数据库连接类。想测登录逻辑?不好意思,得先起个数据库。可要是通过构造函数把数据库操作作为依赖传进来,测试时就能塞一个“假”的数据库实现进去——不连真实服务,只返回预设数据。这样测的是逻辑本身,而不是整个系统。
依赖注入的核心就是“别自己创建依赖,让别人给你”。这样一来,运行时给真对象,测试时给假对象(mock),隔离性拉满。
一个简单的例子
比如有这么一个类,负责检查用户是否有权限访问某个资源:
public class AccessControl {
private IUserRepository _userRepository;
public AccessControl(IUserRepository userRepository) {
_userRepository = userRepository;
}
public bool HasAccess(string userId, string resource) {
var user = _userRepository.FindById(userId);
if (user == null) return false;
return user.Role == "admin" || user.AllowedResources.Contains(resource);
}
}
现在要写单元测试,完全不需要真实用户数据。可以用 Moq 这样的库造个假的 IUserRepository:
[TestMethod]
public void AdminUser_CanAccessAnyResource() {
// Arrange
var mockRepo = new Mock<IUserRepository>();
mockRepo.Setup(r => r.FindById("u123"))
.Returns(new User { Role = "admin", AllowedResources = new List<string>() });
var control = new AccessControl(mockRepo.Object);
// Act
bool result = control.HasAccess("u123", "/delete-all");
// Assert
Assert.IsTrue(result);
}
这个测试跑起来飞快,不依赖任何外部系统,还能精准覆盖边界情况,比如用户为空、权限不足等。
对安全软件的意义
安全相关的代码往往涉及权限校验、行为拦截、日志记录等关键路径。一旦出错,轻则功能失效,重则留下漏洞。用依赖注入配合单元测试,能把这些核心逻辑拆出来单独验证。比如审计模块是否正确记录了敏感操作,不需要真的去触发一次攻击,只要 mock 相关服务,验证调用记录就行。
而且,这样的代码结构更清晰。每个类职责明确,依赖一目了然,新人接手也不容易踩坑。长期维护中,重构也更有底气——只要测试全过,基本不怕改坏。
实际项目里,见过太多把各种服务直接 new 在方法里的写法。结果一写测试就卡住,最后干脆不写了。慢慢地,代码越来越僵,没人敢动。而从一开始就用依赖注入,其实是给自己留条退路,也是给团队留份安心。