Java 使用StringBuilder作为参数失败时

Java 使用StringBuilder作为参数失败时,java,mockito,Java,Mockito,下面是JUnit测试,oneLoginAuthUtil被模拟。但是Mockito.when返回null时oneLoginAuthUtil.getMetaData始终为null。以下是守则- public void func() throws Exception { StringBuilder b = new StringBuilder("test"); RequestContext context = new RequestContext(); Mockito.when(

下面是JUnit测试,
oneLoginAuthUtil
被模拟。但是
Mockito.when
返回
null
oneLoginAuthUtil.getMetaData
始终为
null
。以下是守则-

public void func() throws Exception {
    StringBuilder b = new StringBuilder("test");
    RequestContext context = new RequestContext();
    Mockito.when(oneLoginAuthUtil.getMetaData(context, b)).thenReturn("abcdef");
    ResponseEntity<Object> response = loginControllerImpl.handleGetMetaDataEndPointImpl(context);
}

public String getMetaData(RequestContext context, StringBuilder b) throws Exception {
    Auth auth = getOneLoginAuthObject(context);
    final Saml2Settings settings = auth.getSettings();
    String metadata = settings.getSPMetadata();
    List<String> errors = Saml2Settings.validateMetadata(metadata);
    if (!errors.isEmpty()) {
        b.append(errors.toString());
        throw new SSOException("metadata_validation_error");
    }
    return metadata;
}

public ResponseEntity<Object> handleGetMetaDataEndPointImpl(RequestContext context) {
    try {
        StringBuilder b = new StringBuilder();
        String metadata = oneLoginAuthUtil.getMetaData(context, b);
        log.info(metadata);
        return new ResponseEntity<>(metadata, new HttpHeaders(), HttpStatus.CREATED);
    } catch (Exception e) {
        return new ResponseEntity<>("<error>Exception: " + e.getMessage() + "</error>", new HttpHeaders(),
                HttpStatus.CREATED);
    }
}
public void func()引发异常{
StringBuilder b=新StringBuilder(“测试”);
RequestContext=新的RequestContext();
Mockito.when(oneLoginAuthUtil.getMetaData(context,b)).thenReturn(“abcdef”);
ResponseEntity response=loginControllerImpl.handleGetMetaDataEndPointImpl(上下文);
}
公共字符串getMetaData(RequestContext上下文,StringBuilder b)引发异常{
Auth Auth=getOneLoginAuthObject(上下文);
final Saml2Settings settings=auth.getSettings();
String metadata=settings.getSPMetadata();
列表错误=Saml2Settings.validateMetadata(元数据);
如果(!errors.isEmpty()){
b、 追加(errors.toString());
抛出新SSO异常(“元数据验证错误”);
}
返回元数据;
}
公共响应handleGetMetaDataEndPointImpl(RequestContext上下文){
试一试{
StringBuilder b=新的StringBuilder();
String metadata=oneLoginAuthUtil.getMetaData(上下文,b);
log.info(元数据);
返回新的ResponseEntity(元数据,新的HttpHeaders(),HttpStatus.CREATED);
}捕获(例外e){
返回新的ResponseEntity(“异常:+e.getMessage()+”,新的HttpHeaders(),
HttpStatus.CREATED);
}
}

但是不使用
StringBuilder
作为参数的
oneLoginAuthUtil.getMetaData
方法工作正常。

它返回
null
,因为当您使用特定实例作为参数模拟方法时,当且仅当测试时提供的参数与模拟时提供的参数相等时,Mockito才会返回映射的返回值,但情况显然并非如此。Mockito没有匹配项,因此它返回一个方法的默认值,该方法返回在模拟上调用的对象,该对象为
null

换句话说,
Mockito.when(oneLoginAuthUtil.getMetaData(context,b))
相当于
Mockito.when(oneLoginAuthUtil.getMetaData(Mockito.eq(context),Mockito.eq(b))
,因为类
StringBuilder
不会重写方法
equals(Object obj)
,模拟时提供的实例与测试时提供的实例不相等,因为它们不是同一实例(在依赖
equals(Object obj)
的默认实现时唯一可能相等),因此您可以获得此行为

因此,您应该使用
Mockito.any(StringBuilder.class)
来匹配类型为
StringBuilder
的任何实例,这样实例就不需要再相等了,您的代码将是:

Mockito.when(
    oneLoginAuthUtil.getMetaData(Mockito.eq(context), Mockito.any(StringBuilder.class))
).thenReturn("abcdef");
@RunWith(MockitoJUnitRunner.class)
public class LoginControllerImplTest {

    @Mock
    private OneLoginAuthUtil oneLoginAuthUtil;
    @InjectMocks
    private LoginControllerImpl loginControllerImpl;

    @Test
    public void func() throws Exception {
        RequestContext context = new RequestContext();
        Mockito.when(
            oneLoginAuthUtil.getMetaData(
                Mockito.eq(context), Mockito.any(StringBuilder.class)
            )
        ).thenReturn("abcdef");

        ResponseEntity<Object> response 
            = loginControllerImpl.handleGetMetaDataEndPointImpl(context);
        ...
    }
}
假设您的主类是
LoginControllerImpl
,并且它具有类型为
oneLoginAuthUtil
的成员字段
oneLoginAuthUtil
,您可以使用注释
@InjectMocks
直接注入类型为
oneLoginAuthUtil
的mock,那么完整的代码将是:

Mockito.when(
    oneLoginAuthUtil.getMetaData(Mockito.eq(context), Mockito.any(StringBuilder.class))
).thenReturn("abcdef");
@RunWith(MockitoJUnitRunner.class)
public class LoginControllerImplTest {

    @Mock
    private OneLoginAuthUtil oneLoginAuthUtil;
    @InjectMocks
    private LoginControllerImpl loginControllerImpl;

    @Test
    public void func() throws Exception {
        RequestContext context = new RequestContext();
        Mockito.when(
            oneLoginAuthUtil.getMetaData(
                Mockito.eq(context), Mockito.any(StringBuilder.class)
            )
        ).thenReturn("abcdef");

        ResponseEntity<Object> response 
            = loginControllerImpl.handleGetMetaDataEndPointImpl(context);
        ...
    }
}
@RunWith(MockitoJUnitRunner.class)
公共类LoginControllerImpletest{
@嘲弄
私人OneLoginAuthUtil OneLoginAuthUtil;
@注射模拟
私人登录控制mpl登录控制mpl;
@试验
public void func()引发异常{
RequestContext=新的RequestContext();
莫基托,什么时候(
oneLoginAuthUtil.getMetaData(
Mockito.eq(上下文),Mockito.any(StringBuilder.class)
)
)。然后返回(“abcdef”);
反应性反应
=loginControllerImpl.handleGetMetaDataEndPointImpl(上下文);
...
}
}

StringBuilder
未实现
equals()
hashCode()
,因此不能用于类似的参数匹配


真正的问题是,您不应该将可变对象(例如
StringBuilder
)传递给测试对象。为什么返回“元数据”要求您在范围之外附加到
StringBuilder

您确定对实际调用和
when()使用相同的对象
context
b
。然后返回()
?通过实际调用
getMetaData
函数显示测试的完整代码。
getMetaData
方法的返回语句在哪里?添加了完整代码。谢谢Nicolas。但是问题是StringBuilder作为param,如果我只有RequestContext Mockito.when可以正常工作。啊,好的,那么对StringBuilder使用any,对RequestContextNo使用eq仍然为null。下面是代码StringBuilder b=新StringBuilder(“测试”);RequestContext=新的RequestContext();Mockito.when(oneLoginAuthUtil.getMetaData(Mockito.eq(context),Mockito.any(StringBuilder.class)),然后返回(“abcdef”);如果两者都使用any,那么如果它仍然不起作用,您的问题在别处,因为它在my sideStringBuilder上正常工作。它用于在元数据中附加错误,并将这些错误记录回func()中