单元测试中Mock/Skip sleep()调用的通用解决方法
单元测试中处理含
sleep()调用方法的通用方案 针对你给出的Foo类场景,要避免测试时等待25秒,以下是几种实用方案,按推荐优先级排序:
1. 抽象时间服务+依赖注入(最佳实践)
核心思路是把睡眠逻辑从业务代码中抽离,通过依赖注入替换实现,测试时用mock服务跳过实际等待。
改造原代码
先定义时间服务接口和生产环境实现:
interface TimeService { void sleepSeconds(long seconds) throws InterruptedException; } class RealTimeService implements TimeService { @Override public void sleepSeconds(long seconds) throws InterruptedException { TimeUnit.SECONDS.sleep(seconds); } }
修改Foo类,通过构造函数注入时间服务:
class Foo { private final TimeService timeService; // 生产环境传入RealTimeService,测试传入mock public Foo(TimeService timeService) { this.timeService = timeService; } public boolean someMethodWithSleep() { boolean failed = false; for (int i = 0; i < 5; i++) { failed = trySomething(); if (failed) { timeService.sleepSeconds(5); } else { return true; } } return false; } protected boolean trySomething() { return false; } }
测试代码(用Mockito)
import org.junit.Test; import org.mockito.Mockito; import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.verify; public class FooTest { @Test public void testFailureScenarioWithoutSleep() throws InterruptedException { TimeService mockTimeService = Mockito.mock(TimeService.class); Foo foo = new Foo(mockTimeService); // 执行测试方法 boolean result = foo.someMethodWithSleep(); // 断言结果 assertFalse(result); // 验证睡眠方法被调用了5次 verify(mockTimeService, Mockito.times(5)).sleepSeconds(5); } }
这种方式符合依赖倒置原则,代码耦合度低,测试维护性好。
2. 提取可覆写的睡眠方法(轻量重构方案)
如果不想引入额外的服务类,可以把睡眠逻辑抽成protected方法,测试时通过子类覆写跳过等待。
改造原代码
class Foo { public boolean someMethodWithSleep() { boolean failed = false; for (int i = 0; i < 5; i++) { failed = trySomething(); if (failed) { sleepFiveSeconds(); } else { return true; } } return false; } protected boolean trySomething() { return false; } // 提取为可覆写方法 protected void sleepFiveSeconds() throws InterruptedException { TimeUnit.SECONDS.sleep(5); } }
测试代码
import org.junit.Test; import static org.junit.Assert.assertFalse; public class FooTest { @Test public void testFailureScenario() throws InterruptedException { Foo testFoo = new Foo() { @Override protected boolean trySomething() { return false; // 模拟失败场景 } @Override protected void sleepFiveSeconds() { // 空实现,跳过睡眠 } }; boolean result = testFoo.someMethodWithSleep(); assertFalse(result); } }
这个方案改动小,适合快速调整现有代码。
3. Mock静态sleep方法(不推荐,仅用于无法重构的场景)
如果完全不能修改原代码,可以用PowerMock直接mock静态的TimeUnit.SECONDS.sleep()方法,但这种方式会修改类加载器,增加测试维护成本,仅作为最后手段。
测试代码
import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.when; @RunWith(PowerMockRunner.class) @PrepareForTest(TimeUnit.class) public class FooTest { @Test public void testFailureScenario() throws InterruptedException { // Mock静态sleep方法,使其不执行实际等待 PowerMockito.mockStatic(TimeUnit.class); PowerMockito.doNothing().when(TimeUnit.SECONDS).sleep(5); // Spy原类,mock trySomething返回false Foo spyFoo = PowerMockito.spy(new Foo()); when(spyFoo.trySomething()).thenReturn(false); boolean result = spyFoo.someMethodWithSleep(); assertFalse(result); // 验证sleep被调用5次 PowerMockito.verifyStatic(TimeUnit.SECONDS, PowerMockito.times(5)); TimeUnit.SECONDS.sleep(5); } }
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

