Codelab: テスト駆動開発を加速する Gemini

1. はじめに

この Codelab では、コードを実装する前にテストを作成するテスト駆動開発(TDD)の原則について説明します。このアプローチを示す簡単な電卓アプリを Ruby で作成します。

また、コーディング アシスタントとして Gemini を使用する方法についても説明します。Gemini は、テストケースの生成、コード実装の提案、さらにはコードの説明を提供して TDD サイクルを加速します。

最後に、計算機アプリを Google のフルマネージド サーバーレス プラットフォームである Cloud Functions にデプロイして、アプリにアクセスできるようにする方法について説明します。

2. 前提条件

  • 課金が有効な Google Cloud プロジェクト
  • ウェブブラウザ

プロジェクトを作成する

  • Google Cloud コンソールのプロジェクト選択ページで、Google Cloud プロジェクトを選択または作成します。
  • Cloud プロジェクトで課金が有効になっていることを確認します。詳しくは、プロジェクトで課金が有効になっているかどうかを確認する方法をご覧ください。

Gemini API を有効にする

  • Gemini Marketplace にアクセスして API を有効にします。gcloud を使用して API を有効にすることもできます。
gcloud services enable cloudaicompanion.googleapis.com --project PROJECT_ID

Cloud Functions API を有効にする

gcloud services enable cloudfunctions.googleapis.com --project PROJECT_ID

Cloud Shell をアクティブにする

  • Google Cloud コンソールで実行されるコマンドライン環境である Cloud Shell を使用します。この環境には、必要な言語がプリロードされています。Cloud コンソールで、右上の [Cloud Shell をアクティブにする] をクリックします。

6757b2fb50ddcc2d.png

  • Cloud Shell に接続すると、すでに認証は完了しており、プロジェクトに各自のプロジェクト ID が設定されていることがわかります。Cloud Shell で次のコマンドを実行して、認証されたことを確認します。
gcloud auth list
  • Cloud Shell で次のコマンドを実行して、gcloud コマンドがプロジェクトを認識していることを確認します。
gcloud config list project
  • プロジェクトが設定されていない場合は、次のコマンドを使用して設定します。
gcloud config set project <YOUR_PROJECT_ID>

gcloud コマンドとその使用方法については、ドキュメントをご覧ください。

エディタを開く

  • この Codelab では、組み込みの Cloud エディタを使用します。Cloud Shell ウィンドウで、右上の [Open editor] ボタンをクリックします。これにより、VSCode エディタが開きます。

e5a5e95d543db556.png

Ruby 拡張機能を追加する

  • 左側のサイドメニューで拡張機能アイコン 999939b067e1d0dc.png をクリックし、検索バーに vscode ruby と入力して、VSCode Ruby 拡張機能をインストールします。

a6fd4e37b3d847d2.png

3. Gemini の準備

LLM は非決定的です。そのため、これらのプロンプトを試しているときに受け取るレスポンスが、Codelab に添付されているスクリーンショット内のレスポンスとは異なる場合があります。

まず、Gemini に TDD とは何かを相談してみましょう。現在使用している Cloud Shell エディタで Gemini との会話を開始するには、次の手順を行います。

  • ページの左側にある Gemini アイコン 25fdcae4447d063c.png を見つけます。これをクリックすると、左側にチャット ウィンドウが開きます。
  • [Enter a prompt here] と表示されているテキスト ボックスに入力を開始できます。

5e09b0fb6a1b44dd.png

プロンプト:

What is Test Driven Development?

対応:

Test-driven development (TDD) is a software development methodology that requires developers to write automated tests before writing any production code. This helps ensure the code is correct and meets the requirement.
....

TDD とそのメリットについて簡単に説明します。

次に、Gemini に Ruby とは何かを相談してみましょう。

プロンプト:

What is Ruby?

対応:

Ruby is a general-purpose, interpreted, object-oriented programming language. It is designed to be easy to read and write. Ruby is dynamically typed, meaning that the type of a variable is not declared and can change during execution.
....

Ruby とは何か、言語として Ruby を使用するメリットについて説明します。

4. プロジェクトのセットアップ

簡単な Sinatra アプリを作成することから始めましょう。

  • 空のフォルダを作成しましょう。任意の名前を付けることができます。この Codelab では gemini-tdd とします。左側の Cloud エディタの [Explorer] タブで、/home/<YOUR-USERNAME>/ の下にフォルダを作成します。

