依赖注入与单元测试:让代码更易测试的实用技巧
写代码时,你有没有遇到过这种情况:想给某个类写个单元测试,结果发现它直接创建了数据库连接、调用了外部API,甚至还读取了配置文件。一跑测试,要么超时,要么得先把整个环境搭好,烦不胜烦?
这时候,依赖注入(Dependency Injection,简称DI)就能派上用场了。它不只是框架里的高级概念,更是写可测试代码的关键。
为什么依赖注入能帮到单元测试?
想象一下你在做一道菜,需要酱油。如果这道菜的做法是“自己去超市买酱油”,那你每次想试味道都得出门一趟。但如果改成“别人把酱油递给你”,你就可以在厨房里直接换不同牌子的酱油来尝——这就是依赖注入的核心思想。
在代码中,一个类不该自己去“创建”它所依赖的对象,而是应该通过构造函数或方法参数“接收”这些依赖。这样一来,写单元测试的时候,你就可以传入一个“假”的依赖(也就是Mock),而不是真实的、难以控制的服务。
一个简单的例子
比如我们有个订单服务,需要发送邮件通知:
class OrderService {
private EmailService emailService;
public OrderService() {
this.emailService = new EmailService(); // 直接new,问题就在这
}
public void placeOrder(String user) {
// 处理订单逻辑
emailService.send(user, "订单已创建");
}
}这个写法在测试时会真的发邮件,显然不合适。改用依赖注入后:
class OrderService {
private EmailService emailService;
public OrderService(EmailService emailService) {
this.emailService = emailService; // 由外部传入
}
public void placeOrder(String user) {
// 处理订单逻辑
emailService.send(user, "订单已创建");
}
}现在写测试就轻松多了:
[Test]
public void should_send_email_when_order_placed() {
EmailService mockEmailService = Mockito.mock(EmailService.class);
OrderService service = new OrderService(mockEmailService);
service.placeOrder("alice");
verify(mockEmailService).send("alice", "订单已创建");
}你看,根本不需要真正的邮件服务,也能验证逻辑是否正确。
实际开发中的常见做法
很多项目用Spring这类框架管理依赖注入,但即使不用框架,手动传递依赖也是完全可行的。关键在于设计时要有意识:别让类自己去“找”依赖,而是让它“等着被给”。
当你写出这样的代码,单元测试自然就变得简单、快速、稳定。测试不再依赖网络、数据库或第三方服务,可以在本地秒级运行,这对持续集成和日常调试都有巨大帮助。
归根结底,依赖注入不是为了炫技,而是为了让代码更容易被验证。而单元测试,正是验证代码是否靠谱的第一道防线。