Codelab: Gemini to accelerate test driven development

1. Introduction

This codelab introduces the principles of Test Driven Development ( TDD ), where you write tests before implementing code. We'll build a simple calculator app using Ruby showcasing this approach.

You'll also learn how to use Gemini, as your coding assistant. Gemini will help accelerate the TDD cycle by generating test cases, suggesting code implementations, and even providing explanations of the code.

Finally, you'll see how to deploy the calculator app to Cloud Function, Google's fully managed serverless platform, making your app accessible.

2. Prerequisites

  • A Google Cloud Project with Billing enabled
  • A web browser

Create your project

Enable the Gemini API

  • Navigate to Gemini Marketplace to enable the API. You can also use gcloud to enable the API:
gcloud services enable cloudaicompanion.googleapis.com --project PROJECT_ID

Enable the Cloud Functions API

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

Activate Cloud Shell

  • You will use Cloud Shell, a command-line environment running in Google Cloud Console that comes pre-loaded with required languages installed. From the Cloud Console, click Activate Cloud Shell on top right corner:

6757b2fb50ddcc2d.png

  • Once connected to Cloud Shell, you should see that you are already authenticated and that the project is set to your project ID. Run the following command in Cloud Shell to confirm that you're authenticated:
gcloud auth list
  • Run the following command in Cloud Shell to confirm that the gcloud command knows about your project:
gcloud config list project
  • If your project is not set, use the following command to set it:
gcloud config set project <YOUR_PROJECT_ID>

Refer documentation for gcloud commands and usage.

Open Editor

  • For this codelab, we're going to use the inbuilt Cloud editor. In the cloud shell window, click on the Open editor button on the top right corner. This will open up a VSCode Editor for you.

e5a5e95d543db556.png

Add Ruby Extension

  • On the left side menu, click extensions icon 999939b067e1d0dc.png, write vscode ruby in the search bar and install the VSCode Ruby extension.

a6fd4e37b3d847d2.png

3. Gemini Warm-Up

LLMs are non-deterministic. So, while you're trying these prompts, the response that you receive might look different from the ones in the screenshots attached in the codelab.

Let's first ask Gemini what a TDD is. In order to start conversing with Gemini in the same Cloud Shell Editor where we are, follow these steps:

  • On the left hand side of the page, locate the Gemini icon 25fdcae4447d063c.png, this will open up the chat window for you on the left side.
  • You can start typing on the textbox which says Enter a prompt here

5e09b0fb6a1b44dd.png

Prompt:

What is Test Driven Development?

Response:

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.
....

It gives you a brief introduction of TDD and its benefits.

Let's now ask Gemini what Ruby is:

Prompt:

What is Ruby?

Response:

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.
....

It explains what Ruby is and what are the benefits of using Ruby as a language.

4. Project Setup

Let's now start by creating a simple Sinatra app.

  • Let's create an empty folder. You can name it whatever you want. We'll call it gemini-tdd for this codelab. In the cloud editor on left side under Explorer tab, create folder under /home/<YOUR-USERNAME>/

d1d920b074cd7c79.png

  • Let's open the folder we just created. On the leftmost menu, click on the icon 1d080370a3ccad53.png, click File -> Open Folder.
  • Let's create a new Ruby file in this root folder which will act as a starting point for the app, you can name it whatever you want. We'll call it app.rb in this codelab. 7beab8d21c59eee7.png
  • Let's also add a Gemfile in the root folder. It'll be good to ask Gemini about the Gemfile as well.
  • We'll also create an app_test.rb file to write our test cases.

5. Gemini-Driven Implementation

Now let's begin by giving Gemini some context on what we're trying to do and ask it to generate test cases for the same. Copy the following prompt in the Gemini chat prompt.

Prompt:

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.

Response:

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. 
.....

It gives you a sample test case for the Calculator app i.e. for adding two numbers. It also imported the required dependency minitest/autorun for the tests. Let's copy the content to app_test.rb. Let's try and run this test now; since we don't have any code written, it'll fail for sure.

Let's ask Gemini how to run this test.

Prompt:

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

Response:

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.

So, let's go ahead and run the test. Right click on app_test.rb in the Explorer tab and click on "Open in Integrated Terminal"

66b4bb9598c1feca.png

Make sure you are in the same folder as the file app_test.rb, if not use the cd command to land there. In the terminal run the command returned by Gemini:

ruby app_test.rb

You'll encounter the following error:

NameError: uninitialized constant Calculator

