私はMockitoが初めてです。
以下のクラスで、someMethod
が呼び出された後にfoo
が一度だけ呼び出されたことをMockitoを使って確認するにはどうすればよいですか?
public class Foo
{
public void foo(){
Bar bar = new Bar();
bar.someMethod();
}
}
次の確認電話をかけたいです。
verify(bar, times(1)).someMethod();
ここでbar
はBar
の擬似インスタンスです。
Barインスタンス、またはBarインスタンスを作成するために使用されるファクトリ(またはこれを行う他の483の方法のうちの1つ)を注入すると、テストを実行するために必要なアクセス権が得られます。
工場の例:
このように書かれたFooクラスを考えます。
public class Foo {
private BarFactory barFactory;
public Foo(BarFactory factory) {
this.barFactory = factory;
}
public void foo() {
Bar bar = this.barFactory.createBar();
bar.someMethod();
}
}
あなたのテストメソッドでは、このようにBarFactoryを注入することができます:
@Test
public void testDoFoo() {
Bar bar = mock(Bar.class);
BarFactory myFactory = new BarFactory() {
public Bar createBar() { return bar;}
};
Foo foo = new Foo(myFactory);
foo.foo();
verify(bar, times(1)).someMethod();
}
おまけ:これは、TDDがコードの設計をどのように推進できるかの例です。
古典的な反応は、「あなたはしない」です。内部ではなくFoo
のパブリックAPIをテストします。
foo()
の影響を受けているFoo
オブジェクト(あるいは環境内の他のオブジェクト)の動作はありますか?もしそうなら、それをテストします。そうでなければ、メソッドは何をしますか?
DIや工場を使いたくない場合あなたは少しトリッキーな方法であなたのクラスをリファクタリングすることができます:
public class Foo {
private Bar bar;
public void foo(Bar bar){
this.bar = (bar != null) ? bar : new Bar();
bar.someMethod();
this.bar = null; // for simulating local scope
}
}
そしてあなたのテストクラス:
@RunWith(MockitoJUnitRunner.class)
public class FooTest {
@Mock Bar barMock;
Foo foo;
@Test
public void testFoo() {
foo = new Foo();
foo.foo(barMock);
verify(barMock, times(1)).someMethod();
}
}
それからあなたのfooメソッドを呼んでいるクラスはこのようにするでしょう:
public class thirdClass {
public void someOtherMethod() {
Foo myFoo = new Foo();
myFoo.foo(null);
}
}
このようにメソッドを呼び出すときにわかるように、Barクラスをfooメソッドを呼び出している他のクラスにインポートする必要はありません。
もちろん、欠点は、呼び出し側にBarオブジェクトの設定を許可していることです。
それが役に立てば幸い。
PowerMockito.whenNew
を使用したサンプルコードの解決策
FooTest.Java
package foo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
//Both @PrepareForTest and @RunWith are needed for `whenNew` to work
@RunWith(PowerMockRunner.class)
@PrepareForTest({ Foo.class })
public class FooTest {
// Class Under Test
Foo cut;
@Mock
Bar barMock;
@Before
public void setUp() throws Exception {
cut = new Foo();
}
@After
public void tearDown() {
cut = null;
}
@Test
public void testFoo() throws Exception {
// Setup
PowerMockito.whenNew(Bar.class).withNoArguments()
.thenReturn(this.barMock);
// Test
cut.foo();
// Validations
Mockito.verify(this.barMock, Mockito.times(1)).someMethod();
}
}
私はMockitoの@InjectMocks
が行くべき道だと思います。
あなたの意図に応じて、あなたは使うことができます:
docs の詳細情報
以下はフィールドインジェクションの例です。
クラス:
public class Foo
{
private Bar bar = new Bar();
public void foo()
{
bar.someMethod();
}
}
public class Bar
{
public void someMethod()
{
//something
}
}
テスト:
@RunWith(MockitoJUnitRunner.class)
public class FooTest
{
@Mock
Bar bar;
@InjectMocks
Foo foo;
@Test
public void FooTest()
{
doNothing().when( bar ).someMethod();
foo.foo();
verify(bar, times(1)).someMethod();
}
}
はい、本当にやりたい、または必要な場合は、PowerMockを使用できます。これは最後の手段と考えるべきです。 PowerMockを使うと、呼び出しからコンストラクターへのモックを返させることができます。その後、モックで確認をしてください。とは言っても、csturtzが「正しい」答えです。
別の簡単な方法は、bar.someMethod()にログステートメントを追加し、テストの実行時に上記のメッセージが表示されることを確認することです。こちらの例を参照してください: ロガー内のメッセージ
これは、Bar.someMethod()がprivate
の場合に特に便利です。