53

アプリケーションの目的でSpringを使用し、単体テストにはSpring Testingフレームワークを使用しています。ただし、小さな問題があります。アプリケーション コードは、クラスパス内の場所 (XML ファイル) のリストから Spring アプリケーション コンテキストをロードします。しかし、単体テストを実行するときは、Spring Bean の一部を本格的な実装クラスではなくモックにする必要があります。さらに、一部の単体テストでは一部の Bean をモックにする必要があり、他の単体テストでは他の Bean をモックにする必要があります。これは、アプリケーションのさまざまなレイヤーをテストしているためです。

これはすべて、アプリケーション コンテキストの特定の Bean を再定義し、必要に応じてコンテキストを更新することを意味します。これを行っている間、1 つ (または複数) の元の XML Bean 定義ファイルにある Bean のごく一部のみを再定義したいと考えています。私はそれを行う簡単な方法を見つけることができません。Spring は単体テストに適したフレームワークであると常に見なされているため、ここで何かが欠けているに違いありません。

それを行う方法はありますか?

ありがとう!

4

13 に答える 13

18

TestClassspring bean.xml の場所について、カスタムでいくつかの簡単なルールを提案します。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
    "classpath*:spring/*.xml",
    "classpath*:spring/persistence/*.xml",
    "classpath*:spring/mock/*.xml"})
@Transactional
@TestExecutionListeners({
    DependencyInjectionTestExecutionListener.class,
    TransactionalTestExecutionListener.class,
    DirtiesContextTestExecutionListener.class})
public abstract class AbstractHibernateTests implements ApplicationContextAware {

    /**
     * Logger for Subclasses.
     */
    protected final Logger log = LoggerFactory.getLogger(getClass());

    /**
     * The {@link ApplicationContext} that was injected into this test instance
     * via {@link #setApplicationContext(ApplicationContext)}.
     */
    protected ApplicationContext applicationContext;

    /**
     * Set the {@link ApplicationContext} to be used by this test instance,
     * provided via {@link ApplicationContextAware} semantics.
     */
    @Override
    public final void setApplicationContext(
            final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
}

指定された場所にある場合mock-bean.xml、それらは「通常の」場所にあるすべての「実際の」bean.xmlファイルを上書きします - 通常の場所は異なる場合があります。

しかし…アプリケーションが古くなると問題を追跡するのが難しくなるため、モック Bean と非モック Bean を混在させることは決してありません。

于 2009-02-19T15:31:22.853 に答える
16

Spring がテストに適していると言われる理由の 1 つは、単体テストで新しいものやモックを作成するのが簡単だからです。

別の方法として、次のセットアップを使用して大成功を収めました。これはあなたが望むものに非常に近いと思います。強くお勧めします。

異なるコンテキストで異なる実装を必要とするすべての Bean について、アノテーション ベースの配線に切り替えます。他はそのままで構いません。

次の一連の注釈を実装します

 <context:component-scan base-package="com.foobar">
     <context:include-filter type="annotation" expression="com.foobar.annotations.StubRepository"/>
     <context:include-filter type="annotation" expression="com.foobar.annotations.TestScopedComponent"/>
     <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
 </context:component-scan>

次に、ライブ実装に @Repository でアノテーションを付け、スタブ実装に @StubRepository でアノテーションを付けます。さらにいくつかの注釈が必要になるかもしれませんが、これらは素晴らしい出発点です。

spring.xml が多数ある場合は、基本的にコンポーネント スキャン定義のみを含むいくつかの新しい spring xml ファイルを作成する必要があります。通常は、これらのファイルを通常の @ContextConfiguration リストに追加するだけです。この理由は、コンテキスト スキャンの構成が異なることが多いためです (信頼してください。Web テストを行っている場合、少なくとも 1 つの注釈を追加することになります。これにより、4 つの関連する組み合わせが作成されます)。

次に、基本的に使用します

@ContextConfiguration(locations = { "classpath:/path/to/root-config.xml" })
@RunWith(SpringJUnit4ClassRunner.class)

この設定では、スタブ データとライブ データを交互に組み合わせることはできないことに注意してください。私たちはこれを試しましたが、結果として混乱が生じたと思います。誰にもお勧めしません ;) スタブの完全なセットまたはライブ サービスの完全なセットのいずれかを配線します。