d1d920b074cd7c79.png

  • 作成したフォルダを開きましょう。左端のメニューでアイコン 1d080370a3ccad53.png をクリックし、[File] -> [Open Folder] をクリックします。
  • このルートフォルダに、アプリの開始点となる新しい Ruby ファイルを作成しましょう。任意の名前を付けることができます。この Codelab では app.rb とします。 7beab8d21c59eee7.png
  • ルートフォルダに Gemfile も追加しましょう。Gemfile について Gemini に相談するのもよいでしょう。
  • テストケースを記述する app_test.rb ファイルも作成します。

5. Gemini による実装

まず、Gemini に実行しようとしていることのコンテキストを伝え、それに対するテストケースを生成するようにリクエストします。次のプロンプトを Gemini とのチャット プロンプトにコピーします。

プロンプト:

Answer as a software engineer with Ruby expertise. We want to build a Ruby calculator application with a test driven development approach. Let's start by writing some tests for the calculator app using minitest gem.

対応:

Here is a simple test for a calculator app that adds two numbers:
require "minitest/autorun"

describe "Calculator" do
  it "should add two numbers" do
    calculator = Calculator.new
    assert_equal 5, calculator.add(2, 3)
  end
end
This test uses the assert_equal method to verify that the result of the add method is correct. 
.....

計算機アプリのサンプル テストケース(2 つの数値を加算する)が表示されます。また、テストに必要な依存関係 minitest/autorun もインポートされました。コンテンツを app_test.rb にコピーしましょう。このテストを実行してみましょう。コードが記述されていないため、必ず失敗します。

このテストを実行する方法について Gemini に相談してみましょう。

プロンプト:

How do we run the tests written in app_test.rb using ruby command?

対応:

To run the tests written in app_test.rb using the ruby command, you can use the following steps:
1.Make sure that you are in the same directory as the app_test.rb file.
2.Open a terminal window.
3.Type the following command:
ruby app_test.rb
This will run all of the tests in the app_test.rb file and report the results.

それでは、テストを実行してみましょう。[Explorer] タブで app_test.rb を右クリックし、[Open in Integrated Terminal] をクリックします。

66b4bb9598c1feca.png

ファイル app_test.rb と同じフォルダにいることを確認します。そうでない場合は、cd コマンドを使用して移動します。ターミナルで、Gemini から返されたコマンドを実行します。

ruby app_test.rb

次のエラーが発生します。

NameError: uninitialized constant Calculator

エディタから直接 Gemini に修正方法を相談してみましょう。Cloud Shell エディタのナビゲーション ペインでチャット アイコンをクリックし、次のプロンプトを入力します。

プロンプト:

NameError: uninitialized constant Calculator. Give me Calculator class code.

対応:

The error you are getting is because you have not defined the Calculator class. You need to define the Calculator class before you can use it in your tests.
Here is the code for the Calculator class:
Class Calculator
  def add(a, b)
    a + b
  end
end
Once you have defined the Calculator class, you can run the tests again and they will pass.
.....

Gemini は、Calculator というクラスがないため、作成する必要があることを伝えています。また、Calculator クラスを作成するコードも提供しています。このコードを使用しましょう。[Explorer] タブから app.rb を開き、Gemini から提供されたコードをコピーします。app.rb のコンテンツを変更しているときに、Gemini から自動補完として提案が表示されることがあります。提案を受け入れる場合は tab キーを押し、受け入れない場合は Esc キーを押します。

もう一度テストを実行してみましょう。統合ターミナルで次のコマンドを入力します。

ruby app_test.rb

ただし、同じエラー「NameError: uninitialized constant Calculator」が発生します。Gemini に修正方法を相談してみましょう。

6. Gemini を使用した反復的な TDD

プロンプト:

I am still getting this error: "NameError: uninitialized constant Calculator".

対応:

The error you are getting is because you have not defined the Calculator class. You need to define the Calculator class before you can use it in your tests.
Here is the code for the Calculator class:
class Calculator
  def add(a, b)
    a + b
  end

  def subtract(a, b)
    a - b
  end

  def multiply(a, b)
    a * b
  end

  def divide(a, b)
    a / b
  end
end
....

