程式碼研究室:以 Gemini 加速開發測試導向的開發作業

1. 簡介

本程式碼研究室介紹測試導向開發原則 ( TDD ),您可以在實作程式碼之前撰寫測試。我們會使用 Ruby 建構簡單的計算機應用程式,說明這個方法。

您也會瞭解如何運用 Gemini 做為程式設計助理。Gemini 可以產生測試案例、建議導入程式碼,甚至提供程式碼說明,協助加快 TDD 週期。

最後,您將瞭解如何將計算機應用程式部署至 Google 的全代管無伺服器平台 Cloud 函式,讓您的應用程式更容易使用。

2. 必要條件

  • 已啟用帳單的 Google Cloud 專案
  • 網路瀏覽器

建立專案

啟用 Gemini API

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

啟用 Cloud Functions API

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

啟用 Cloud Shell

  • 您將使用 Cloud Shell,這是在 Google Cloud 控制台執行的指令列環境,已預先載入必要的語言。在 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 指令和使用方式,請參閱說明文件

開啟編輯器

  • 在本程式碼研究室中,我們將使用內建的 Cloud 編輯器。在 Cloud Shell 視窗中,按一下右上角的「Open editor」按鈕。系統會為您開啟 VSCode 編輯器。

e5a5e95d543db556.png

新增 Ruby 擴充功能

  • 在左側選單中點選擴充功能圖示 999939b067e1d0dc.png,在搜尋列中編寫 vscode ruby 並安裝 VSCode Ruby 擴充功能。

a6fd4e37b3d847d2.png

3. Gemini 暖身

LLM 並非確定性,因此,當您嘗試這些提示時,您收到的回應可能會與程式碼研究室附加的螢幕截圖中的回應不同。

首先,請向 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 是什麼意思:

提示:

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 應用程式開始。

  • 我們要建立一個空資料夾。命名方式不限。在本程式碼研究室中,我們會將其命名為 gemini-tdd。前往雲端編輯器,在左側「Explorer」分頁下方,在「/home/<YOUR-USERNAME>/」中建立資料夾

d1d920b074cd7c79.png

  • 開啟剛剛建立的資料夾在最左側的選單中,依序點選「File」圖示 1d080370a3ccad53.png 和「File」->開啟資料夾。
  • 讓我們在這個根資料夾建立新的 Ruby 檔案,做為應用程式的起點,您可以隨意命名。我們會在本程式碼研究室中稱為 app.rb7beab8d21c59eee7.png
  • 我們也在根資料夾中新增 Gemfile。建議你也問 Gemini 有關「Gemfile」的問題。
  • 我們也會建立 app_test.rb 檔案來撰寫測試案例。

5. 以 Gemini 驅動的實作方式

我們先來向 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. 
.....

它會提供「計算機」應用程式的測試案例範例,即新增兩個數字。同時也匯入了測試所需的依附元件 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 類別的程式碼。繼續使用這段程式碼從「多層檢視」分頁開啟 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
....

結果可能與上述情況不同,因此,我們要向 Gemini 明確指出,「計算機」類別已在 app.rb 中定義。

提示:

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 的回覆,指出要匯入已定義計算機類別的 app.rb。有時可能需要 2 到 3 次提示才能獲得正確回應,但抓出時機後會越來越好。請注意,在 Gemini 的回覆中,您可能也會看到需要「./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 函式:

提示:

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 手動部署。但我不想這麼做,要求 Gemini 提供 gcloud 指令,我可以直接從整合式終端機執行指令。

提示:

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. 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。您可能不一定會看到「Gemfile 中沒有明確的全域資源」錯誤訊息,直接在 Google Cloud 控制台實際操作。如果遇到這種情況,請確認 Gemfile 的第一行程式碼如下:

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 功能。我們已在「多層檢視」分頁中開啟 app.rb,這裡已有「計算機」應用程式的程式碼。在 app.rb 的第一行中,開始輸入 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

如果一切順利,系統就會執行部署作業,您應該會看到應用程式的代管網址。好,讓我們稍微修改 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. 端對端測試

開啟網址並傳遞查詢參數 ab,如下所示:

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

您應該會看到以下格式的回應:"Result: 5"。計算機的用途很多。這時您可以擴充這個應用程式,讓 Gemini 助您一臂之力。

10. 結語

我們瞭解如何在 Gemini 的協助下,使用 TDD 方法開發 Ruby 應用程式。如您所見,Gemini 可能會提供不同的結果,但用您不熟悉的語言,絕對能加快開發程序,且仍會顯示功能或程式碼片段,幫助您加快學習曲線。

所有開發人員 ( 無論經驗為何) 都能提供 Gemini 協助進行開發程序。如果尚未開發,可以透過 Gemini 說明現有程式碼。你可以要求 Gemini 針對現有程式碼產生測試,而這對於我而言最無聊的工作至少 🙂?

祝你使用 Gemini 輕鬆寫程式!