問題を別の視点から見る必要があります。関数がトランザクションを使用しているかどうかをテストすることは、動作の観点からは役に立ちません。関数が期待どおりに動作するかどうかについての情報は提供しません。
テストする必要があるのは動作です。つまり、期待される結果は正しいです。わかりやすくするために、関数内で操作 A と操作 B を実行するとします (1 つのトランザクション内で実行されます)。オペレーション A は、アプリでユーザーに 100 米ドルを入金します。操作 B は、ユーザーのクレジット カードから 100 USD を引き落とします。
ユーザーのクレジット カードの引き落としが失敗するように、テストに無効な入力情報を提供する必要があります。関数呼び出し全体をexpect { ... }.not_to change(User, :balance)
.
このようにして、予想される動作をテストします。クレジット カードによる引き落としが失敗した場合、ユーザーに金額を入金しないでください。また、コードをリファクタリングするだけであれば (たとえば、トランザクションの使用を停止し、手動でロールバックするなど)、テスト ケースの結果は影響を受けません。
そうは言っても、@luacassusが述べたように、両方の操作を分離してテストする必要があります。また、@ rb512 が述べたように、ソースコードに「互換性のない」変更を加えた (つまり、動作を変更した) 場合に、テスト ケースが失敗するのはまったく正しいことです。