JUnitテストクラス全体でSpringアプリケーションコンテキストを再利用する


83

多数のJUnitテストケース(統合テスト)があり、それらは論理的に異なるテストクラスにグループ化されています。

http://static.springsource.org/spring/docs/current/spring-framework-referenceに記載されているように、Springアプリケーションコンテキストをテストクラスごとに1回ロードし、JUnitテストクラスのすべてのテストケースで再利用できます。/html/testing.html

ただし、一連のJUnitテストクラスに対してSpringアプリケーションコンテキストを1回だけロードする方法があるかどうか疑問に思っていました。

FWIWでは、Spring 3.0.5、JUnit 4.5を使用し、Mavenを使用してプロジェクトをビルドします。


5
以下のすべての答えは素晴らしいですが、私はcontext.xmlを持っていません。忘却への道に注釈を付けましたか?context.xmlなしでこれを行う方法はありますか?
markthegrea 2018

2
あなたはあなたの解決策への答えを見つけましたか?同じ問題があり、アノテーションとSpringBootを使用してこれを実行したいと思います。
AleksandarT

回答:


96

はい、これは完全に可能です。あなたがしなければならないのはlocationsあなたのテストクラスで同じ属性を使うことです:

@ContextConfiguration(locations = "classpath:test-context.xml")

Springはアプリケーションコンテキストをlocations属性ごとにキャッシュlocationsするため、同じコンテキストが2回目に表示された場合、Springは新しいコンテキストを作成するのではなく、同じコンテキストを使用します。

この機能に関する記事を書きました:Spring統合テストの高速化。また、Springのドキュメントで詳細に説明されています:9.3.2.1コンテキスト管理とキャッシュ

これには興味深い意味があります。SpringはJUnitがいつ実行されるかを知らないため、すべてのコンテキストを永久にキャッシュし、JVMシャットダウンフックを使用してそれらを閉じます。この動作(特に、異なるテストクラスが多数ある場合locations)は、過度のメモリ使用量、メモリリークなどにつながる可能性があります。コンテキストをキャッシュすることのもう1つの利点。


ああ!気づかなかった。私たちは長い間このアプローチを採用してきましたが、私は(誤って)テスト実行の期間が長いのは、すべてのテストクラスでコンテキストの読み込みが発生したためだと考えています。今すぐ注意深くチェックします。ありがとう。
ラメッシュ2011

1
むしろ、春にはテストケースの実行順序についての知識がありません。この結果、コンテキストが後で必要になるのか、破棄できるのかがわかりません。
philnate 2015

1
これが実際にどのように真実であるかはわかりません。Eclipse / JUnitは、Run As / JUnitテストを実行するたびに、環境のクランクアップに2分を費やします。何かがキャッシュされていれば、これは起こりません。
user1944491 2015年

3
コンテキスト定義にXMLを使用する代わりに、注釈を介してこれを完全に実行できるかどうかについて何か考えはありますか?私はそれについてドキュメントとここSOでたくさん検索しましたが、それが不可能だと思うようなものは何も見つかりませんでした。
ジャン=フランソワ・Savard

イニシャライザーがある場合、これは当てはまりませんか?私はクラスのすべてのテストのために初期化しています
Kalpesh Soni 2016

26

Tomasz Nurkiewiczの回答に追加するために、Spring 3.2.2の時点で、@ContextHierarchy注釈を使用して、個別の関連付けられた複数のコンテキスト構造を持つことができます。これは、複数のテストクラスが(たとえば)インメモリデータベースの設定(データソース、EntityManagerFactory、tx managerなど)を共有する場合に役立ちます。

例えば:

@ContextHierarchy({
  @ContextConfiguration("/test-db-setup-context.xml"),
  @ContextConfiguration("FirstTest-context.xml")
})
@RunWith(SpringJUnit4ClassRunner.class)
public class FirstTest {
 ...
}

@ContextHierarchy({
  @ContextConfiguration("/test-db-setup-context.xml"),
  @ContextConfiguration("SecondTest-context.xml")
})
@RunWith(SpringJUnit4ClassRunner.class)
public class SecondTest {
 ...
}

この設定により、「test-db-setup-context.xml」を使用するコンテキストは1回だけ作成されますが、その中のBeanは個々の単体テストのコンテキストに注入できます。

マニュアルの詳細:http//docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management(「コンテキスト階層」を検索)