上記と同じレスポンスが表示される場合とされない場合があります。そこで、app.rb に Calculator クラスが定義されていることを Gemini に明示的に伝えます。

プロンプト:

Calculator class is defined in app.rb already.

対応:

I see that the Calculator class is defined in the app.rb file. However, you are getting the NameError error because you are trying to use the Calculator class in the app_test.rb file without requiring the app.rb file.
To fix this, you need to add the following line to the top of the app_test.rb file:
require_relative "app"

Gemini から、Calculator クラスが定義されている app.rb をインポートするレスポンスが返されました。適切なレスポンスを得るまでに 2 ~ 3 回のプロンプトの反復が必要になる場合がありますが、慣れてくると改善されます。Gemini からのレスポンスで、代わりに「require "./app" 」が表示される場合もあります。これを app_test.rb の app.rb に追加して、同じテストをもう一度実行しましょう。

この時点で、app_test.rb が次のようになっていることを確認してください。

require "minitest/autorun"
require "./app"

describe "Calculator" do
  it "should add two numbers" do
    calculator = Calculator.new
    assert_equal 5, calculator.add(2, 3)
  end
end

プロジェクト フォルダ内の Cloud Shell ターミナルから次のコマンドを実行します。

ruby app_test.rb

すべてがうまくいけば、テストは合格するはずです。このように、TDD アプローチを使用して非常にシンプルな Ruby アプリを開発し、Gemini のサポートを受けました。プロンプトのレスポンスは常に同じになるとは限りません。これは LLM モデルの特性によるものです。Gemini から電卓の操作がさらに提案される可能性がありますが、先ほどの手順で問題なく動作するはずです。

7. アプリを Cloud Functions にデプロイする

Gemini に、これを Cloud Functions にデプロイするようにリクエストしましょう。

プロンプト:

Let's deploy this app to Cloud Functions

対応:

To deploy your app to Cloud Functions, you can use the following steps:
1.Create a new Cloud Functions project.
2.Create a new Cloud Function.
3.Set the trigger for the Cloud Function to be an HTTP request.
4.Set the entry point for the Cloud Function to be the app method in the app.rb file.
5.Deploy the Cloud Function.
Once you have deployed the Cloud Function, you can test it by sending an HTTP request to the function's URL. 
....

Gemini は、Cloud Functions UI を使用して手動でデプロイする方法を説明します。しかし、手動でデプロイしたくありません。統合ターミナルから直接実行できる gcloud コマンドを Gemini に相談しましょう。

プロンプト:

How do I deploy this app to Cloud Functions gen2 using the gcloud command?

対応:

...
Here are the specific commands you can use to deploy your app to Cloud Functions using the gcloud command:

gcloud functions deploy calculator --runtime ruby32 --trigger-http --entry-point app

This command will create a new Cloud Function called calculator that is triggered by an HTTP request and uses the app method in the app.rb file as the entry point.

Once the Cloud Function is deployed, you can test it by sending an HTTP request to the function's URL. For example, you can use the following command to send a request to the function:

curl -X POST -H "Content-Type: application/json" -d '{"a": 2, "b": 3}' https://REGION-PROJECT_ID.cloudfunctions.net/calculator

Gemini から提供された gcloud コマンドをターミナルで実行しましょう。

gcloud functions deploy calculator --runtime ruby32 --trigger-http --entry-point app

次のエラーが発生します。

ERROR: (gcloud.functions.deploy) OperationError: code=3, message=Build failed: Could not find Gemfile.lock file in your app. Please make sure your bundle is up to date before deploying.; Error ID: 7916f05c

Gemini のサポートを受けてエラーを修正しましょう。

プロンプト:

ERROR: (gcloud.functions.deploy) OperationError: code=3, message=Build failed: Could not find Gemfile.lock file in your app. Please make sure your bundle is up to date before deploying.; Error ID: 7916f05c

対応:

It looks like you're trying to deploy a function to Cloud Functions, but you don't have a Gemfile.lock file in your app. To fix this, run the following command in your app directory:
bundle install
This will create a Gemfile.lock file that lists all of the gems that your app uses.
...

ターミナルで bundle install を実行して Gemfile.lock を作成しましょう。コマンドを実行したら、[Explorer] タブを確認します。Gemfile.lock が表示されているはずです。

もう一度 Cloud Functions にデプロイしてみましょう。ターミナルで同じデプロイ コマンドを実行します。

