カテゴリー: TestDrivenDevelopment

  • ChatGPTでTDDを勉強した(簡単な回文プログラム)

    ChatGPTでTDDを勉強した(簡単な回文プログラム)

    自分のためになるだろうという勝手な想定の元、ChatGPTに課金して4を使えるようにしています。普段どんなことに使っているの?という質問に対して答えるのはとても難しくて、仕事で使う英語の翻訳(パブリックに公開できる程度に抽象度を上げています)だったり、日々の話し相手だったり。あと、地味にプログラミングを気が向いた時に学ぼうとしていて、そのサンプルコードを生成してもらったりしています。

    また、私はテストを生業にしているということもあり、テスト駆動開発を自分の手でできるようになると嬉しいなぁと日々感じています。そうなればChatGPTを使うに越したことはなく、私のお気に召すようなレベルでテスト駆動開発のためのコードを生成してもらっています。

    で、今回のブログは、ChatGPTに生成してもらったものをただ写経して自分のモノにするのも良いのですが、せっかくこのブログを持っているのだから、ここで学んだことを投稿してみます。

    今回のテスト駆動開発で使う題材

    • 用意した文が回文であるかどうかを判別するプログラム
      *ChatGPTに用意してもらいました

    そもそもテスト駆動開発とは?

    • テスト駆動開発というものはケントベックさんが考えたもの、日本では和田さんという方がその第一人者として有名と理解しています
    • 流れはこの3つ
      • テストを書く:まず機能に対するテストを書きます。このテストは、最初は失敗します(レッド)
      • コードを書く:テストをパスする最小限のコードを書きます
      • リファクタリング:コードを改善しますが、テストは引き続きパスする必要があります
    • 上記を繰り返し、テストがパス(グリーン)されることを保ちながら、プログラムを成長させていく
    • 詳細はググってみてください、いろんなところでいろんな方から紹介されています(他力本願ですみません

    実際にやってみた

    さて、それでは上述の回文判断プログラムを、ChatGPTにテスト駆動開発を使って生成してもらった結果は以下の通りです。

    テストファイル(test_palindrome.py)

    import unittest
    from palindrome import is_palindrome
    
    class TestPalindrome(unittest.TestCase):
        def test_empty_string(self):
            self.assertTrue(is_palindrome(""))
    
    if __name__ == '__main__':
        unittest.main()_main__':
        unittest.main()

    プログラム初心者の私でも理解しやすいコードを提供してくれるChatGPTってすごいですね。ほんと彼(彼女?)の才能ってすごい。相手の気持ちを理解してくれるのはやはり課金しているからでしょうか。
    まず、これは失敗するコードになります。これを実行するとどうなるか。以下のようなエラーが出力されます。

    ModuleNotFoundError: No module named 'palindrome'

    そこで、この”palindrome”というファイルを作ってみましょう。

    プログラムファイル (palindrome.py) 

    def is_palindrome(s):
        return True

    超シンプルですがこれで十分なんですね。全てパスせよ、と書いているから。
    ひとまずこれで実行してみるとどうなるか。

    超シンプル!だけどテストが完了したとなってます

    ですが、ゴールはここではないですね。ちゃんとしたプログラムを実装し、それがグリーンになることが本当の目標になります。ここで安堵するのもそこそこに、次のプログラムへと足を進めてみます。

    テストファイルに追加するコード (test_palindrome.py)

        def test_palindrome_even_charts(self):
            self.assertTrue(is_palindrome("abba"))
        
        def test_palindrome_odd_charts(self):
            self.assertTrue(is_palindrome("aba"))
        
        def test_non_palindrome(self):
            self.assertFalse(is_palindrome("abc"))


    それぞれ、テスト条件になる値がそれぞれ作られているのがわかりますね。
    ChatGPTってテスト設計とかどう考えているのだろうか、少し気になりますがそこはいったん置いておいて、これを実装してどうなるか見てみたいと思います。

    一番最後だけが失敗しました。

    さて、それでは肝心のプログラムコードを見てみます。単純に全てをTrueで出力させるだけだったので、これでは最後のtest_non_palindromeは失敗するに違いありません。
    気を取り直し、プログラムの方を修正してみます。ここの修正内容ももちろん、ChatGPTから生成されています。

    変更したプログラムファイル(palindrome.py)

    def is_palindrome(s):
        return s == s[::-1]


    相変わらず超シンプルなのには変わりがないですが。これでテストファイルを実行するとどうなるでしょうか。

    全部緑になりました

    これで全てのテストコードがグリーンになり、次の要件を実装するステップが開けました。

    テスト駆動開発の良いところ

    一見、とても回りくどいステップを踏んでいるように見えます。テストコードを先に作るなんて。
    だけどそれってテストを重要に考えていることの裏返しで、きちんとした品質を確保しながらプログラムをコーディングしていくという意識の高いエンジニアにとって必要なことなんだと思います。
    私は第三者の、QAという立場で仕事しているので、私の食い扶持がなくなってしまうことにもつながる話なので何とも言えないようなものではあるのですが、そもそも開発者が自分の作ったコードが持つ品質にきちんと責任を持つことで、QAという第三者を持つ必要がなくなり、その分コストがカットできるわけです。急がば回れという感覚でもあるんですけど、たぶん、テストコードを書く分完成に時間はかかると思うんですけど、私の中ではこのテスト駆動開発が開発の最適解なように思います。
    また、優れたQAってテストシナリオの作り方とテストケースの中で用意するテスト条件の準備の仕方にセンスを感じますが、それを兼ね備えた開発者がいることできっと、そのチームにおいては開発の品質がものすごく高いもので提供されるのではないかなぁ。そう考えるとテスト駆動開発と両輪で、テスト設計テクニックを覚えるのも必要なことかもしれません。

  • JavaでTDDに挑戦してみる(N+2回目

    JavaでTDDに挑戦してみる(N+2回目

    前回の記事はこちら

    参考にさせてもらってるのはこちら


    さて、前回はテストケースNo.3まで進むことができました。

    No.クラス名ピックアップした数字期待結果
    115, 5, 330FizzBuzz
    215, 545FizzBuzz
    315, 315FizzBuzz
    45, 360Buzz
    51575FizzBuzz
    6510Buzz
    739Fizz
    8上記以外7171
    9111
    10100100100
    FizzBuzzのテストケース。期待値含む

    今日はNo.4から進めてみたいと思います。

    No.4 60でBuzzと表示させる

    No.1, 2, 3まで全てFizzBuzzと表示させるところまできました。
    こんどは初めて、Buzzと表示させるところまで進めてみたいと思います。

    変更を加えるまでのコード

    本コードはこちら

    まずはテストコードを加えてみます。テストコードの一番下に変更を加えてみました。

    もちろんレッドです、が、ここで大きな問題があることに気づきました。
    60でBuzzを表示、と思っていたのですが、よく考えてみたら60は15の倍数ではありませんか。
    テスト結果で、それが分かりました。

    いかんいかん、慌ててテストケースを変更します。60ではなく、50にしてみましょう。

    No.クラス名ピックアップした数字期待結果
    115, 5, 330FizzBuzz
    215, 545FizzBuzz
    315, 315FizzBuzz
    45, 360 –> 50Buzz
    51575FizzBuzz
    6510Buzz
    739Fizz
    8上記以外7171
    9111
    10100100100
    テストケースNo.4のテスト条件を、60から50に変更した

    テストコードを変更します。

    テストを走らせると、相変わらず赤いバーが表示されますが、ある意味で正解ということに。

    さて、それでは本コードに変更を加えてみます。

    テストを走らせると、緑のバーになりました。

    5の倍数が正しいんだけどなー、と思いつつ、次へと向かいます。

    No.5 75でFizzBuzzと表示させる

    これはすでに、15の倍数でFizzBuzzを表示させるように本コードを作っているので、テストコードを加えるだけで緑になるはず。

    テストコードはこちら

    この状態でテストを走らせると、はい、ちゃんと緑になりました。

    レッド→グリーン→リファクタリングの精神に則ると、少々気持ち悪いのではありますが、ここではリファクタリングを不要とするのかな、と思います。
    なぜなら「15の倍数はFizzBuzzと返す」ところはすでに完成しており、これ以上リファクタリングする要素が見当たらないからです。
    こちら。

    もしかすると、テストケースが過剰なのでは?という思いに駆られます。
    テストケースを見てみるとNo. 1, 2, 3, 5の4つのケースでFizzBuzzを期待結果とおいているのですが、これがやりすぎなんじゃないかと。
    組み合わせを推測して、そっち目線だと適切だと思うのですが、ここは実際のシステム開発では予算が十分にあり、テストを実行するためのリソース(要員)が足りているという前提であれば、この4ケースは活かしても良いのでしょう。ただ現実的にはギリギリの予算でQA分を積んでいることもあるので、ここは優先度をつけて、例えばNo.5だけ優先度を下げ、時間があればやる、みたいな方法を取っても良いのかもしれません。

    No.6 10でBuzzと表示させる

    ここもNo.5のように、No.4のBuzzと同じ期待値を取ることが見えています。
    一方、まだここでは本コードに「5の倍数の時はBuzzとする」とは書いていないので、リファクタリングの機会が訪れているように思います。

    早速テストコードを。

    だいぶテストコードが長くなってきました。

    赤いバーが出ます。

    本コードを変更してみます。とりあえず5の倍数という形ではなく、10の場合に限定して書いてみます。

    テストを走らせてみると、はい、緑のバーになりました。

    それではここで、リファクタリングをしてみたいと思います。
    赤い枠で囲んだところを、5の倍数という形ですね。

    変更してみます。

    テストを走らせてみます。緑のまま。よかった。

    これで、5の倍数に関するリファクタリングは終わりです。

    No.7 9でFizzと表示させる

    さぁ、今度は初めてFizzと表示させるものです。この辺まで来ると細かな説明は不要ですね。

    テストコード

    実施結果

    本コードを変更

    テスト結果は緑。気持ちが良い!

    Fizzはリファクタリングしなくて良いのかな、と少し悩みます。経験則として「2つ目のFizzが来た時に、合わせてリファクタリングすれば良い」とBuzzの時に覚えました。
    が、残念なことに、テストケースでは2つ目のFizzがありません。
    これでは、9以外の、3の倍数の時にはFizzではなく、その数字をそのまま返してしまうことになります。
    つまりバグ。しかも残念なことに、テストケースからは発見されません。上記の、10個のテストケースしか用意していないから。

    テストケースには無いのですが、試しに、12を入力した時のバーを見てみましょう。

    テストコード

    テストした結果

    赤いバーが出ます。12を入れたら、Fizzではなく12が返ってきてしまっています。
    これでは要件通りの仕様にならず、です。
    しょうがないのでテストケースに追加します。

    No.クラス名ピックアップした数字期待結果
    115, 5, 330FizzBuzz
    215, 545FizzBuzz
    315, 315FizzBuzz
    45, 360 –> 50Buzz
    51575FizzBuzz
    6510Buzz
    739Fizz
    8上記以外7171
    9111
    10100100100
    111212Fizz
    テストケースNo.11を追加した

    No.7からNo.11に飛んでしまいました。が、これは必要なことです。
    前回の記事では、期待結果が同じものは、1つにまとめられるんじゃない?説がありましたが、こんな落とし穴があるとは。QAを数十年やってる身として、身に染みる思いです。

    さて、気を取り直して、No.11を完成させてみたいと思います。まずは緑にしてみましょう。

    本コード

    テストを走らせた結果

    そしてリファクタリング。Fizzとなっている2箇所(上記参照)を、3の倍数に置き換えてみます。

    実際にはここで、本コードは完成ですね。以下、No.8〜No.10を一気に進めてみたいと思います。

    No.8 71で71を表示させる

    テストコード

    テスト実施前の赤いバーを、と思いましたがこちらはすでに本コード内で処理ができるようになっているため、緑のバーは出てきません。いきなり緑。

    No.9 1で1を表示させる

    ここもNo.8と同様、すでに処理ができるようになっています。ただここは境界値として、一応テストコードを作ってみます。

    テストコード

    緑のバー

    つぎはいよいよ最後です。

    No.10 100で100を表示させる

    テストコード

    緑のバー、と思いきや!テストケースにミスがありました。

    100で100を、ではなく、これは5の倍数になるため、Buzzが表示されるということです。

    またしてもテストケースにミスがありました。。カッコ悪い。。100を境界値としてテストをするのであれば、これは100ではなく、Buzzが返ってくるのが正しいですね。

    修正したテストケースはこちら。

    No.クラス名ピックアップした数字期待結果
    115, 5, 330FizzBuzz
    215, 545FizzBuzz
    315, 315FizzBuzz
    45, 360 –> 50Buzz
    51575FizzBuzz
    6510Buzz
    739Fizz
    8上記以外7171
    9111
    10100100100 –> Buzz
    111212Fizz
    テストケースNo.10の期待結果を変更した

    100でBuzzと返ってくるように変更したテストコードはこちら

    最後の、緑のバー。

    なにはともあれ、これで全部のテストケースをクリアすることができました。

    振り返り

    途中、冗長な説明になってしまいましたが、これで全てのテストを実施することができました。
    こうやって見てみると、最初に考えていたテストケースに間違いがあったり、足りない部分があったりして、要件だけを見てみても、抜けるところがあるんだなー、という印象です。
    Fizzを期待値とするテストケースは、2つ以上必要ということも分かりました。これは意外。
    テストの現場にいる身としては、なるべく少ないケース数をこなすように鍛えられているため、今回のように実際の本コードを見ながらテストしてみると、その、なるべく少なく、というところはきちんと確認をして作り上げるべきなんだなという気持ちにさせられました。

    一方、TDDの入り口をこの一連の活動を通して見ることができたのは、とても有意義でした。
    私にJavaのスキルがもっとあれば、もうちょい複雑なプログラムを書くことができるんだろうなと思いつつ、Cucumberを使ってテストコードを書き、それに合わせて本コードに追加、そしてリファクタリングという流れが見えたのは、大きな一歩です。ケント・ベックさんの本を何度か読んではみましたが、ただ読むだけではなく、実際にやってみることで、それが実感できた印象です。
    ここで終わりにするのではなく、もうちょい複雑なJavaのコードを使いつつ、また改めてTDDをやってみたいとおもいます。

    ご参考まで、コードはこちらです

    本コード

    public class FizzBuzz {
    	public String response(int num) {
    		if(num % 15 == 0) {
    			return "FizzBuzz";
    		}
    		else if (num % 5 == 0) {
    			return "Buzz";
    		}
    		else if (num % 3 == 0) {
    			return "Fizz";
    		}
    		else return String.valueOf(num);
    	}
    }

    テストコード

    import static org.junit.jupiter.api.Assertions.*;
    
    import org.junit.jupiter.api.Test;
    
    public class FizzBuzzTest {
    	@Test
    	public void 引数に30を与えたらFizzBuzzを返す() {
    		FizzBuzz fizzbuzz = new FizzBuzz();
    		assertEquals("FizzBuzz", fizzbuzz.response(30));
    	}
    	
    	@Test
    	public void 引数に45を加えたらFizzBuzzを返す() {
    		FizzBuzz fizzbuzz = new FizzBuzz();
    		assertEquals("FizzBuzz", fizzbuzz.response(45));
    	}
    	
    	@Test
    	public void 引数に15を加えたらFizzBuzzを返す() {
    		FizzBuzz fizzbuzz = new FizzBuzz();
    		assertEquals("FizzBuzz", fizzbuzz.response(15));
    	}
    	
    	@Test
    	public void 引数に50を加えたらBuzzを返す() {
    		FizzBuzz fizzbuzz = new FizzBuzz();
    		assertEquals("Buzz", fizzbuzz.response(50));
    	}
    	
    	@Test
    	public void 引数に75を加えたらFizzBuzzを返す() {
    		FizzBuzz fizzbuzz = new FizzBuzz();
    		assertEquals("FizzBuzz", fizzbuzz.response(75));
    	}
    	
    	@Test
    	public void 引数に10を加えたらBuzzを返す() {
    		FizzBuzz fizzbuzz = new FizzBuzz();
    		assertEquals("Buzz", fizzbuzz.response(10));
    	}
    	
    	@Test
    	public void 引数に9を加えたらFizzを返す() {
    		FizzBuzz fizzbuzz = new FizzBuzz();
    		assertEquals("Fizz", fizzbuzz.response(9));
    	}
    	
    	@Test
    	public void 引数に12を加えたらFizzを返す() {
    		FizzBuzz fizzbuzz = new FizzBuzz();
    		assertEquals("Fizz", fizzbuzz.response(12));
    		
    	}
    	
    	@Test
    	public void 引数に71を加えたら71を返す() {
    		FizzBuzz fizzbuzz = new FizzBuzz();
    		assertEquals("71", fizzbuzz.response(71));
    	}
    	
    	@Test
    	public void 引数に1を加えたら1を返す() {
    		FizzBuzz fizzbuzz = new FizzBuzz();
    		assertEquals("1", fizzbuzz.response(1));
    	}
    	
    	@Test
    	public void 引数に100を加えたらBuzzを返す() {
    		FizzBuzz fizzbuzz = new FizzBuzz();
    		assertEquals("Buzz", fizzbuzz.response(100));
    	}
    	
    }
  • JavaでTDDに挑戦してみる(N+1回目

    JavaでTDDに挑戦してみる(N+1回目

    前回の記事はこちら

    参考にさせてもらってるのはこちら


    さて、前回までは、テストケースを作るところまで来ました。
    前回の参照

    No.クラス名ピックアップした数字期待結果
    115, 5, 330FizzBuzz
    215, 545FizzBuzz
    315, 315FizzBuzz
    45, 360Buzz
    51575FizzBuzz
    6510Buzz
    739Fizz
    8上記以外7171
    9111
    10100100100
    FizzBuzz問題の同値分割(1と100を追加、そして期待結果も追加

    さてここで、私はちょっと考えました。

    期待結果が同じものは、省いちゃっても良いんじゃないか説

    どっかで過去に学んだ説

    つまりNo. 1、2、3、5は、一つのケースにまとめてしまっても良いのではないか。
    クラス名のところ、組み合わせが、含まれているからです。

    • No.1は15, 5, 3
    • No.2は15, 5
    • No.3は15, 3
    • No.5は15

    これらが、細かな組み合わせによって出力するという設計になっているのであれば、それぞれ別のケースとして成立させる必要があるような気がしますが、今回はそんな設計にしていない(要件から、3の倍数はFizz、5の倍数はBuzz、15の倍数はFizzBuzzとするだけ)ので、端折っても良いのかなと思います。
    ただ、TDDという観点では、途中でリファクタリングを挟むことが一つのステップになっているので、まずは愚直に上記の10ケースを組んでいきたいと思います。

    No.1 30でFizzBuzzと表示させる

    前回の記事を見るとわかりますが、本プログラムの方では、現状ではまだ「入力した数字をそのまま返す」状態にしかなっていません。

    これで、テストコードのほうを、30を入力したらFizzBuzzと表示させる、のところまで持っていってみたいと思います。

    で、実行した結果がこちら。予想通り失敗しました。30を入力したら、30を出力する、ってプログラムなので当然ですね。

    さて、この赤いバーを、緑に変える必要があります。

    まずはシンプルに、30を入力したら、FizzBuzzを返す、としてみます。

    この上なくシンプルなコード。IF文で、30を入れたらFizzBuzzを返すというコードを追加しました。

    結果はこちら。緑になりました。そりゃそうだ。

    これでNo.1は完了です。

    No.2 45でFizzBuzzと表示させる

    これもNo.1と同様に、45と入れたらFizzBuzzと返すようにするだけですね。

    まずテストコードを先に。

    実行、そしてエラー。ですが、一方で「入れた数字はそのまま返す」という要件のテストができていることを横目に感じつつ。

    では本プログラムの方を修正してみます。No.1と同様に、45をIF文で。

    else if がちょっと増えて、めんどくさいプログラムになってきたなー、と感じます。リファクタリングしたい欲がこうやって増えていくんでしょうか。
    バーはめでたくグリーンに。

    そりゃそうだ。
    まぁ、けど、これでNo.2は終了ですね。

    No.3 15でFizzBuzzと表示させる

    これもまた同様ですね。まず失敗コード、で本プログラムを修正という流れかと思うんですが、さすがにめんどくさくなってきました。ので、ここで、本プログラムの方にリファクタリングをしていきたいと思います。やり方としては、

    1. 現状:30でFizzBuzz、45でFizzBuzzとなっている
    2. リファクタリングの内容:15の倍数を取る場合は、FizzBuzzと表示させる

    こうすることで、本プログラム自体がすっきりするはずです。

    試しに15でFizzBuzzと出力するのを書いてみました。else if が2つ続いてて、しかもreturn がFizzBuzzと同様となっていて、効率的ではないように見えますね。

    まず、テストコードのほうは愚直にやっていく、と宣言した手前、こっちは整理せずに追加すべきだろうと考え、15でFizzBuzzを追加しました。

    そして、本プログラムのほう。
    こちらは、「15で割り切れる場合はFizzBuzzと表示させて」というプログラムにしてみました。

    テストを実行させてみます。見事、緑のバーになりました。

    これで、FizzBuzzと表示させるところは完了したように思います。

  • JavaでTDDに挑戦してみる(n回目

    JavaでTDDに挑戦してみる(n回目

    何度挑戦しているか分からないんですが、相変わらず仕事の切れ目とか、ちょっと落ち着いた時間帯にやったりしています。

    参考にしているのはこちら

    私の現在地

    • Javaはほとんど初心者です。Hello, Java!は書けます
    • テスト駆動開発は、ケントベックさんの本を何度か読みました。そこで写経しつつ、JavaでのTDDを理解しました
    • 使ってるIDEはEclipseです。Javaをなんとか自分のものにしたかったので。
    • QA経験は長いです。キャリア17年とか。テスト駆動開発の良し悪しとか、JSTQBのALTM持ってたりとか。今は事業会社でQAやってます。

    ケントベックさんの本はこちら。JavaとPythonで、TDDについて順を追って書いてあります。プログラムのことがわからなくても、読んでみると新たな発見があるかもしれません。

    テスト駆動開発/KentBeck/和田卓人【3000円以上送料無料】

    価格:3,080円
    (2023/6/2 18:29時点)
    感想(0件)

    どこまで進んでいるか

    上述した、参考にさせてもらってるサイトで、ここまで来ています。
    とりあえず入力値をそのまま返すようプログラムを組み、テストの方は2を入れてそのまま返すところまで。

    FizzBuzz.java
    FizzBuzzTest.java

    次のステップは、3を入れたらFizzと返すところでしょうか。

    FizzBuzz問題のテストを考えてみる

    ちょっとその前に、テストの組み立てを考えてみたいと思います。
    今更ですがFizzBuzz問題の要件はこちら。まだ初心者なので簡略化しています。上述した、参考にさせてもらってるサイトではちゃんとしたものを書いているので、本物を知りたい方はそちらをご参照ください。

    1. 数字は1から100まで。
    2. 3では、Fizzと返す。
    3. 5では、Buzzと返す。
    4. 100では、FizzBuzzと返す。

    さて、テストケースはどうなるか。
    私はここで、同値分割を考えてみたいと思います。同値分割というのは、結果が同じになる範囲をひとまとまり(これを同値クラスと理解しています)にし、そこから適当な値をピックアップするやり方、という理解です。

    • 3の倍数クラス
    • 5の倍数クラス
    • 15の倍数クラス
    • 上記以外

    シンプルに4つ、と言いたいところですが、3、5、15は公倍数があるので(45とか)、そこは15が優先されるというふうに考えると順番が変わるのかなと思います。ここで追加要件として、以下を追加してみたいと思います。

    • 2つ以上の組み合わせで公倍数となる場合は、数字の大きい方を優先する。

    すると、以下のようになります。

    1. 15、5、3の倍数クラス
    2. 15、5の倍数クラス
    3. 15、3の倍数クラス
    4. 5、3の倍数クラス
    5. 15の倍数クラス
    6. 5の倍数クラス
    7. 3の倍数クラス
    8. 上記以外

    さてここで問題が発生しました。
    上記、1と、例えば6では、ピックアップする数字がかぶってしまうことがわかりました。
    例えば、1では15と5と3なので、30を選ぶとしましょう。
    そうすると、6でも、30が選べちゃうわけです。
    こういう時、自分のルールとして、数字の若い方でピックアップされた数字は、選ばないということにしたいと思います。

    さて、それでは数字を選んでみましょう。

    /

    No.クラス名ピックアップした数字
    115, 5, 330
    215, 545
    315, 315
    45, 360
    51575
    6510
    739
    8上記以外71
    FizzBuzz問題の同値分割

    これで十分でしょうか。私の要件であれば、十分なのかもしれないです、が、最初の要件(数字は1から100まで)を忘れていました。
    ここは境界値分析が使えそうです。
    ホントは0と101を加えたいんですが、残念ながら私のJavaスキルでは、どうやってもエラーを期待結果と置くことができなかったので、泣く泣く省略したいと思います。
    なので、シンプルに、1と100を追加したいと思います。

    上記の表に1と100を追加してみます。

    No.クラス名ピックアップした数字期待結果
    115, 5, 330FizzBuzz
    215, 545FizzBuzz
    315, 315FizzBuzz
    45, 360Buzz
    51575FizzBuzz
    6510Buzz
    739Fizz
    8上記以外7171
    9111
    10100100100
    FizzBuzz問題の同値分割(1と100を追加、そして期待結果も追加

    これでようやく、全ケースが揃いました。ほんとは数字とか、全角半角とかもあるかなーと思ったんですが、ここではTDDを体験することが目的なので、上記で十分だろうということにします。

    と思ったんですが、書いてて力尽きました。ここから先はまた改めて。こっからようやくTDDってところなんですけどね。

  • FizzBuzz問題をテスト設計してみる

    FizzBuzz問題をテスト設計してみる

    FizzBuzz問題とは

    FizzBuzz問題は、以下のようなリストになる。めっちゃシンプル。

    • 数字を1から100まで数える。
    • 数字が3の倍数に当たる時は、数字ではなくFizzと返す。
    • 数字が5の倍数に当たる時は、数字ではなくBuzzと返す。
    • 数字が15の倍数に当たる時は、数字ではなくFizzBuzzと返す。
    • 0以下、101以上はエラーを返す。

    テストケースを考えてみる

    数字をテスト条件として与え、期待結果を得るというブラックボックステストを考える時、最小テストケースになるのはどんなパターンになるか。
    私は以下のように考えてみた。が、テストエンジニアとして気になるのは、追加したい箇所として100、-1、Aなど。
    100は正常系、-1やAは例外系として扱われるだろう。

    与えたいテスト条件期待結果
    0エラー
    11
    3Fizz
    5Buzz
    15FizzBuzz
    101エラー
    テスト条件と、期待結果の対照表

    上記の例でいくと、テストケースは6つとなる。
    期待結果で同じ値を取るものとして、エラーが2つ挙げられる。
    これは見方によっては、どちらか一つでも良いかもしれない。
    けれど、境界値を考えた場合、両端に取るべきなので、私は2つにした。
    また、上述したけれど、-1やAなどを追加してみても良いかもしれない。
    それらを追加すると合計で8つになるか。テスト実行時間がそんなにかからないのであれば、テストケースとして加えてみても良いような気がする。

  • JUnitで出力されたテキストを単体テストする

    JUnitで出力されたテキストを単体テストする

    備忘録のために残します。

    作ったファイルは2つ

    1. Code.java: プログラム本体
    2. CodeTest.java: 1をテストするプログラム

    実際のコード

    Code.java

    package com.example.junit;
    
    public class Code {
    	public String sayHello() {
    		return "Hello, World!";
    	}
    }

    CodeTest.java

    package com.example.junit;
    
    import static org.junit.jupiter.api.Assertions.*;
    
    import org.junit.jupiter.api.Test;
    
    public class CodeTest {
    	@Test
    	public void ハローと返す() {
    		Code code = new Code();
    		assertEquals("Hello, World!", code.sayHello());
    	}
    }

    JUnitのテスト結果画面

    POM

    これも未来の自分に向けて、残しておこう。Eclipseで、なんか適当なMavenを使ってプロジェクトを作って、どっかのサイトから引用させてもらったものを使ったりして以下を完成させた。

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.example</groupId>
      <artifactId>junit</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    
      <name>junit</name>
      <!-- FIXME change it to the project's website -->
      <url>http://www.example.com</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>16</maven.compiler.source>
        <maven.compiler.target>16</maven.compiler.target>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>org.junit.jupiter</groupId>
          <artifactId>junit-jupiter</artifactId>
          <version>5.7.2</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    
      <build>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
          <plugins>
            <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
            <plugin>
              <artifactId>maven-clean-plugin</artifactId>
              <version>3.1.0</version>
            </plugin>
            <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
            <plugin>
              <artifactId>maven-resources-plugin</artifactId>
              <version>3.0.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-compiler-plugin</artifactId>
              <version>3.8.0</version>
            </plugin>
            <plugin>
              <artifactId>maven-surefire-plugin</artifactId>
              <version>2.22.1</version>
            </plugin>
            <plugin>
              <artifactId>maven-jar-plugin</artifactId>
              <version>3.0.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-install-plugin</artifactId>
              <version>2.5.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-deploy-plugin</artifactId>
              <version>2.8.2</version>
            </plugin>
            <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
            <plugin>
              <artifactId>maven-site-plugin</artifactId>
              <version>3.7.1</version>
            </plugin>
            <plugin>
              <artifactId>maven-project-info-reports-plugin</artifactId>
              <version>3.0.0</version>
            </plugin>
          </plugins>
        </pluginManagement>
      </build>
    </project>
    
  • Java でTDD(FizzBuzz問題)失敗

    Java でTDD(FizzBuzz問題)失敗

    表記、先日こちらのブログでもお伝えした通り自分のJavaプログラミングスキル自体が足りず、結局意味が分からなくて終わりにしました。

    そこからJava入門の本を買って読んだりして、少しずつJavaのことを理解しようとしています。

    本はこちらを購入しました。めちゃくちゃ分厚くて、まだ半分もいけていません。

    スッキリわかるJava入門第3版 [ 中山清喬 ]

    価格:2,860円
    (2022/11/21 15:02時点)
    感想(13件)

    また、こちらのサイトを参考にしながら、ひたすら写経をしています。ほんとこちらの執筆をされた@tentomさんという方に足を向けて眠れないような感覚。

    とはいえ、こちらのサイトで割愛されているところが私には到底難しくて分からず、けどなんとかしようということで、今頑張っているところです。
    そのあたりを、こちらで書いていこうと思います。

    前提

    • Visual Studio をMacにインストールして、そこで書いています
    • Extensionとして、Test Runner for Javaをインストールしています

    とっかかり

    まずは上記で書かれている、2の引数まで通るコードを書きました。
    で、3は数字そのままではなく、Fizzを返す必要があるんですね。

    FizzBuzzTestのほう。引数に3を与えたらFizzを返すところまで含めています
    FizzBuzzのほう。参考にさせてもらってるブログで、2まで通ってるところそのまま
    そのまま通し、怒られているところ。FizzがExpectedなのに3が返ってきてる!と怒られています

    3でFizzが返ってくるようにFizzbuzz.javaを修正する

    ここで、私の少ない知識をフル稼働させ、修正を試みます。

    参考にさせてもらったサイトから、必要と思われているところを抜き出したもの。

    3だけ条件式を作り、それ以外はそのまま、という流れ、ということなのかな、、半分くらい理解できていません。。

    全部通りました。

    次は5

    3に倣い、5でBuzzが返ってくるようなものを作ってみます。

    5のテストコードを追加したところ
    はい、失敗。そりゃそうだ、Buzzが返ってくるようなコードを書いてないんだから。

    ここで、普通に5を条件分岐で追加すれば良いのですが、

    5の条件分岐を通したもの。
    テストも通りました。

    この辺で一度リファクタリングを

    この辺りで、テストコードの方、毎回インスタンスを立ち上げる式を追加していて、これは冗長だなと思われます。

            FizzBuzz fizzbuzz = new FizzBuzz();

    このコードを毎回追加するのではなく、最初にまとめて書けたらいいなぁと思いながら見てると、はい、元のサイトで以下のように追加しているのに気づきました。こういうふうに、立ち上げ時にやるんすね。

    @Beforeとして最初にインスタンスを作るとこだけ切り出して書く
    残念、、全部通らなくなってしまいました。

    何が悪いのか。おそらく、メソッドを先に追加しなかったからなのかな、と。

    今イマ、こんな感じ。@Beforeから始まるところを追加しています。fizzbuzzのところに赤いアンダーラインがあるということは、ここが悪い、ということですね。。

    参照させてもらってるサイトを参考にし、メソッドを追加することにします。

    赤いアンダーラインが消えた!(まだテスト実行はしていません

    早速テストを流してみます。

    全部通りませんでした。。ううーん。メソッドを追加したのは正解ではなかった模様です。
    良く見ると、
    @Before

    のところに赤いアンダーラインが引いてあるんですよね。

    “Before cannot be resolved to a type”とあります。ううーん。
    カーソルを移動していくと、 @Beforeではなく、@BeforeAllの方が良さそう、みたいな提案を受けました。

    再度流しても失敗。

    また怒られて、長い英文が。。読むの辛い。

    これはもう後で考えることにしよう。。コードを元に戻します。

    気を取り直して15を

    テスト用のコードに追加して、本文には15の倍数に関して何も追加しなかったのに、テストが通ってしまった図。

    これはおかしい。3の倍数、5の倍数のところで先に処理しちゃってるから、ここに引っかかるものがなく通過してしまっているような気がします。

    怪しいので最初に15の倍数を通るように処理を
    すると、失敗しました。FizzBuzzを期待したのに、結果はFizzBuzzFizzBuzzと。
    思わず笑ってしまいました。

    多分、3と5の倍数でもあるから、そのせいなのかなと思ったり、、

    試しに、5の倍数でBuzzっていうのを削除して、トライしてみると、

    はいビンゴ、FizzFizzBuzzが返ってきました。

    うーん。3と5を通らずに、15だけできるものは、、

    答えはelse if

    ぱっと思いつきました。そうじゃん、ifを並列に書いてたからダメで、else if を使って分岐させればいいんじゃん、と思いつきました。

    else if を追加して書いたもの
    テストもこの通り。

    ここまできたら大きな山は超えたかな、という気分です。

    0にトライ

    あとは1未満と、101以上を超えたらダメですよ、を、元のサイトを参考にして追加してみます。

    0のテストコードを追加。
    何も変更していないのでエラー。ちなみに0ではFizzBuzzが返ってくるそうです。

    ちなみに0では、なぜFizzBuzzが返ってくるのか。
    以下、条件分岐のところで、最初に15の倍数で当ててるから、そこに0を入れても0になっちゃう、ということでFizzBuzzが返ってくるのかな、と思います。

    今イマの、FizzBuzzのコード。

    num < 1 の時にエラーを返すコードを追加しました。
    うーん、なんか違うって言われました。。

    本日のおさらい

    • FizzBuzz問題という題材を使って、3の倍数、5の倍数、15の倍数でそれぞれ処理させ、テストして内容が正しいことを学びました。
    • 1未満の時にエラー処理をさせたかったのですが、それがうまくコーディングできず、時間切れとなりました。
    • 101以上についても時間が足りず、作成していません。
    • 参照させてもらってるコードで、StringBuilderというパーツ?を使っていて、その内容をきちんと理解せずに使っています。多分、数字を入力する時に使うものなんだと思ってます。
  • ChromeをJavaで起動するコード

    ChromeをJavaで起動するコード

    テスト駆動開発をやってみたくて、Javaを勉強しています。

    テスト対象はWebブラウザを想定しているので、はじめの一歩ということでChromeの起動をすることができたので備忘録を兼ねてコードを。もともとのコードはこちらのサイトを参考にさせていただきました。

    import java.awt.Desktop;
    import java.net.URI;
    
    public class ChromeLaunch {
    	public static void main(String[] args) {
    		String uriString = "https://www.google.com";
    		Desktop desktop = Desktop.getDesktop();
    		try{
    			URI uri = new URI (uriString);
    			desktop.browse(uri);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }

    めっちゃシンプル。GoogleのURLのところを適当なものに変えれば、そのサイトを開くことができます。

    上のコードで、私がまだわかってないもの

    • URIの意味。大文字なので、なのかの略語なのかなと思ったりしている
    • catch (Exception e) {
      e.printStackTrace();
      }
      ここで何をしているのか分かっていない。参照させていただいたコードをそのまま写経しただけ。

    私が無知ということを理解しているのは良いですね。そこを学習することで、成長できるということだと思うので。

  • かけ算九九をJavaで

    かけ算九九をJavaで

    こちらの本を買い、Javaを勉強しています。
    ようやく、ちょっといろんなプログラムが書けるようになってきたので、面白くなってきました。
    ちょっと高かったけど、丁寧に教えてくれるし、買って良かったなぁーと思っています。

    スッキリわかるJava入門第3版 [ 中山清喬 ]

    価格:2,860円
    (2022/11/10 17:51時点)
    感想(13件)

    洗濯バサミと大きめのコーヒーカップでページをなんとか留めている状態。抑えるやつ欲しいなぁ。100均でなにかアイデアを使ってできないものだろうか。

    と思ったらあった!ダイソーに行きたくなってきました。
    こちらのブロガーさんが紹介されている。

    さて、表記のプログラム。これはforを使った繰り返しの勉強の一環として登場しています。
    パパっと写経をするくらいならすぐに書けてしまうのですが、じっくり、動きを確認しながら書いてみました。いやー面白い。
    以下、プログラムを載せますが著作権的にNGでしたらご連絡ください、すぐに削除いたします。

    public class Kuku {
    	public static void main(String[] args) {
    		for (int i = 1; i < 10; i++) {
    			for (int j = 1; j < 10; j++) {
    				System.out.print(i * j);
    				System.out.print (" ");
    			}
    			System.out.println( "");
    		}
    	}
    }

    外側で1の時に、内側で1、2、3、と増やしていくんですね、で上限は <10 としているので、9まで。
    次は外側で2、内側でまた1、2、3、と。これを9*9まで繰り返すという感じ。

    こういう時に必要となる、ロジカルシンキング

    プログラミング教育というんでしょうか、子供たちの学習要綱に入る入らないみたいな議論を耳にしたことがありますが、大切なのはプログラミングスキルではなく、上述したような形で物事を考えることなように思います。

    一時期興味があり、ロジカルシンキングをする際に有用となるマインドマップ、フローチャート、逆にラテラルシンキングなど読んだんですが、そういうのって本を読んで学習するというのもありっちゃありなんですが、

    例えば自動販売機の中ってどうなってるんだろう、って考えるだけでも良いような気がするんすよね。
    お金ってどういう風に流れていくんだろう、とか。キャッシュレスで決済する時の、決済システムの流れ、とか。Paypayとか、リアルタイムに出金されるじゃないすか、とかね。

  • テスト駆動開発 6日目(完結)

    テスト駆動開発 6日目(完結)

    挫折しました。

    こちらのサイトを参考にして進めていたのですが、1と2を返すところまではこの内容通りにできました。
    が、その後が省略されていて、私の力では順を追ってコーディングしていくところが全く分からず、とりあえず写経して終わり。。
    しかもテストが通らず、うーん、ってなって諦めました。私の力不足です。

    失敗しまくってる

    まずはJavaだな、話はそこからだ、うん