Springで自動配線された@ValueフィールドをMockitoでモックするにはどうすればよいですか?


123

Spring 3.1.4.RELEASEとMockito 1.9.5を使用しています。私の春のクラスでは:

@Value("#{myProps['default.url']}")
private String defaultUrl;

@Value("#{myProps['default.password']}")
private String defaultrPassword;

// ...

私が現在そのように設定しているJUnitテストから:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest 
{ 

「defaultUrl」フィールドの値をモックしたいのですが。他のフィールドの値をモックしたくないことに注意してください。「defaultUrl」フィールドのみをそのままにしたいと思います。またsetDefaultUrl、クラスには明示的な「setter」メソッド(例:)がなく、テストのためだけにメソッドを作成したくないことにも注意してください。

これを踏まえて、どのようにしてその1つのフィールドの値を模擬できますか?

回答:


144

ReflectionTestUtils.setFieldコードに変更を加えないようにするために、Springの魔法を使うことができます。

さらに詳しい情報については、このチュートリアルを確認してください。ただし、メソッドは非常に使いやすいので、おそらく必要ないでしょう。

更新

Spring 4.2.RC1の導入以来、クラスのインスタンスを提供することなく静的フィールドを設定できるようになりました。ドキュメントのこの部分とこのコミットを参照してください。


12
リンクが停止した場合に備えて:テスト中ReflectionTestUtils.setField(bean, "fieldName", "value");にメソッドを呼び出す前に使用しますbean
のMichałStochmal

2
プロパティファイルから取得するプロパティをモックするための適切なソリューション。
Antony Sampath Kumar Reddy

@MichałStochmal、ファイルされたプライベートなので例外が生成されますjava.lang.IllegalStateException:メソッドにアクセスできませんでした:クラスorg.springframework.util.ReflectionUtilsは修飾子付きのクラスcom.kaleidofin.app.service.impl.CVLKRAProviderのメンバーにアクセスできません"" org.springframework.util.ReflectionUtils.handleReflectionException(ReflectionUtils.java:112)at org.springframework.util.ReflectionUtils.setField(ReflectionUtils.java:655)
Akhil Surapuram

112

@Valueフィールドをモックする方法をいつも忘れているので、このSO投稿にググったのは3回目です。受け入れられた答えは正しいですが、「setField」の呼び出しを正しく行うには常に時間が必要なので、少なくとも私自身はここにサンプルのスニペットを貼り付けます。

生産クラス:

@Value("#{myProps[‘some.default.url']}")
private String defaultUrl;

テストクラス:

import org.springframework.test.util.ReflectionTestUtils;

ReflectionTestUtils.setField(instanceUnderTest, "defaultUrl", "http://foo");
// Note: Don't use MyClassUnderTest.class, use the instance you are testing itself
// Note: Don't use the referenced string "#{myProps[‘some.default.url']}", 
//       but simply the FIELDs name ("defaultUrl")

32

プロパティ設定をテストクラスにモックすることもできます

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest 
{ 
   @Configuration
   public static class MockConfig{
       @Bean
       public Properties myProps(){
             Properties properties = new Properties();
             properties.setProperty("default.url", "myUrl");
             properties.setProperty("property.value2", "value2");
             return properties;
        }
   }
   @Value("#{myProps['default.url']}")
   private String defaultUrl;

   @Test
   public void testValue(){
       Assert.assertEquals("myUrl", defaultUrl);
   }
}

24

関連する解決策を提案したいと思います。これは、@Value-annotatedフィールドをパラメーターとしてコンストラクターに渡すのではなく、ReflectionTestUtilsクラス。

これの代わりに:

public class Foo {

    @Value("${foo}")
    private String foo;
}

そして

public class FooTest {

    @InjectMocks
    private Foo foo;

    @Before
    public void setUp() {
        ReflectionTestUtils.setField(Foo.class, "foo", "foo");
    }

    @Test
    public void testFoo() {
        // stuff
    }
}

これを行う:

public class Foo {

    private String foo;

    public Foo(@Value("${foo}") String foo) {
        this.foo = foo;
    }
}

そして

public class FooTest {

    private Foo foo;

    @Before
    public void setUp() {
        foo = new Foo("foo");
    }

    @Test
    public void testFoo() {
        // stuff
    }
}

このアプローチの利点:1)依存関係のないコンテナー(単なるコンストラクター)なしでFooクラスをインスタンス化できます。2)テストを実装の詳細に結合していません(リフレクションは文字列を使用してフィールド名に関連付けます)フィールド名を変更すると問題が発生する可能性があります)。


3
欠点:誰かがアノテーションに手を加えた場合、たとえば「foo」ではなく「bar」プロパティを使用しても、テストは引き続き機能します。私はこのケースを持っています。
Nils El-Himoud

@ NilsEl-Himoudそれは一般的にOPの質問にとっては公平な点ですが、あなたが提起する問題は、リフレクションユーティリティとコンストラクターを使用することによって得られる問題は良くも悪くもありません。この回答のポイントは、reflection util(受け入れられた回答)よりもコンストラクタを考慮することでした。マーク、答えてくれてありがとう。この微調整が簡単で清潔であることに感謝します。
マーキー

22

次の魔法のSpring Testアノテーションを使用できます。

@TestPropertySource(properties = { "my.spring.property=20" }) 

org.springframework.test.context.TestPropertySourceを参照してください

たとえば、これはテストクラスです。

@ContextConfiguration(classes = { MyTestClass.Config.class })
@TestPropertySource(properties = { "my.spring.property=20" })
public class MyTestClass {

  public static class Config {
    @Bean
    MyClass getMyClass() {
      return new MyClass ();
    }
  }

  @Resource
  private MyClass myClass ;

  @Test
  public void myTest() {
   ...

そして、これはプロパティを持つクラスです:

@Component
public class MyClass {

  @Value("${my.spring.property}")
  private int mySpringProperty;
   ...

13

私は以下のコードを使用し、それは私のために働きました:

@InjectMocks
private ClassABC classABC;

@Before
public void setUp() {
    ReflectionTestUtils.setField(classABC, "constantFromConfigFile", 3);
}

リファレンス:https : //www.jeejava.com/mock-an-autowired-value-field-in-spring-with-junit-mockito/


1
同じ... + 1
穏やかな

私は同じことをやったが、それでも反映されていない
Shubhro Mukherjee

1

また、クラスには明示的な「setter」メソッド(setDefaultUrlなど)がなく、テスト目的でのみ作成したくないことにも注意してください。

これを解決する1つの方法は、コンストラクターインジェクションを使用するようにクラスを変更することですは、テストとSpringインジェクションに使用されることです。これ以上の反射:)

したがって、コンストラクタを使用して任意の文字列を渡すことができます。

class MySpringClass {

    private final String defaultUrl;
    private final String defaultrPassword;

    public MySpringClass (
         @Value("#{myProps['default.url']}") String defaultUrl, 
         @Value("#{myProps['default.password']}") String defaultrPassword) {
        this.defaultUrl = defaultUrl;
        this.defaultrPassword= defaultrPassword;
    }

}

そしてあなたのテストでは、それを使ってください:

MySpringClass MySpringClass  = new MySpringClass("anyUrl", "anyPassword");
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.