TDDとGitHub Copilotで怠惰なプログラミングしちゃおうよ
こんにちは、最近はGitHub Copilotがいないとそわそわしてしまうようになってしまいました。
それに加えてTDDをしないと体が痒くなって来るようにもなってしまいました。
そわそわも体の痒みも止めるために合せ技で怠惰なプログラミングをしてやろう、という記事です。
TDDとGitHub Copilotの合せ技って?
ここでは詳細まで説明はしませんが、TDDは
- レッド : 期待するテストを書き、テストが通らない状態
- グリーン : とりあえず↑のテストが通る状態
- リファクタリング : グリーンを保ちつつ、良いコードにしていく
のサイクルを回すことを指します。
そこで、GitHub Copilotを使いながらレッドの状態を作り、とりあえず通る実装を自分で行い、リファクタリングをGitHub Copilotにしてもらいます。
結果以下のようになります。
- レッド : GitHub Copilot with 自分
- グリーン : 自分 with GitHub Copilot
- リファクタリング : GitHub Copilot
怠惰だなぁ、
前提
本記事の前提はRuby on Railsを使いつつ、テストはRspecで書きます。
また、以下のようなものを実装するとします。
- Userというモデルがあり、複数のUserを束ねるOrganizationモデルがあるとする
- Userの中で一番年齢が高いUserをOrganizationのオーナーとして返す関数を作成したい(年功序列w
User テーブル:
カラム名 | データ型 | 説明 |
---|---|---|
id | integer | ユーザーの一意のID |
string | ユーザーのメールアドレス | |
age | integer | ユーザーの年齢 |
organization_id | integer | ユーザーが所属する組織のID |
created_at | datetime | レコード作成日時 |
updated_at | datetime | レコード更新日時 |
Organization テーブル:
カラム名 | データ型 | 説明 |
---|---|---|
id | integer | 組織の一意のID |
name | string | 組織の名前 |
created_at | datetime | レコード作成日時 |
updated_at | datetime | レコード更新日時 |
class User < ApplicationRecord
belongs_to :organization
end
class Organization < ApplicationRecord
has_many :users
end
レッド1 : GitHub Copilot with 自分
まずは簡単なテストを書いていきます。
ここではすでにorganization
とuser
というFactoryが用意されてるとします
RSpec.describe Organization, type: :model do
describe 'owner' do
subject { organization.owner }
let(:organization) { create(:organization) }
let(:user) { create(:user, organization: organization, age: 25) }
it 'ownerを返す' do
is_expected.to eq user
end
end
end```
ここでも基本的にtabを多用します。
土台作りなのである程度は自分でも用意しないといい感じのサジェストはしてくれないですね。
グリーン1 : 自分 with GitHub Copilot
通るように実装します。一旦グリーンになればいいのでとりあえず動くようにusers.first
でも返します。
class Organization < ApplicationRecord
has_many :users
def owner
users.first
end
end
ここではリファクタリングをすることもないので、リファクタリングの工程は飛ばして再度レッドになるようにします。
レッド2 : GitHub Copilot with 自分
既存のuser
をunexpected_user
にし、expected_user
を追加します。
また、it
の説明文にageが一番高いユーザーを返す
と入れます。
tabを押す前がこちら
RSpec.describe Organization, type: :model do
describe 'owner' do
subject { organization.owner }
let(:organization) { create(:organization) }
let(:unexpected_user) { create(:user, organization: organization, age: 25) }
let(:expected_user) { create(:user, organization: organization, age: 30) }
it 'ageが一番高いユーザーを返す' do
is_expected.to eq user
end
end
end
そしてis_expected.to eq user
の行を消し、サジェストをそのまま入れると
RSpec.describe Organization, type: :model do
describe 'owner' do
subject { organization.owner }
let(:organization) { create(:organization) }
let(:unexpected_user) { create(:user, organization: organization, age: 25) }
let(:expected_user) { create(:user, organization: organization, age: 30) }
it 'ageが一番高いユーザーを返す' do
is_expected.to eq expected_user
end
end
end
になります。無事is_expected.to eq expected_user
をサジェストしてくれました。
グリーン2 : 自分 with GitHub Copilot
雑にageが一番高いユーザーを返すように実装します。
(今回はあえてリファクタリングができるようなcodeにしています)
class Organization < ApplicationRecord
has_many :users
def owner
owner = nil
users.each do |user|
owner = user if owner.nil? || user.age > owner.age
end
owner
end
end
さぁ、リファクタリングをしてもらいましょう。
リファクタリング : GitHub Copilot
ower
の関数を選択してリファクタリングをお願いしましょう
GitHub Copilotくんのリファクタリングしてくれた結果がこちら。
いいですね〜怠惰ですね〜
order: :desc
などを使うほうが早かったりもしそうなので、これもお願いしてみましょう
出来上がったcode
結果出来上がったcodeがこちらです。
class Organization < ApplicationRecord
has_many :users
def owner
users.order(age: :desc).first
end
end
感想
途中謎な実装(リファクタリングを挟みたかったため)なども自分でしましたが、結果いい感じのcodeに落ち着きました。本来のTDDではさらに異常系のテストなどを書いていき、もっとレッド=>グリーン=>リファクタリングのサイクルを回していくはずですが、今回は一旦ここまでにしておきます。
GitHub Copilotがないともうプログラミングができないほど怠惰な体になってしまいましたが、GitHub CopilotくんとTDDの相性は良さそうですね。
とはいえ、どんなcodeが望ましいかなどは実装者の頭にないとリファクタリングの工程で的確な指示はできないことも実感しました。
これからもGitHub Copilotくんと仲良くしていきます〜
Discussion