Codelab:利用 Gemini 加快由测试驱动的开发

1. 简介

此 Codelab 介绍了测试驱动开发 ( TDD) 的原则,也就是先编写测试,然后再实现代码。我们将使用 Ruby 构建一个简单的计算器应用来演示此方法。

您还将学习如何将 Gemini 用作编码助理。Gemini 可生成测试用例、提供代码实现建议,甚至提供代码说明,从而帮助缩短 TDD 周期。

最后,您将了解如何将计算器应用部署到 Cloud Functions(Google 的全代管式无服务器平台),使您的应用可供用户使用。

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 命令和用法,请参阅文档

打开编辑器

  • 在此 Codelab 中,我们将使用内置的 Cloud 编辑器。在 Cloud Shell 窗口中,点击右上角的“打开编辑器”按钮。这将为您打开一个 VSCode 编辑器。

e5a5e95d543db556.png

添加 Ruby 扩展程序

  • 在左侧菜单中,点击扩展程序图标 999939b067e1d0dc,在搜索栏中输入 vscode ruby,并安装 VSCode Ruby 扩展程序。

a6fd4e37b3d847d2.png

3. Gemini 热身

LLM 具有不确定性。因此,在尝试这些提示时,您收到的回答可能与 Codelab 中随附的屏幕截图中的回答不同。

我们先来问问 Gemini,什么是 TDD。若要在当前使用的同一 Cloud Shell Editor 中与 Gemini 对话,请按以下步骤操作:

  • 在页面左侧找到 Gemini 图标 25fdcae4447d063c,系统会在左侧打开聊天窗口。
  • 您可以开始在显示“Enter a prompt here”的文本框中输入内容了

5e09b0fb6a1b44dd

提示:

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 Editor 中,在“探索器”标签页下,在 /home/<YOUR-USERNAME>/ 下创建文件夹

d1d920b074cd7c79.png

  • 我们来打开刚刚创建的文件夹。在最左侧的菜单中,点击 1d080370a3ccad53 图标,然后点击“文件”->打开文件夹。
  • 我们将在此根文件夹中创建一个新的 Ruby 文件,该文件将用作应用的起点,您可以随意为其命名。在本 Codelab 中,我们将其称为 app.rb7beab8d21c59eee7
  • 我们还在根文件夹中添加一个 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.

现在,我们来运行测试。右键点击“探索器”标签页中的 app_test.rb,然后点击“在集成式终端中打开”

66b4bb9598c1feca

确保您与文件 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 类。

提示:

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 的回答显示 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 界面手动部署它。但我不想这样做。让我们来让 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 的 Functions 框架

我们来问问 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 中无显式全局资源”错误消息,了解所有最新动态。如果遇到这种情况,请确保将以下内容作为 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

按 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 一起愉快地编码!