LoginContext()をインスタンス化するnew()呼び出しを含むレガシークラスがあります。
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
}
}
インスタンス化する前にJAASセキュリティを設定する必要があるため、Mockitoを使用してこのクラスをテストし、LoginContextをモックしたいと思いますが、login()メソッドを変更してLoginContextを外部化せずにそれを行う方法はわかりません。 Mockitoを使用してLoginContextクラスをモックすることは可能ですか?
将来的には Eran Harelの答え (モック可能なnew
を工場に移動するリファクタリング)をお勧めします。しかし、元のソースコードを変更したくない場合は、非常に便利でユニークな機能を使用してください:spies。 ドキュメント から:
実際のオブジェクトのスパイを作成できます。スパイを使用すると、realメソッドが呼び出されます(メソッドがスタブ化されていない場合)。
たとえば、レガシーコードを扱う場合は、実際のスパイを慎重に、ときどき使用する必要があります。
あなたの場合、あなたは書くべきです:
TestedClass tc = spy(new TestedClass());
LoginContext lcMock = mock(LoginContext.class);
when(tc.login(anyString(), anyString())).thenReturn(lcMock);
私はすべてEran Harelのソリューションに賛成しています。それが不可能な場合、Tomasz Nurkiewiczのスパイに関する提案は素晴らしいものです。ただし、どちらも当てはまらない状況があることに注意してください。例えば。 login
メソッドが少し「beefier」だった場合:
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
}
}
...これは、新しいLoginContext
の初期化を独自のメソッドに抽出し、前述の解決策の1つを適用するためにリファクタリングできない古いコードでした。
完全を期すために、3番目の手法に言及する価値があります。 PowerMock を使用して、new
演算子が呼び出されたときにモックオブジェクトを注入します。ただし、PowerMockは特効薬ではありません。それはモックするクラスにバイトコード操作を適用することで機能しますが、テストされたクラスがバイトコード操作またはリフレクションを採用している場合は危険な練習になる可能性があり、少なくとも私の個人的な経験から、テストにパフォーマンスヒットをもたらすことが知られています。繰り返しますが、他のオプションがない場合、唯一のオプションは良いオプションでなければなりません:
@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
}
}
ファクトリを使用して、ログインコンテキストを作成できます。その後、ファクトリをモックして、テストに必要なものを返すことができます。
public class TestedClass {
private final LoginContextFactory loginContextFactory;
public TestedClass(final LoginContextFactory loginContextFactory) {
this.loginContextFactory = loginContextFactory;
}
public LoginContext login(String user, String password) {
LoginContext lc = loginContextFactory.createLoginContext();
}
}
public interface LoginContextFactory {
public LoginContext createLoginContext();
}
_ public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
}
}
_
-テストクラス:
_ @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);
//comment: this is giving mock object ( lcMock )
TestedClass tc = new TestedClass();
tc.login ("something", "something else"); /// testing this method.
// test the login's logic
}
}
_
TestLogin()から実際のメソッドtc.login ("something", "something else");
を呼び出す場合{-このLoginContext lcはnullに設定され、lc.doThis();
の呼び出し中にNPEをスローします
私が知っていることではありませんが、テストしたいTestedClassのインスタンスを作成するときにこのようなことをするのはどうですか:
TestedClass toTest = new TestedClass() {
public LoginContext login(String user, String password) {
//return mocked LoginContext
}
};
別のオプションは、Mockitoを使用してTestedClassのインスタンスを作成し、模擬インスタンスがLoginContextを返すようにすることです。
テスト対象のクラスを変更できる状況で、バイトコードの操作を避けたり、物事を高速にしたり、サードパーティの依存関係を最小限に抑えることが望ましい場合、ファクトリを使用してnew
を抽出します操作。
public class TestedClass {
interface PojoFactory { Pojo getNewPojo(); }
private final PojoFactory factory;
/** For use in production - nothing needs to change. */
public TestedClass() {
this.factory = new PojoFactory() {
@Override
public Pojo getNewPojo() {
return new Pojo();
}
};
}
/** For use in testing - provide a pojo factory. */
public TestedClass(PojoFactory factory) {
this.factory = factory;
}
public void doSomething() {
Pojo pojo = this.factory.getNewPojo();
anythingCouldHappen(pojo);
}
}
これを配置すると、Pojoオブジェクトでの呼び出しのテスト、アサート、検証が簡単になります。
public void testSomething() {
Pojo testPojo = new Pojo();
TestedClass target = new TestedClass(new TestedClass.PojoFactory() {
@Override
public Pojo getNewPojo() {
return testPojo;
}
});
target.doSomething();
assertThat(testPojo.isLifeStillBeautiful(), is(true));
}
このアプローチの唯一の欠点は、TestClass
に余分なパラメーターで複製する必要がある複数のコンストラクターがある場合に発生する可能性があります。
SOLID=.
public class Pojo {
interface PojoFactory { Pojo getNewPojo(); }
public static final PojoFactory productionFactory =
new PojoFactory() {
@Override
public Pojo getNewPojo() {
return new Pojo();
}
};