如何对时间相关的限流方法进行单元测试?method参数类型如何优化
单元测试编写方案
首先要解决时间依赖问题,不要让单测真实等待1秒,建议在CallChecker实现中抽象出时间获取接口,单测时通过mock返回固定时间即可,覆盖以下核心场景:
- 同IP同method1秒内调用10次,全部返回true
- 同IP同method1秒内第11次调用,返回false
- 跨秒后限流计数重置,第11次调用落到下一秒时返回true
- 同IP不同method,限流规则互不影响
- 同method不同IP,限流规则互不影响
- 空参数等边界场景(如业务要求参数非空可测试参数校验逻辑)
以下为JUnit5+Mockito的单测示例:
@ExtendWith(MockitoExtension.class) public class CallCheckerTest { @Mock private TimeSource timeSource; // 自定义的时间获取接口,用于mock时间 private CallChecker callChecker; @BeforeEach void setUp() { // 假设DefaultCallChecker是你实现的CallChecker实现类 callChecker = new DefaultCallChecker(timeSource); } @Test void sameIpSameMethod_10CallsInOneSecond_AllReturnTrue() { when(timeSource.currentTimeMillis()).thenReturn(1000L); String testIp = "192.168.1.1"; String testMethod = "com.xxx.user.getUserInfo"; for (int i = 0; i < 10; i++) { assertTrue(callChecker.canCall(testIp, testMethod)); } } @Test void sameIpSameMethod_11thCallInOneSecond_ReturnFalse() { when(timeSource.currentTimeMillis()).thenReturn(1000L); String testIp = "192.168.1.1"; String testMethod = "com.xxx.user.getUserInfo"; for (int i = 0; i < 10; i++) { callChecker.canCall(testIp, testMethod); } assertFalse(callChecker.canCall(testIp, testMethod)); } @Test void sameIpSameMethod_nextSecond_LimitReset() { String testIp = "192.168.1.1"; String testMethod = "com.xxx.user.getUserInfo"; // 第一秒调用10次打满限流 when(timeSource.currentTimeMillis()).thenReturn(1000L); for (int i = 0; i < 10; i++) { callChecker.canCall(testIp, testMethod); } // 时间跳到下一秒,计数重置可以正常调用 when(timeSource.currentTimeMillis()).thenReturn(2000L); assertTrue(callChecker.canCall(testIp, testMethod)); } }
附加问题:method参数的更合适类型
有比Object更合适的类型,原因和推荐方案如下:
- 用
Object作为参数存在明显缺陷:限流逻辑需要用method作为key统计次数,必须要求传入的Object正确重写了equals和hashCode方法,否则同一个业务方法如果传入不同的实例对象,会被识别为不同的key,直接导致限流失效,同时没有类型约束,任何无关对象都可以传入,容易出bug。 - 最推荐用
String类型:直接用方法的全限定名(比如com.xxx.service.UserService.getUser)作为标识,String本身已经正确实现了equals和hashCode,可读性高,性能也很好。 - 也可以选择
java.lang.reflect.Method类型:反射的Method类已经正确重写了equals和hashCode,能唯一标识一个方法,缺点是比String重,动态代理生成的方法可能存在兼容问题。 - 哪怕自定义一个
MethodIdentity标记接口约束参数类型,也比无约束的Object更安全。
内容的提问来源于stack exchange,提问作者Deqing
相关产品推荐
相关产品推荐

