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

Hamcrest equalTo比较GString与String内容匹配却返回false的解决方法

问题描述

列表中存储了值类型为GString的参数,使用Hamcrest进行匹配校验时,即便二者作为普通字符串比较时内容完全一致,仍会返回String isn't GString的匹配失败结果。
期望实现Hamcrest在比较过程中自动将GString解析为普通String完成匹配,目前的临时规避方案是在将GString添加到列表时主动调用.toString()方法,但该方案侵入性较强,并不理想。

问题复现代码:

@Test
def stackOverflow() {
    given:
    // values
    def v1 = '01'
    def v2 = '02'
    
    // a groovy string containing them
    def param = "${v1}:${v2}"

    // a matcher using a concrete string
    def matcher = Matchers.equalTo("01:02")
    
    when:
    // add actual into a list
    def mylist = [param]

    then:
    // check that actual Gstring matches expected String and fail
    that mylist, Matchers.hasItem(matcher)
}

报错信息

Condition not satisfied:

that mylist, Matchers.hasItem(matcher)
|    |       |
|    [01:02] class org.hamcrest.Matchers
false

Expected: a collection containing "01:02"
     but: mismatches were: [was <01:02>]
问题根因

匹配失败的核心原因是Hamcrest内置的Matchers.equalTo()依赖Java原生equals()方法做相等判断,而Groovy的GString和普通String的equals()实现自带强类型校验:只要比较双方一个是GStringImpl、一个是String,哪怕字符序列完全一致,equals()也会返回false,才会出现日志里打印内容完全相同但判定不匹配的现象。

解决方案

方案1:自定义兼容GString的通用相等匹配器

一次定义即可全局复用,不需要修改业务侧构造参数的逻辑,比较前会自动做类型转换:

import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher

class StringSafeMatcher extends TypeSafeMatcher<Object> {
    private final String expected

    StringSafeMatcher(Object expected) {
        this.expected = expected?.toString()
    }

    @Override
    protected boolean matchesSafely(Object actual) {
        return expected == actual?.toString()
    }

    @Override
    void describeTo(Description description) {
        description.appendText("string value equal to ").appendValue(expected)
    }

    @Override
    protected void describeMismatchSafely(Object actual, Description mismatchDescription) {
        mismatchDescription.appendText("was ").appendValue(actual?.toString())
    }

    static Matcher<Object> equalToString(Object expected) {
        return new StringSafeMatcher(expected)
    }
}

使用时直接替换原生equalTo即可:

def matcher = StringSafeMatcher.equalToString("01:02")

后续无论集合里存的是GString还是普通String,都会统一转成String做内容比较,不会再出现类型不匹配的问题。

方案2:使用Spock原生断言

从代码的given/when/then结构可以看出使用的是Spock测试框架,Spock本身的断言逻辑对GString做了兼容,不需要依赖Hamcrest就能完成校验:

then:
// 校验列表包含目标字符串
mylist.contains("01:02")
// 或者校验对应位置的值
mylist[0] == "01:02"

Groovy的==操作符和集合contains方法不会卡在GString和String的类型差异上,会自动比较字符序列内容,写法也更简洁。

不推荐方案

手动在每个GString加入集合前调用.toString(),这种方案需要在所有参数构造位置加转换逻辑,漏写就会复现问题,维护成本极高。

内容的提问来源于stack exchange,提问作者Peter Kahn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 00:57:31