依存関係が通常非常に大きいものの近くで gui をテストするときは、主に自動配線されたスタブの依存関係を使用します。コードのよりクリーンな領域では、より定期的な単体テストを使用します。

私たちのシステムには、コンポーネント スキャン用の次の xml ファイルがあります。

  • 通常のWeb制作に
  • スタブのみで Web を開始する場合
  • 統合テスト用 (junit)
  • 単体テスト用 (junit)
  • Selenium Web テスト用 (junit)

これは、アプリケーションを開始できるシステム全体の構成が全部で 5 つあることを意味します。アノテーションのみを使用するため、Spring は、配線したい単体テストでさえ自動配線するのに十分高速です。私はこれが非伝統的であることを知っていますが、それは本当に素晴らしいです.

統合テストは完全なライブ セットアップで実行されますが、1 回か 2 回は本当に実用的になることに決め、5 つのライブ配線と 1 つのモックを作成したいと考えています。

public class HybridTest {
   @Autowired
   MyTestSubject myTestSubject;


   @Test
   public void testWith5LiveServicesAndOneMock(){
     MyServiceLive service = myTestSubject.getMyService();
     try {
          MyService mock = EasyMock.create(...)
          myTestSubject.setMyService( mock);

           .. do funky test  with lots of live but one mock object

     } finally {
          myTestSubject.setMyService( service);
     }


   }
}

私は、テストの純粋主義者がこれのために私のいたるところにいることを知っています. しかし、代替案が本当に醜い場合に、非常に実用的なソリューションであることが非常にエレガントであることが時々あります。繰り返しになりますが、通常はギニアに近い場所にあります。

于 2009-02-19T23:08:14.110 に答える
7

@InjectedMock アノテーションを使用してこのチュートリアルを参照してください

それは私に多くの時間を節約しました。あなたはただ使う

@Mock
SomeClass mockedSomeClass

@InjectMock
ClassUsingSomeClass service

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

そしてあなたの問題はすべて解決されます。Mockito は、Spring 依存性注入をモックに置き換えます。私はそれを自分で使用しただけで、うまく機能します。

于 2013-10-31T22:13:35.443 に答える
6

ここにリストされているいくつかの非常に複雑で強力なソリューションがあります。

しかし、 Stas が要求したことを達成するための、はるかに簡単な方法があります。これは、テスト メソッドのコードを 1 行変更するだけで済みます。単体テストと Spring 統合テスト、自動配線された依存関係、プライベートおよび保護されたフィールドに対して同様に機能します。

ここにあります:

junitx.util.PrivateAccessor.setField(testSubject, "fieldName", mockObject);
于 2009-06-16T07:39:51.520 に答える
4

ルックアップをまったく必要としない単体テストを作成することもできます。

@ContextConfiguration(locations = { "classpath:/path/to/test-config.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class MyBeanTest {

    @Autowired
    private MyBean myBean; // the component under test

    @Test
    public void testMyBean() {
        ...
    }
}

これにより、実際の構成ファイルをテスト構成ファイルと簡単に組み合わせることができます。

たとえば、Hibernateを使用する場合、sessionFactory Beanを1つの構成ファイル(テストとメインアプリの両方で使用)に入れ、dataSource Beanを別の構成ファイルに含めることができます(DriverManagerDataSourceを使用してin-メモリデータベース、もう一方はJNDIルックアップを使用する場合があります)。

しかし、間違いなく@cletusの警告に注意してください;-)

于 2009-02-19T14:43:53.947 に答える
4