gcloud functions deploy calculator --runtime ruby32 --trigger-http --entry-point app

次のエラーが発生します。

ERROR: (gcloud.functions.deploy) OperationError: code=3, message=Build failed: unable to execute functions-framework-ruby; please ensure a recent version of the functions_framework gem is in your Gemfile; Error ID: 89ae7dda

8. Functions Framework for Ruby

エラーについて Gemini に相談してみましょう。

プロンプト:

ERROR: (gcloud.functions.deploy) OperationError: code=3, message=Build failed: unable to execute functions-framework-ruby; please ensure a recent version of the functions_framework gem is in your Gemfile; Error ID: 89ae7dda

対応:

It looks like you're trying to deploy a function to Cloud Functions, but the build is failing because the functions_framework gem is not in your Gemfile. To fix this, add the following line to your Gemfile:

gem "functions_framework"

Then, run the following command to install the gem:

bundle install

Once the gem is installed, you can try deploying your function again.
....

つまり、アプリに functions_framework gem がありません。Gemfile に次の行を追加しましょう。

gem "functions_framework"

ターミナルで bundle install を実行します。 「No explicit global resource in Gemfile」というエラーが発生する場合としない場合があります。このエラーが発生した場合は、Gemfile の 1 行目を次のようにします。

source "https://rubygems.org"

次に、ターミナルから bundle install をもう一度実行します。もう一度デプロイ コマンドを実行しましょう。

gcloud functions deploy calculator --runtime ruby32 --trigger-http --entry-point app --project PROJECT_ID

次のエラーが発生します。

ERROR: (gcloud.functions.deploy) OperationError: code=3, message=Build failed: failed to verify function target "app" in source "app.rb": Undefined function: "app"; Error ID: e12ab0f9

この問題を解決するために、Gemini の Generate Code 機能を使用してみましょう。[Explorer] タブから app.rb を開きます。ここには、計算機アプリのコードがすでにあります。app.rb の 1 行目に「FunctionsFramework」と入力し、「.」と入力します。グレーのテキストで候補が表示されます。

9e17476a4a586433.png

Tab キーを押して提案を承認します。承認されると、次のような候補が表示されます。

f3c35d1539789acc.png

Tab キーを押して、さらに提案を承認します。app.rb は次のようになります。

(関数名を add から app に変更します)

require "functions_framework"

FunctionsFramework.http "app" do |request|
 a = request.params["a"].to_i
 b = request.params["b"].to_i
 Calculator.new.add(a, b)
end

class Calculator
 def add(a, b)
   a + b
 end
end

Cloud Functions アプリのエントリ ポイントを作成し、エディタで Gemini のコード候補を使用する方法を学習しました。それでは、アプリをもう一度デプロイしてみましょう。

gcloud functions deploy calculator --runtime ruby32 --trigger-http --entry-point app --project PROJECT_ID

すべてがうまくいけば、デプロイが完了し、アプリがホストされている URL が表示されます。では、app.rb を少し変更して、加算結果を返すようにしましょう。最終的な app.rb は次のようになります。

require "functions_framework"

FunctionsFramework.http "app" do |request|
 a = request.params["a"].to_i
 b = request.params["b"].to_i
 calculator = Calculator.new
 add = calculator.add(a, b)
 "Result: #{add}"
end

class Calculator
 def add(a, b)
   a + b
 end
end

9. エンドツーエンド テスト

URL を開き、次のようにクエリ パラメータ ab を渡します。

https://[APP-URL]/?a=2&b=3

レスポンスは「"Result: 5"」と表示されます。電卓には多くの操作があります。このアプリを拡張して、これらの操作も実行できるようにし、Gemini のサポートを受けることができます。

10. まとめ

Gemini のサポートを受けて、TDD アプローチを使用して Ruby アプリを開発する方法について説明しました。Gemini からは異なる結果が返される可能性がありますが、あまり詳しくない言語での開発プロセスを加速し、学習曲線を加速させる関数やスニペットを表示できます。

経験に関係なく、すべてのデベロッパーが Gemini のサポートを受けて開発プロセスを進めることができます。開発以外にも、Gemini を使用して既存のコードを説明したり、既存のコードのテストを生成したりできます。これは少なくとも私にとっては最も退屈な作業です。

Gemini を使用してコーディングをお楽しみください。