RubyKaigi 2024で発表されたRSpecテストコード生成ツール Omochi についてまとめてみた

# はじめに
私は[株式会社エル・ティー・エス リンク](https://lts-link.com/)でRailsエンジニアをしているysk_ymdyと申します。
担当サービスは、IT人材と案件のマッチングプラットフォーム [アサインナビ](https://assign-navi.jp/)です。

先日「RubyKaigi 2024」に参加し、たくさんの学びや気づきを得ることができました。
その中でも<a href="https://qiita.com/mikiko_bridge">@mikiko_bridge</a>さんさんが発表された Omochi というgemが、私含めRailsで開発するエンジニアにとって大変役立つものなのではと思い、共有させていただきます。

https://github.com/mikik0/omochi

# Omochiってどんなgem?
ひとことで言うと、RSpecを導入しているRailsアプリケーションに対して、LLMを使ってテストコードを自動生成してくれるgemです。

Railsで開発する際、テストコードの作成は手間がかかったり抜け漏れが気になったりすることがあると思います。
その手助けになるツールがOmochiなんです💡

詳しい説明は、@mikiko_bridgeさんの[Github](https://github.com/mikik0/omochi)での文面をお借りしながら以下で紹介いたします🙇‍♂️
> Omochi is a CLI tool to support Ruby on Rails development with RSpec. It detects methods uncovered by tests and prints the test code generated by LLM.

Omochiは、RSpecを使用したRuby on Rails開発をサポートするCLIツールです。テストで発見されたメソッドを検出し、LLMによって生成されたテストコードを出力します。

> There are two advantages of using Omochi. We can make sure every method is covered by tests with GitHub Actions support. We can also generate spec files for uncovered methods so that we can reduce time to write them manually.

Omochiを使用する利点は2つあります。GitHub Actionsのサポートにより、すべてのメソッドがテストでカバーされていることを確認できます。また、カバーされていないメソッドの仕様ファイルを生成することもできるため、手動で記述する時間を短縮できます。

# どうやってOmochiを導入するの？

[Github](https://github.com/mikik0/omochi)での記載（2024/5/31時点）を元に順を追って見ていきます。

まずは実行環境です。

- ruby3.x
- AWS Credentials (for --create)

が必要です。
AWS Credentialsについては[こちら](https://dev.classmethod.jp/articles/aws_credential_file_difference/)

次にgemのインストールです。

```
$ gem specific_install -l https://github.com/mikik0/omochi.git
```

ここまでで一旦Omochiの導入は完了です。

# Omochiを実行するには？

同じく[Github](https://github.com/mikik0/omochi)での記載（2024/5/31時点）を元にシチュエーション別に見ていきます。

local_pathに対してspec（テスト）が正しく作成されているか確認したい時
(verifyコマンド)

```
$ omochi verify local_path
```

local_pathに対して新しくspecファイルを作成したい時
(--createオプション)

```
$ omochi verify local_path -c
# もしくは↓
$ omochi verify local_path --create
```

指定されたGithubプルリクエストに対してspecが正しく作成されているか確認したい時
(--githubオプション)
```
$ omochi verify local_path -h
# もしくは↓
$ omochi verify local_path --github
```

コマンドがわからなくなった時
(helpコマンド)
```
$ omochi --help
Commands:
  omochi help [COMMAND]     # 利用可能なコマンド全体、もしくは特定のコマンドの詳細を表示する
  omochi verify local_path  # 新しく作成されたすべてのメソッドおよび関数のために作成されたspec（テスト）を検証する。
```
基本的には、verify, helpコマンドのみのようですね👀

# 実行するとどうなるの？
こちらも同じく[Github](https://github.com/mikik0/omochi)での記載（2024/5/31時点）のサンプルを元に見ていきます。

verifyコマンド-cオプションを実行してみると

```
$ omochi verify -c
"Verify File List: [\"lib/omochi/cli.rb\", \"lib/omochi/util.rb\"]"
"specファイルあり"
===================================================================
verifyのテストを以下に表示します。
require 'rspec'

describe 'exit_on_failure?' do
  it 'returns true' do
    expect(exit_on_failure?).to eq(true)
  end
end
======= RESULT: lib/omochi/cli.rb =======
- exit_on_failure?
"specファイルなし"
======= RESULT: lib/omochi/util.rb =======
- local_diff_path
- github_diff_path
- remote_diff_path
- get_ast
- dfs
- find_spec_file
- get_pure_function_name
- dfs_describe
- print_result
- get_ignore_methods
- create_spec_by_bedrock
===================================================================
lib/omochi/util.rbのテストを以下に表示します。
require "spec_helper"

describe "local_diff_path" do
  it "returns array of diff paths from git" do
    allow(Open3).to receive(:capture3).with("git diff --name-only", any_args).and_return(["path1", "path2"], "", double(success?: true))
    expect(local_diff_path).to eq(["path1", "path2"])
  end

  it "returns empty array if git command fails" do
    allow(Open3).to receive(:capture3).with("git diff --name-only", any_args).and_return("", "error", double(success?: false))
    expect(local_diff_path).to eq([])
  end
end

describe "github_diff_path" do
  it "returns array of diff paths from gh" do
    allow(Open3).to receive(:capture3).with("gh pr diff --name-only", any_args).and_return(["path1", "path2"], "", double(success?: true))
    expect(github_diff_path).to eq(["path1", "path2"])
  end

  it "returns empty array if gh command fails" do
    allow(Open3).to receive(:capture3).with("gh pr diff --name-only", any_args).and_return("", "error", double(success?: false))
    expect(github_diff_path).to eq([])
  end
end

describe "remote_diff_path" do
  it "returns array of diff paths from remote" do
    allow(Open3).to receive(:capture3).with(/git diff --name-only .*${{ github\.sha }}/, any_args).and_return(["path1", "path2"], "", double(success?: true))
    expect(remote_diff_path).to eq(["path1", "path2"])
  end

  it "returns empty array if git command fails" do
    allow(Open3).to receive(:capture3).with(/git diff --name-only .*${{ github\.sha }}/, any_args).and_return("", "error", double(success?: false))
    expect(remote_diff_path).to eq([])
  end
end

describe "get_ast" do
  it "returns AST for given file" do
    allow(File).to receive(:read).with("file.rb").and_return("code")
    allow(Parser::CurrentRuby).to receive(:parse_with_comments).with("code").and_return(["ast"], ["comments"])
    expect(get_ast("file.rb")).to eq([{ast: "ast", filename: "file.rb"}])
  end
end

describe "dfs" do
  let(:node) { double(:node, type: :def, children: [double(:child, children: ["name"])]) }
  let(:result) { {} }

  it "traverses node and captures def names" do
    dfs(node, "file.rb", result)
    expect(result).to eq({"name" => "def name\nend"})
  end
end

describe "find_spec_file" do
  before do
    allow(File).to receive(:exist?).with("spec/app/file_spec.rb").and_return(true)
  end

  it "returns spec file path if exists" do
    expect(find_spec_file("app/file.rb")).to eq("spec/app/file_spec.rb")
  end

  it "returns nil if spec file does not exist" do
    allow(File).to receive(:exist?).with("spec/app/file_spec.rb").and_return(false)
    expect(find_spec_file("app/file.rb")).to be_nil
  end
end

# similarly test other functions
```

このように、生成してくれたコードを.specファイルにコピペするとテストが出来上がるようです🙌
以上が、Omochiの実行結果です。

# さいごに
Omochiで作成されるテストコードは、LLMが生成するコードのためまだ完璧とは言えないようですが、
Omochi実行
↓
コピペ
↓
部分を修正
とspecを書く手間がかなり省けそう かつ メソッドレベルでOmochiが確認してくれるため人間が0から書くより網羅性を担保できそうですね！

@mikiko_bridgeさんも[こちらの記事](https://qiita.com/mikiko_bridge/items/dbb8378a41ba63e36ff7#%E4%BB%8A%E5%BE%8C%E3%81%AE%E5%B1%95%E6%9C%9B)にて、

> Omochi は、開発初期段階であり、以下の機能強化を予定しています。
コード構造の改善による可読性向上
Request spec 対応
Omochi自体のSpecを増やす
GitLab 対応
Omochi は、今後も Ruby on Rails 開発を支援するCLI ツールとして、進化し続けていく予定です。

と今後の展望をお話ししてくださっています。
Railsエンジニアを助ける強力なツールOmochiから今後も目が離せないですね！👀
ローカル環境でデモ等した際、追記していきます。

# 参考
- https://qiita.com/mikiko_bridge/items/dbb8378a41ba63e36ff7
- https://github.com/mikik0/omochi