簡単。単体テストにカスタム アプリケーション コンテキストを使用するか、まったく使用せずに手動で Bean を作成して注入します。

あなたのテストは少し広すぎるように思えます。単体テストは、ユニットのテストに関するものです。Spring Bean は、ユニットのかなり良い例です。そのためのアプリケーション コンテキスト全体は必要ありません。単体テストが非常に高レベルで、何百もの Bean やデータベース接続などが必要な場合、非常に脆弱な単体テストがあり、次の変更で壊れる可能性があり、維持するのが難しく、実際にはそうではありません。多くの価値を追加していません。

于 2009-02-19T13:44:09.113 に答える
2

テストアプリのコンテキストでインポート機能を使用して、prod Bean をロードし、必要なものをオーバーライドできます。たとえば、prod データ ソースは通常 JNDI ルックアップ経由で取得されますが、テストするときは DriverManager データ ソースを使用するので、テストのためにアプリ サーバーを起動する必要はありません。

于 2009-02-19T14:02:34.967 に答える
1

ダッフィーモの答えを積み重ねるほどの評判ポイントはありませんが、私は彼が私にとって「正しい」答えだったと言いたかっただけです.

カスタム applicationContext.xml を使用して、単体テストのセットアップで FileSystemXmlApplicationContext をインスタンス化します。そのカスタム xml の上部で、duffymo が示すように実行します。次に、インポートで宣言された ID をオーバーライドするモック Bean、非 JNDI データ ソースなどを宣言します。

私にとって夢のように働きました。

于 2009-07-24T20:23:30.000 に答える
1

テスト コンテキストを使用する必要はありません (XML ベースか Java ベースかは関係ありません)。@MockBeanSpring ブート 1.4 以降、Spring Bean のモックとスパイのネイティブ サポートを導入した新しいアノテーションが利用可能になりました。

于 2016-09-05T13:32:17.710 に答える
0

私も同じことをしたいと思っています。

現在使用しているメカニズムはかなり手動ですが、機能します。

たとえば、タイプ Y の Bean をモックアウトしたいとします。私たちが行うことは、その依存関係を持つすべての Bean にインターフェイスを実装させることです - 「IHasY」。このインターフェースは

interface IHasY {
   public void setY(Y y);
}

次に、テストで util メソッドを呼び出します...

 public static void insertMock(Y y) {
        Map invokers = BeanFactory.getInstance().getFactory("core").getBeansOfType(IHasY.class);
        for (Iterator iterator = invokers.values().iterator(); iterator.hasNext();) {
            IHasY invoker = (IHasY) iterator.next();
            invoker.setY(y);
        }
    }

この新しい依存関係を注入するためだけに xml ファイル全体を作成したくないので、これが気に入っています。

xml 構成ファイルを作成する場合は、モック Bean を使用して新しいファクトリを作成し、デフォルトのファクトリをこのファクトリの親にします。次に、新しい子ファクトリからすべての Bean をロードするようにしてください。これを行うと、Bean ID が同じ場合、サブファクトリーは親ファクトリーの Bean をオーバーライドします。

私のテストでは、もしプログラムで工場を作成できたら、それは素晴らしいことです。xml を使用しなければならないのは、あまりにも面倒です。その子ファクトリーをコードで作成しようとしています。次に、各テストは、ファクトリを希望どおりに構成できます。そのような工場が機能しない理由はありません。

于 2009-06-10T09:33:10.430 に答える
0

spring-reinjectは、Bean をモックに置き換えるように設計されています。

于 2014-02-02T17:39:00.000 に答える
0

おそらく、Bean に修飾子を使用できますか? 別のアプリケーション コンテキストでモックアップする Bean を再定義し、修飾子「test」でラベル付けします。単体テストでは、Bean を配線するときに、常に修飾子「test」を指定してモックアップを使用します。

于 2009-02-19T13:47:55.580 に答える