4

ここには、トランザクションと JUnit に関するかなりの数の質問があります。しかし、同じ問題を抱えている人が見つからないため、破棄する前にこれを読んでください。

注釈付きのビジネスメソッドがあり@Transactionalます。このメソッド内で、特別な状況が発生した場合、プログラムでロールバックを実行します。TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

ここで、プログラムによるロールバックが良いか悪いかについての議論には立ち入りません。そこにいることを受け入れ、そこにとどまって一緒に働くことを受け入れましょう。

アプリケーションを起動して、このビジネス メソッドを昔ながらの方法でテストすると、すべてが完全に機能します。ロールバックする必要がある場合はロールバックされ、すべてが正常な場合はすべて正常です。@Transactionalまた、ロールバックが必要な場合でも何もロールバックされないことを確認するために、 を使用せずにテストを行いました。すべてが計画どおりに機能します。

しかし、私が抱えている問題はJUnitにあります。現在、このメソッドの 2 つの JUnit テストがあります。1 つは失敗する (そしてプログラムによるロールバックをトリガーする) もので、もう 1 つはロールバックなしで成功するものです。

私は Junit クラスのさまざまなセットアップを試しました。現在、次のようになっています。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:springTestContext.xml", "classpath:springTestContext-dao.xml"})
@TransactionConfiguration(transactionManager = "txManager")
public class MyManagerTest extends AbstractTransactionalJUnit4SpringContextTests {
    @Mock
    private ProductDao productDao;

    @InjectMocks
    MyManager myManager = new MyManagerImpl();

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

    @Test
    public void testUnParsableXml() {
        String xml = "adlsfas";
        Response response = myManager.processXMLContent(xml);
        assertFalse(response.isSuccess());
        System.out.println(response.getResponse());
    }

}
@Service("myManager")
public class MyManagerImpl extends BaseManager implements MyManager {
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public Response processXMLContent(String xml) {
       /* NB. Extremly simplified version.... */
       Response response = new Response();
       try {
            parseXml(); // just dummy sample. Its actually parsing xml
            response.setSuccess(true)
       catch(SAXException e) {
           TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
           response.setSuccess(false);
       }
       return response;
    }
}

springTestContext には<tx:annotation-drivenアノテーションがあり、dao-context には transactionmanager、entityfactory、および datasource があります。おそらくそれらも必要ないでしょうか?このテストは、db で行うことはまったくありません。私がテストしたいのは、失敗した場合にトランザクションでプログラムによるロールバックが行われることだけです。

しかし、私がそれらを追加した理由は、私がここで助けを得ようとしているというエラーのためでした. ビジネスメソッドでプログラムによるロールバックが呼び出されるたびに、常にこのエラーが発生します(junitテストの場合のみ、それ以外の場合は完全に機能します):

org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope

だからあなたへの私の質問は次のとおりです。私は何を間違っていますか。ビジネスメソッドでトランザクションを実行するにはどうすればよいですか? おまけの質問として、トランザクションでロールバックが呼び出されたことをどのようにテストできますか?

お時間をいただきありがとうございます。

4

3 に答える 3

2

MyManager myManager = new MyManagerImpl();コンテキストから実際の Bean を使用する代わりに、 MyManager クラスの新しいインスタンスを作成する

@Resource(name="myContext")
MyManager myManager;

明らかに、プロキシは作成されておらず、参照している MyManager インスタンスは Spring Bean でさえありません。これは、DI コンテナー内で作成されていないためです。

テストクラスの@Transactionalアノテーション付きメソッドについては、TransactionalTestExecutionListenerで実行されると思います(そのため、メカニズムはコンテナー内とは少し異なります) が、コンテナー自体のトランザクションラッパーには影響しないはずです - beforeTestMethod と afterTestMethod を参照必要に応じて、PlatformTransactionManager で何が行われているかを確認してください。

ドキュメントから:

TestContext フレームワークでは、テスト クラスで @TestExecutionListeners を明示的に宣言しなくても、トランザクションはデフォルトで @TestExecutionListeners アノテーションを介して構成される TransactionalTestExecutionListener によって管理されます。ただし、トランザクションのサポートを有効にするには、 @ContextConfiguration セマンティクスによってロードされるアプリケーション コンテキストで PlatformTransactionManager Bean を提供する必要があります。さらに、クラス レベルまたはメソッド レベルで @Transactional を宣言する必要があります。

@Transactionalしたがって、テストクラスと実際の Bean の注釈付きメソッドの実行にはそれほど違いはないと思います(もちろん、テストクラスのデフォルトのロールバックフラグを除いて) - TransactionAspectSupport と TransactionalTestExecutionListener の両方が、基礎となるメソッドを呼び出すだけですPlatformTransactionManager .

PS統合テストに関しては、Bean(ProductDaoとMyManagerの両方)をモックしようとしていることは明確ではありません-Springとの統合テストを行うとき、実際のBeanがどのように相互作用するかをテストし、コンテナの外部の依存関係のみをモックします(たとえば、実際の Web サーバーへのバインドの代わりに MockServletContext を使用する)、または重量/実稼働レベルのサーバーへの依存関係を軽量/組み込みサーバーに置き換える - これは、現実的な条件、利便性、およびテスト実行速度のバランスです。

于 2012-11-30T06:17:30.557 に答える
2

AbstractTransactionalJUnit4SpringContextTestsによって設定されたSpringJUnit4ClassRunnerでテストを実行する必要がありますが、 MockitoJunitRunnerでオーバーライドしました。テストの上部にあるRunWithを削除するだけで機能します。

于 2012-11-27T19:20:49.337 に答える