github copilot + TDD で ズンドコキヨシ

## 経緯

- github copilot と TDD が相性が良いという話を聞いて...
- 試しに ズンドコキヨシでやってみたという話です。

---

## 用意するもの

- github copilot が使える エディタ
  - 自分は neovim を使いました。
- product code
  - zundoko_kiyoshi.rb
- テストコード
  - test_kiyoshi.rb

---
## チャレンジ内容

- コードは極力打たない
- コメント文でgithub copilotを誘導する

---

## 問題文

- 「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」が出たら「キ・ヨ・シ！」と出力して終了

---

## 問題を分解する

- ランダムに ズンとドコを標準出力する
  - 配列に出力予定を保存する
- 配列を 結合して ズンズンズンズンドコ が含んでいたら 真を返す
- ズンズンズンズンドコになるまで処理をloopする
  - 条件が整ったら
    - キヨシ！ と出力する
    - loop を抜ける

---

## 各個撃

- ランダムに ズンとドコを標準出力する
  - 配列に出力予定を保存する

---

### 必要なライブラリを準備

- Gemfile

```ruby
# frozen_string_literal: true

source "https://rubygems.org"

gem "minitest"
gem "minitest-spec-context"
gem "minitest-reporters"
```

- bundle i

---

### 実行コード側を用意

- test_kiyoshi.rb

```ruby
require './zundoko_kiyoshi'
require 'minitest/autorun'
require 'minitest/spec'
require 'minitest-spec-context'
require 'minitest/reporters'
Minitest::Reporters.use!

describe 'ズンドコキヨシ' do
  before do
    @ki = ZundokoKiyoshi.new
  end

  context 'ズンかドコをランダムに出す' do
    it '@ki.zundokoがズンかドコか？' do
      assert_includes %w(ズン ドコ), @ki.zundoko
    end
  end
end
```

---

### REDを確認

- bundle exec ruby test_kiyoshi.rb

---

### product code

- zundoko_kiyoshi.rb

```ruby
class ZundokoKiyoshi
  def initialize
    @stock = []
  end

  # ランダムにズンとドコを返却する
  # また、@stockに追加する
end
```

---

### コメント文の後ろに挿入しようとしばらく待つと...

- あら、便利。下記のコードが出るのでタブキーを押してそのまま採用

```ruby
class ZundokoKiyoshi
  def initialize
    @stock = []
  end

  # ランダムにズンとドコを返却する
  # また、@stockに追加する
  def zundoko
    result = ["ズン", "ドコ"].sample
    @stock << result
    result
  end
end
```

---

### GREEN を確認

- bundle exec ruby test_kiyoshi.rb
- クリア: ランダムに ズンとドコを標準出力する
  - リファクタリングを飛ばしてない？と思われるので一応
  - ここまでコードが短いと「明白な実装」になってるので、飛ばしてます。

---

### 次の各個撃破

- 配列を 結合して ズンズンズンズンドコ が含んでいたら 真を返す
- test_kiyoshi.rb

```ruby

require './zundoko_kiyoshi'
require 'minitest/autorun'
require 'minitest/spec'
require 'minitest-spec-context'
require 'minitest/reporters'
Minitest::Reporters.use!

describe 'ズンドコキヨシ' do
  before do
    @ki = ZundokoKiyoshi.new
  end

  context 'ズンかドコをランダムに出す' do
    it '@ki.zundokoがズンかドコか？' do
      assert_includes %w(ズン ドコ), @ki.zundoko
    end
  end

  context 'ズンズンズンズンドコ を評価' do
    it '@stockにズンズンズンズンドコ が含んでいたら 真を返す' do
      @ki.stock = %w(ズン ズン ズン ズン ドコ)
      assert @ki.kiyoshi?
    end
  end
end
```

---

## RED を確認

- bundle exec ruby test_kiyoshi.rb

---

## product code に追加

- コメント文まで書く

```ruby
class ZundokoKiyoshi
  def initialize
    @stock = []
  end
  
  attr_accessor :stock

  # ランダムにズンとドコを返却する
  # また、@stockに追加する
  def zundoko
    result = ["ズン", "ドコ"].sample
    @stock << result
    result
  end

  # @stockをjoinして ズンズンズンズンドコを含んだら
  # trueを返却する
end
```

---

### コメント文の後に出るかな？

- 候補が出るので、同じようにタブキーを押して採用

```ruby
class ZundokoKiyoshi
  def initialize
    @stock = []
  end

  attr_accessor :stock

  # ランダムにズンとドコを返却する
  # また、@stockに追加する
  def zundoko
    result = ["ズン", "ドコ"].sample
    @stock << result
    result
  end

  # @stockをjoinして ズンズンズンズンドコを含んだら
  # trueを返却する
  def kiyoshi?
    @stock.join.include?("ズンズンズンズンドコ")
  end
end
```

---

### GREEN を確認

- bundle exec ruby test_kiyoshi.rb

---

### 条件が整ったら キヨシ！ と出力する

- 疑似コード通りに整えるだけ

```ruby
require './zundoko_kiyoshi'
require 'minitest/autorun'
require 'minitest/spec'
require 'minitest-spec-context'
require 'minitest/reporters'
Minitest::Reporters.use!

describe 'ズンドコキヨシ' do
  before do
    @ki = ZundokoKiyoshi.new
  end

  context 'ズンかドコをランダムに出す' do
    it '@ki.zundokoがズンかドコか？' do
      assert_includes %w(ズン ドコ), @ki.zundoko
    end
  end

  context 'ズンズンズンズンドコ を評価' do
    it '@stockにズンズンズンズンドコ が含んでいたら 真を返す' do
      @ki.stock = %w(ズン ズン ズン ズン ドコ)
      assert @ki.kiyoshi?
    end
  end

  context 'ズンズンズンズンドコになったらキ・ヨ・シ！' do
    it '条件が整ったらキ・ヨ・シ！と出力しloopを抜ける' do
      loop do
        puts @ki.zundoko
        if @ki.kiyoshi?
          puts 'キ・ヨ・シ！'
          break
        end
      end
    end
  end
end
```

---

## 結論

- うまくいった
- メソッドが短くなればなるほど...リファクタリングがなくなる
  - 自動生成で十分行けちゃう