Open4

open ai apiのテストコード

yoshiyoshi
let(:mock_openai_client) { double('OpenAI::Client') }
before do
  allow(OpenAI::Client).to receive(:new).and_return(mock_openai_client)
end

OpenAI::Client.new が呼び出されたときに、実際のオブジェクトの代わりにこのモックオブジェクトを返すように指定。
{double('OpenAI::Client') } とすることで、OpenAI::Client のモックオブジェクトが作成される。
このモックオブジェクトは、実際の OpenAI::Client オブジェクトと同じインターフェース(つまり同じメソッドやプロパティ)を持ちますが、実際のAPIとの通信は行わない。

https://linyclar.github.io/rails_memos/development_tools/rspec_mocks_memo/

yoshiyoshi

before do ブロック内で allow を使用する理由は、テストの実行前に特定の設定やモックを事前に準備しておくことが重要だからです。この場合、OpenAI::Client.new と mock_openai_client の chat メソッドが呼ばれるときの振る舞いを事前に定義しています。

テスト中にユーザーがフォームに入力して変換ボタンをクリックすると、コントローラーが OpenAI::Client.new を呼び出します。before do ブロックで allow(OpenAI::Client).to receive(:new).and_return(mock_openai_client) と設定することで、実際にこのメソッドが呼び出されたときにモックオブジェクトが返されるようになります。つまり、コントローラーがこのメソッドを呼び出すタイミングで、すでにモックが用意されており、実際のAPI呼び出しを行わないようになっています。

このように事前にモックを設定しておくことで、テストの実行時にコードが予定通りの振る舞いをすることを保証し、外部依存性(この場合はOpenAIのAPI)を排除することができます。これにより、テストはより高速で信頼性が高く、独立して実行できるようになります。

yoshiyoshi
    describe '自己受容トレーニング結果' do
      let(:user) { create(:user) }
      let(:mock_openai_client) { double('OpenAI::Client') }
      let(:mock_response) {
        {
          'choices' => [
            { 'message' => { 'content' => '変換されたポジティブなメッセージ' }}
          ]
        }
      }
      before do
        login(user)
        visit new_self_esteem_training_path
        allow(OpenAI::Client).to receive(:new).and_return(mock_openai_client)
        allow(mock_openai_client).to receive(:chat).and_return(mock_response)
      end
      it '自己否定的なテキストを自己受容のメッセージに変換する' do
        fill_in 'text', with: '自己否定的な思考'
        click_button '変換'
        sleep 15
        expect(page).to have_text('変換されたポジティブなメッセージ')
        expect(page).to have_current_path(result_self_esteem_trainings_path, ignore_query: true)
      end
    end

allow(mock_openai_client)の説明

  1. モックの設定:

    • テストの前処理(before do ブロック内)で、OpenAI::Client クラスが新しいインスタンスを生成するたびに mock_openai_client を返すように設定されています(allow(OpenAI::Client).to receive(:new).and_return(mock_openai_client))。
  2. コントローラーでの処理:

    • コントローラー内で @client = OpenAI::Client.new(access_token: @api_key) が呼び出されると、テスト設定に基づき、実際には mock_openai_client@client として設定されます。
  3. モックの反応:

    • その後、コントローラーで @client.chat が呼び出されると、これは実際には mock_openai_client.chat の呼び出しとなります。これは、@clientmock_openai_client の参照を持っているためです。
yoshiyoshi

例えば以下のようなパスが返されるとき、クエリパラメータ (text=自己否定的な思考) の部分を省いてURLのパス部分のチェックしたいい場合。
ページのURLパス:(/self_esteem_trainings/result?text=自己否定的な思考)
ignore_query: trueを付けることでクエリパラメータは無視され、URLのパス部分のみがチェックされる。

expect(page).to have_current_path(result_self_esteem_trainings_path, ignore_query: true)