You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对时间相关的限流方法进行单元测试?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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 10:21:03