使用 Mockito 在其中使用 new() 调用的测试类

2022-08-31 11:57:20

我有一个遗留类,其中包含实例化对象的调用:new()LoginContext

public class TestedClass {
  public LoginContext login(String user, String password) {
    LoginContext lc = new LoginContext("login", callbackHandler);
  }
}

我想使用Mockito来测试这个类来模拟,因为它要求在实例化之前设置JAAS安全的东西,但我不确定如何在不更改方法以外部化.LoginContextlogin()LoginContext

有没有可能使用Mockito来模拟班级?LoginContext


答案 1

对于未来,我会推荐Eran Harel的答案(重构移动到工厂,这是可以被嘲笑的)。但是,如果您不想更改原始源代码,请使用非常方便且独特的功能:间谍。从文档中new

您可以创建真实对象的间谍。当您使用间谍时,将调用真正的方法(除非方法被存根)。

真正的间谍应该谨慎和偶尔使用,例如在处理遗留代码时。

在你的情况下,你应该写:

TestedClass tc = spy(new TestedClass());
LoginContext lcMock = mock(LoginContext.class);
when(tc.login(anyString(), anyString())).thenReturn(lcMock);

答案 2

我完全支持Eran Harel的解决方案,在不可能的情况下,Tomasz Nurkiewicz的间谍建议非常好。但是,值得注意的是,在某些情况下,两者都不适用。例如,如果方法有点“更强”:login

public class TestedClass {
    public LoginContext login(String user, String password) {
        LoginContext lc = new LoginContext("login", callbackHandler);
        lc.doThis();
        lc.doThat();
        return lc;
    }
}

...这是旧代码,无法重构以提取新代码的初始化到自己的方法并应用上述解决方案之一。LoginContext

为了完整性的缘故,值得一提的第三种技术 - 在调用运算符时使用PowerMock注入模拟对象。不过,PowerMock并不是一颗银弹。它的工作原理是对它模拟的类应用字节码操作,如果测试的类使用字节代码操作或反射,这可能是狡猾的做法,至少从我个人的经验来看,已知会在测试中引入性能影响。再说一遍,如果没有其他选项,则唯一的选项必须是好选项:new

@RunWith(PowerMockRunner.class)
@PrepareForTest(TestedClass.class)
public class TestedClassTest {

    @Test
    public void testLogin() {
        LoginContext lcMock = mock(LoginContext.class);
        whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
        TestedClass tc = new TestedClass();
        tc.login ("something", "something else");
        // test the login's logic
    }
}

推荐