私はマルチモジュールMavenを使用していますが、サービスモジュールでのデータベースのセットアップを回避しようとしています(データアクセスモジュールのテストが既にロードされているため)が、機能しません!
ムハンマドヒューディ2014年

5
これは私のために働いた!ありがとう。明確にするために、@ ContextHierarchyアノテーションなしで、Springは各テストのデータベースをロードします。「クラス」パラメータを使用しています:@ContextConfiguration(classes = {JpaConfigTest.class、...
Brel

5
コンテキスト定義にXMLを使用する代わりに、注釈を介してこれを完全に実行できるかどうかについて何か考えはありますか?私はそれについてドキュメントとここSOでたくさん検索しましたが、それが不可能だと思うようなものは何も見つかりませんでした。
ジャン=フランソワ・Savard

1
@Jean-FrançoisSavard(XMLの代わりにannotationswを介して)検索に運がありましたか?
javadev 2017年

@javadevこれがあなたが探しているものであることを願っています
docs.spring.io/spring/docs/current/spring-framework-reference/…– Raviteja Gubba

1

基本的に、異なるテストクラス間で同じアプリケーションコンテキスト構成を使用している場合、Springはこれを構成するのに十分賢いです。たとえば、次のように2つのクラスAとBがあるとします。

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class A {

    @MockBean
    private C c;
    //Autowired fields, test cases etc...
}

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class B {

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

この例では、クラスAはBean Cをモックしますが、クラスBはBean Dをモックします。したがって、springはこれらを2つの異なる構成と見なし、アプリケーションコンテキストをクラスAに対して1回、クラスBに対して1回ロードします。

代わりに、これら2つのクラス間でアプリケーションコンテキストをSpringに共有させたい場合は、次のように表示する必要があります。

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class A {

    @MockBean
    private C c;

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class B {

    @MockBean
    private C c;

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

このようにクラスを接続すると、Springは、テストスイートで最初に実行されるクラスに応じて、クラスAまたはBのいずれかに対してアプリケーションコンテキストを1回だけロードします。これは、複数のテストクラス間で複製できます。唯一の基準は、テストクラスを異なる方法でカスタマイズしないことです。テストクラスが他のクラスとは異なる結果になるカスタマイズ(春の目で)は、春までに別のアプリケーションコンテキストを作成することになります。


0

以下のように構成クラスを作成します

@ActiveProfiles("local")
@RunWith(SpringJUnit4ClassRunner.class )
@SpringBootTest(classes ={add your spring beans configuration classess})
@TestPropertySource(properties = {"spring.config.location=classpath:application"})
@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
public class RunConfigration {

    private ClassLoader classloader = Thread.currentThread().getContextClassLoader();

    private static final Logger LOG = LoggerFactory.getLogger(S2BXISINServiceTest.class);


    //auto wire all the beans you wanted to use in your test classes
    @Autowired
    public XYZ xyz;
    @Autowired
    public ABC abc;


    }



Create your test suite like below



@RunWith(Suite.class)
@Suite.SuiteClasses({Test1.class,test2.class})
public class TestSuite extends RunConfigration {

    private ClassLoader classloader = Thread.currentThread().getContextClassLoader();

    private static final Logger LOG = LoggerFactory.getLogger(TestSuite.class);


}

以下のようなテストクラスを作成します

public class Test1 extends RunConfigration {


  @Test
    public void test1()
    {
    you can use autowired beans of RunConfigration classes here 
    }

}


public class Test2a extends RunConfigration {

     @Test
    public void test2()
    {
    you can use autowired beans of RunConfigration classes here 
    }


}

0

注目すべき点の1つは、@ SpringBootTestsを使用する場合でもuse @MockBean in different test classes、Springにはすべてのテストでアプリケーションコンテキストを再利用する方法がないことです。

解決策はto move all @MockBean into an common abstract class、問題を解決することです。

@SpringBootTests(webEnvironment = WebEnvironment.RANDOM_PORT, classes = Application.class)
public abstract class AbstractIT {

   @MockBean
   private ProductService productService;

   @MockBean
   private InvoiceService invoiceService;

}

次に、テストクラスは次のように表示されます。

public class ProductControllerIT extends AbstractIT {
   // please don't use @MockBean here
   @Test
   public void searchProduct_ShouldSuccess() {
   }

}

public class InvoiceControllerIT extends AbstractIT {
   // please don't use @MockBean here
   @Test
   public void searchInvoice_ShouldSuccess() {
   }

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