Skip to content

第 1 章: TODO リストと最初のテスト

1.1 はじめに

プログラムを作成するにあたって、まず何をすればよいでしょうか?私たちは、仕様を確認して TODO リスト を作るところから始めます。

TODO リスト

何をテストすべきだろうか——着手する前に、必要になりそうなテストをリストに書き出しておこう。

— テスト駆動開発

1.2 仕様の確認

今回取り組む FizzBuzz 問題の仕様は以下の通りです。

1 から 100 までの数をプリントするプログラムを書け。
ただし 3 の倍数のときは数の代わりに「Fizz」と、5 の倍数のときは「Buzz」とプリントし、
3 と 5 両方の倍数の場合には「FizzBuzz」とプリントすること。

この仕様をそのままプログラムに落とし込むには少しサイズが大きいですね。最初の作業は仕様を TODO リスト に分解する作業から着手しましょう。

1.3 TODO リストの作成

仕様を分解して TODO リストを作成します。

TODO リスト:

  • 数を文字列にして返す
  • 1 を渡したら文字列 "1" を返す
  • 3 の倍数のときは数の代わりに「Fizz」と返す
  • 5 の倍数のときは「Buzz」と返す
  • 3 と 5 両方の倍数の場合には「FizzBuzz」と返す
  • 1 から 100 までの数
  • プリントする

まず「1 を渡したら文字列 "1" を返す」という、最も小さなタスクから取り掛かります。

1.4 テスティングフレームワークの導入

テストファースト

最初にプログラムする対象を決めたので、早速プロダクトコードを実装……ではなく テストファースト で作業を進めましょう。

テストファースト

いつテストを書くべきだろうか——それはテスト対象のコードを書く前だ。

— テスト駆動開発

今回 F# のテスティングフレームワークには xUnit を利用します。xUnit は .NET エコシステムで広く使われているテスティングフレームワークです。

開発環境のセットアップ

dotnet CLI で F# プロジェクトを作成します。

# Nix 環境に入る
$ nix develop .#dotnet

# ライブラリプロジェクトの作成
$ dotnet new classlib -lang F# -n FizzBuzzFSharp
$ cd FizzBuzzFSharp

# テストプロジェクトの作成
$ cd ..
$ dotnet new xunit -lang F# -n FizzBuzzFSharpTest
$ cd FizzBuzzFSharpTest
$ dotnet add reference ../FizzBuzzFSharp/FizzBuzzFSharp.fsproj

F# のテストは xUnit の [<Fact>] アトリビュートを付けた関数として記述します。関数名にはダブルバッククォートで囲むことで日本語を使用できます。

環境確認テスト

環境が正しく設定されていることを確認するため、学習用テストを書きます。

// Tests.fs
module Tests

open Xunit

[<Fact>]
let ``整数を文字列へ変換できる`` () =
    Assert.Equal("42", string 42)

テストを実行します。

$ dotnet test
  合計: 1、成功: 1、失敗: 0、スキップ: 0

テストが通りました。F# の string 関数が整数を文字列に変換できることを確認しました。let バインディングで関数を定義し、Assert.Equal で期待値と実際の値を比較します。

1.5 仮実装

テスト環境の準備ができたので、TODO リストの最初の作業に取り掛かりましょう。

TODO リスト:

  • 数を文字列にして返す
  • 1 を渡したら文字列 "1" を返す

まずはアサーションを最初に書きましょう。

アサートファースト

いつアサーションを書くべきだろうか——最初に書こう。

— テスト駆動開発

Red: 最初のテスト

FizzBuzz のテストを書きます。

// Tests.fs
module Tests

open Xunit
open FizzBuzzFSharp.FizzBuzz

[<Fact>]
let ``数を文字列にして返す_1を渡したら文字列1を返す`` () =
    Assert.Equal("1", generate 1)

テストを実行します。

$ dotnet test
error FS0039: 値またはコンストラクター 'generate' が定義されていません。

generate 関数がまだ定義されていないため、コンパイルエラーになります。これは期待通りの Red フェーズです。

Green: 仮実装

テストを通すために 仮実装 から始めます。

仮実装を経て本実装へ

失敗するテストを書いてから、最初に行う実装はどのようなものだろうか——ベタ書きの値を返そう。

— テスト駆動開発

generate 関数を定義して、文字列リテラルを返します。

// Library.fs
namespace FizzBuzzFSharp

module FizzBuzz =

    let generate (number: int) : string =
        "1"

テストを実行します。

$ dotnet test
  合計: 1、成功: 1、失敗: 0、スキップ: 0

テストが通りました。F# では let バインディングで関数を定義します。module キーワードでモジュールを定義し、その中に関数をまとめます。型注釈 (number: int) : string は省略可能ですが、最初は明示することで意図を明確にします。

TODO リスト:

  • 数を文字列にして返す
  • 1 を渡したら文字列 "1" を返す
  • 3 の倍数のときは数の代わりに「Fizz」と返す
  • 5 の倍数のときは「Buzz」と返す
  • 3 と 5 両方の倍数の場合には「FizzBuzz」と返す
  • 1 から 100 までの数
  • プリントする

ここまでの作業をバージョン管理システムにコミットしておきましょう。

$ git add .
$ git commit -m 'test: 数を文字列にして返す'

1.6 まとめ

この章では以下のことを学びました。

  • TODO リスト で仕様をプログラミング対象に分解する方法
  • テストファースト で最初にテストを書く考え方
  • F# プロジェクトのセットアップ(dotnet CLI、xUnit)
  • 仮実装 でベタ書きの値を返してテストを通す手法
  • F# の let バインディングによる関数定義
  • F# の module によるコードの構造化
  • ダブルバッククォートによるテスト関数名への日本語使用

次章では、2 つ目のテストケースを追加して 三角測量 を行い、プログラムを一般化していきます。