アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

ラバーダックデバッグ

ラバーダックデバッグ

はじめまして。ベトナム人のクアンと申します。

2020年2月来日してアクトインディで働いています。ブログ初投稿です! どうぞよろしくお願いいたします。

自己紹介

2019年にベトナムのハノイ工科大学を卒業しました。学生時代のインターンではPHPをやっていましたが、今はRubyやJSのコードを書いています。最初に担当したのは「海と日本PROJECT」のお魚パズルのゲームをJSで実装することでした。面白かったです。

いこーよに海の日のお魚パズル

では、本日のメインテーマを始めましょう。

ラバーダックデバッグとは?

ラバーダックデバッグはデバッグの方法です。この手法はThe Pragmatic Programmerという本で紹介されました。

手法は簡単です。コードを1行ずつラバーダック人形に説明します。

なぜラバーダックデバッグを使う?

外国人の新卒として、3つの問題を抱えています

  • いこーよは10年以上メンテナンスされているプロダクトなので、ソースコードが複雑になっていてわかりにくいところがあります。

  • プログラミングを知らない人に説明するとき、技術的な話をすると、その人はあまり分かりません。

  • 他のエンジニアに説明するとき、自分の理解している内容を説明できない

とても大変ですよ!

ですから、理解してもらえるよう説明できるようになるためには、練習するしかありません。

説明を練習する時、常に他の人に聞いてもらえるわけではありません。 一人で相手を想像して練習するの場合、相手は自分ですから、自信があると思うことは全て正しそうと思えます。ですから、問題に気づきにくいです。そして、練習でも緊張してしまうことも、悪いところに気づけなくなります。 ですから、ラバーダックのような具体的な物があるほうがいいです。

ラバーダックデバッグの例

例えば、このFizzBuzzのメソッドをラバーダッグデバッグしてみてください

# lib/fizz_buzz.rb
def fizz_buzz(n)
  case
  when (n % 3) == 0
    'Fizz'
  when (n % 5) == 0
    'Buzz'
  else
    n.to_s
  end
end

FizzBuzzは、以下のとおりに発言が進行します。

  • 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, Fizz Buzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, Fizz Buzz, 31, 32, Fizz, 34, Buzz, Fizz, ...

3で割り切れる場合は「Fizz」、5で割り切れる場合は「Buzz」、両者で割り切れる場合(すなわち15で割り切れる場合)は「Fizz Buzz」。

参考:Wikipedia

ルールにしたがって、テストを書きます。

# test/fizz_buzz_test.rb
require 'minitest/autorun'
require './lib/fizz_buzz'

class FizzBuzzTest < Minitest::Test
  def test_fizz_buzz
    assert_equal '1', fizz_buzz(1)
    assert_equal '2', fizz_buzz(2)
    assert_equal 'Fizz', fizz_buzz(3)
    assert_equal '4', fizz_buzz(4)
    assert_equal 'Buzz', fizz_buzz(5)
    assert_equal 'Fizz', fizz_buzz(6)
    assert_equal 'Fizz', fizz_buzz(9)
    assert_equal 'Buzz', fizz_buzz(10)
    assert_equal 'Fizz Buzz', fizz_buzz(15)
  end
end

テストを実行すると、エラーが発生します。

$ ruby test/fizz_buzz_test.rb
Run options: --seed 62311

# Running:

F

Failure:
FizzBuzzTest#test_fizz_buzz [test/fizz_buzz_test.rb:15]:
Expected: "Fizz Buzz"
  Actual: "Fizz"


rails test test/fizz_buzz_test.rb:5



Finished in 0.001213s, 824.4023 runs/s, 7419.6208 assertions/s.

さあ、デバッグしましょう!

まず、ラバーダックを持ちましょう!(ぬいぐるみとかでもOK)

ラバーダックに全部のテストを説明してあげます。

  • 自分:1の場合は1
  • ダック(ダックを押す):ガーガー
  • 自分:2の場合は2
  • ダック:ガーガー
  • 自分:3の場合はFizz
  • ダック:ガーガー
  • 自分:4の場合は4
  • ダック:ガーガー
  • 自分:5の場合はBuzz
  • ダック:ガーガー
  • 自分:6の場合はFizz
  • ダック:ガーガー
  • 自分:9の場合はFizz
  • ダック:ガーガー
  • 自分:10の場合はBuzz
  • ダック:ガーガー
  • 自分:15の場合はFizzBuzz
  • ダック:ガーガー
  • 自分:じゃ、テストには問題がない
  • ダック:ガーガー

次、logやメソッドを確認します

  • 自分:失敗したのは15の15のケースです
  • ダック:ガーガー
  • 自分:エラーはExpected: "Fizz Buzz"/Actual: "Fizz"です。
  • ダック:ガーガー
  • 自分:というのは、3で割り切れるを処理するだけです。
  • ダック:ガーガー
  • 自分:コードを見て、3のケースは前ですから、"Fizz"はもちろんです。
  • ダック:ガーガー
  • 自分:そのあと、5のケース...
  • ダック(押さない):...

コードを説明してみて、問題をみつけました

次はコード編集。5のケースは前になって編集すると、結果も失敗しました。でも、今回のlogはActual: "Buzz"です。やはり、"FizzBuzz"は別のケースにしなければならないです。このコードを追加します。

  when (n % 15) == 0
    'Fizz Buzz'

じゃ、もう一度テストを実行します

$ ruby test/fizz_buzz_test.rb
Run options: --seed 1771

# Running:

.

Finished in 0.000882s, 1133.7868 runs/s, 10204.0816 assertions/s.
1 runs, 9 assertions, 0 failures, 0 errors, 0 skips

成功しました!

投稿は以上です。最後まで読んでいただいてありがとうございます。