Let's ask Gemini how to fix it right from the editor. Click on the chat icon in the navigation pane of the Cloud Shell editor and ask the below prompt:

Prompt:

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

Response:

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 is telling us that we don't have a class called Calculator and that we need to create one. It has also given us the code for creating the Calculator class. Let's go ahead and use this code. Open app.rb from the Explorer tab and copy the code given by Gemini in it. While you're modifying the content in the app.rb, you might get some suggestions from Gemini as auto-complete; you need to press tab if you want to accept it or Esc if not.

Now, let's run the test again. Type the following command in your integrated terminal:

ruby app_test.rb

But you'll still encounter the same error "NameError: uninitialized constant Calculator". Let's ask Gemini how to fix it.

6. Iterative TDD with Gemini

Prompt:

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

Response:

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
....

It gave us some response, may or may not be the same as above. So, let's tell Gemini explicitly that we have the Calculator class defined in app.rb .

Prompt:

Calculator class is defined in app.rb already.

Response:

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"

We got the response from Gemini to import the app.rb where we have the Calculator class defined. Sometimes it may take 2-3 iterations of prompts to get the right response but as and when you get the hang of it, it'll get better. Please note you might also be seeing require "./app" instead, in response from Gemini. Let's add this to the app.rb in app_test.rb and run the same test again.

Please make sure that at this point your app_test.rb looks like this:

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

Run the below command from Cloud Shell Terminal within the project folder:

ruby app_test.rb

If all goes well, the test should pass. So, we developed a very simple Ruby app using the TDD approach and we took help from Gemini. The prompt responses are not going to be the same all the time and that's by nature of LLM models. You might get more operations of the Calculator from Gemini, but overall the same process that we just followed, should work.

7. Deploy the app to Cloud Functions

Let's now ask Gemini to deploy this to Cloud Function:

Prompt:

Let's deploy this app to Cloud Functions

Response:

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 explains how to deploy it manually using Cloud Functions UI. But I don't want to do that. Let's ask Gemini to give us the gcloud command which I can run right from the integrated terminal.

Prompt:

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

Response:

...
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

Let's execute the gcloud command given by Gemini in the terminal.

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

You'll encounter following error:

"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"

Let's take help from Gemini to fix the error.

Prompt:

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

Response:

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.
...

Let's run bundle install in the terminal which will create Gemfile.lock . After running the command, check the explorer tab, you should be able to see the Gemfile.lock .

Let's try to deploy to Cloud Functions again. Execute the same deploy command in the terminal:

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

You'll encounter another following error:

"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

Let's ask Gemini about the error:

Prompt:

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

Response:

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.
....

So essentially, we're missing functions_framework gem in our app. Let's go ahead and add this line in our Gemfile:

gem "functions_framework"

Now run bundle install in the terminal. You may or may not encounter an error saying "No explicit global resource in Gemfile" . If you encounter this, make sure to have the following as the first line of your Gemfile:

source "https://rubygems.org"

Then run bundle install from the terminal again. Now, let's run the deploy command again:

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

You'll encounter the following error now:

"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"

In order to solve this, let's try Generate Code feature of Gemini. Let's open app.rb from the Explorer tab, where we already have the Calculator app code. In app.rb, in the first line, start typing FunctionsFramework , and then type ".". You will see some suggestions in gray text :

9e17476a4a586433.png

Press tab to accept the suggestion. Once that's accepted, you'll see more suggestions like shown below:

f3c35d1539789acc.png

Press tab to accept further suggestions. So, your app.rb should look like this:

( Rename the function name from add to 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

We just created the entry point for the Cloud Functions app and we also learnt how to use Gemini's code suggestion in the editor. Now, let's try deploying the app again.

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

And if everything goes well, the deployment should go through and you should be able to see the URL where your app is hosted. Cool, let's modify the app.rb a little bit to return the addition result. Your final app.rb should look like this:

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. End-to-End Testing

Open up the URL and pass on query parameters a and b as shown below:

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

You should be able to see the response as : "Result: 5". There are plenty of operations a Calculator does. You can extend this app to perform those as well and take Gemini's help on that.

10. Conclusion

We saw how to develop a Ruby app today using the TDD approach with the help of Gemini. As you noticed, Gemini may give you different results but it can definitely help accelerate the development process in a language you might not be most familiar with, and still show you functions or snippets to speed up your learning curve.

Any developer ( regardless of the experience ) can take help from Gemini for their development process. If not for developing, you can use Gemini to explain existing code, you can ask it to generate tests for the existing code which is the most boring job for me at least 🙂

Happy coding with Gemini!