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

使用BeanMatchers测试含Instant字段的Bean的hashCode时生成唯一值失败的异常排查求助

BeanMatchers测试含Instant字段的Bean的hashCode时生成唯一值失败的异常排查求助

问题描述

我最近在测试一个包含Instant字段的Bean的hashCode方法时遇到了异常,折腾了半天没搞懂原因,特来请教。

我的测试代码如下:

@Test
public void testHash() {
    BeanMatchers.registerValueGenerator(Instant::now, Instant.class);
    assertThat(ClassForTest.class, hasValidBeanHashCode());
}

对应的Bean类:

class ClassForTest {
    private Instant instant;

    public Instant getInstant() {
        return instant;
    }

    public void setInstant(Instant instant) {
        this.instant = instant;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ClassForTest that = (ClassForTest) o;
        return Objects.equals(instant, that.instant);
    }

    @Override
    public int hashCode() {
        return Objects.hash(instant);
    }
}

使用的BeanMatchers依赖:

<dependency>
    <groupId>com.google.code.bean-matchers</groupId>
    <artifactId>bean-matchers</artifactId>
    <version>0.14</version>
    <scope>test</scope>
</dependency>

每次运行都会抛出这个异常:

BeanMatchers Could not generate two distinct values after 128 attempts of type java.time.Instant

我觉得测试逻辑是对的,为啥会出现这个问题?怎么解决?


我的解答

嘿,这个问题我碰到过!原因其实很简单:

BeanMatchers的hasValidBeanHashCode()校验规则,需要生成两个字段值不同的Bean实例,来验证“不同实例的hashCode应该尽量不同”这个约定。而你注册的Instant::now作为值生成器,在测试代码快速调用的情况下,很可能因为系统时钟的精度限制(比如很多系统的时钟精度是毫秒级,甚至有些更低),导致连续调用Instant.now()返回的是同一个值——毕竟128次调用可能都在同一毫秒内完成,自然生成不了两个不同的Instant。

解决办法也很直接,自定义一个能保证生成不同Instant的生成器就行,给你几个方案:

方案1:每次生成的Instant递增一个纳秒

BeanMatchers.registerValueGenerator(() -> Instant.now().plusNanos(1), Instant.class);

这样哪怕两次调用的now()值一样,加上1纳秒后就肯定不同了。

方案2:用计数器生成绝对唯一的Instant

如果不想依赖系统时钟,也可以用计数器来生成固定递增的Instant:

AtomicLong counter = new AtomicLong(System.currentTimeMillis());
BeanMatchers.registerValueGenerator(() -> Instant.ofEpochMilli(counter.getAndIncrement()), Instant.class);

这个方式更稳定,每次生成的Instant都是严格递增的,绝对不会重复。

方案3:直接用两个固定的不同Instant值

如果只是为了通过测试,也可以直接指定两个不同的Instant作为生成器的返回值(比如第一次返回Instant.MIN,第二次返回Instant.MAX),不过这种方式灵活性稍差,但胜在简单。

总之核心就是:给BeanMatchers提供一个能稳定生成不同Instant实例的生成器,代替原来的Instant::now,就能解决这个异常啦!

备注:内容来源于stack exchange,提问作者user471011

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.20 10:29:41