Test automation

はじめに 前回,前々回とテストについて書いてきました。前回の最後では,Makefileを使ってテスト用のコマンドを作ることができました。 ところで,このmakeというコマンド,どこかで見覚えがないでしょうか… …はい,実はこのブログを構築している静的サイトジェネレータのコマンドhugoに似ています。Hugoについては,こちらの記事を読んでいただけると嬉しいです。 それでですね,上の記事で説明したように,このサイトはGithub Actionsを使うことで,自動的にビルドされています。この仕組みによって,毎回わざわざhugoコマンドを手元で打ってビルドする必要がなくなったのでした。 では,同じことがC言語プログラムのテストについてもできるのではないでしょうか? …えー,できます! 準備 Github Actionsをテスト用の環境として使いたいので,まずはこれまで作ってきたディレクトリをGithub に push しましょう。前回までの成果物はこんな感じでした。 $> tree . ├── Makefile ├── README.md ├── include │ └── my_abs.h ├── src │ └── my_abs.c └── tests └── test.c このディレクトリをGithubにpushします。以下のような感じになりました。 YAMLファイル Github Actions を使うためには,どういうイベントが起きたら,どういうジョブをするかといったルールを記述するファイルが必要になります。このファイルはYAMLという形式で記述されている必要があります。 というわけで,YAMLファイルが必要になるのですが,便利なことに,基本的な形が整えられたファイルをGithub上でゲットすることができます。やった〜。 まずは,Actionsタブをクリックします。 すると,ページが遷移して,Suggested for this repositoryに,C/C++ with Make という設定が確認できます。 Configureというタブをクリックします。 おー!これが欲しかったのよ〜。 Start commit というタブをクリックして,このYAMLファイルをプロジェクトに追加しましょう。 そして,ローカルに変更を反映するために,git pullコマンドを打っておきましょう。リポジトリは以下のような構成になりました。 c-cpp.yml の編集 現在のc-cpp.ymlファイルには,configureというよくわからないフィールドが含まれているので,思い切って今回これは消しちゃうことにします。問題が起きたら,もう一度戻って考えることにしましょう。 また,makeにも現在のMakefileに記述されていないルールを使っているものがあるので,これらは削除して,代わりにmake testを追加します。jobの名前に関しては,今回走らせたい処理はビルドではなく,テストなので,ここも書き換えることにします。最終的に以下のようなYAMLファイルになりました。 name: C/C++ CI on: push: branches: [ "main" ] pull_request: branches: [ "main" ] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: test run: make test では,mainブランチにpushしてみましょう。Githubのページを覗いてみましょう。...

December 19, 2022

Separation of concerns

はじめに 前回の記事では,テストファーストのやり方で関数を開発するということをしました。結果として以下のようなコードが出来上がりました。 #include <stdbool.h> #include <stdio.h> #include <limits.h> int my_abs(int x) { if (INT_MIN) return (-1); if (x < 0) return (-x); else return (x); } void test(void) { bool result = (my_abs(-100) == 100) && (my_abs(100) == 100); bool special = (my_abs(INT_MIN) == -1) && \ (my_abs(INT_MAX) == INT_MAX) && \ (my_abs(0) == 0); if (result) puts("ok"); else puts("ko"); } int main(void) { test(); } しかし,前回のコードにはちょっとした問題の芽が潜んでいます。それは,テストの対象となるコードと,テスト用のコードが同一のファイルに含まれているという点です。今のやり方でコードを管理していると,テスト対象となるコードが,テスト用のコードを呼び出すといった意図しない呼び出し関係が生じてしまうかもしれません。 関心ごとの分離 前回の記事に書いたように,ソフトウェアはそれぞれが固有の働きをする複数のパーツからなると考えることができます。この時非常に重要なのは,各パーツは一つの仕事を担当するように設計する,ということです。各パーツの仕事を明確にして管理しなければ,先ほど確認したようなコード間での意図しない結合が起きてしまったり,どのファイルにどの関数が含まれるのかわからなくなってしまうからです。このような設計指針で設計されたソフトウェアのパーツを単一責務であると言います。ところで,ソフトウェアのパーツのことは一般にモジュールと呼ばれるので,今後は我々もモジュールという言葉を使うことにしましょう。 テストと開発コードはそれぞれ異なる仕事をする関数です。従って,これらのコードを含むモジュールが単一責務を満たすようにするためには,これらは異なるモジュールに分離する必要があります。このように単一責務を満たすために,モジュールを管理しやすい単位に分割することを関心ごとの分離,と言います。今回はmy_abs()と,test()を違うファイルに分割すれば良いことになります。それぞれ,my_abs.c,test.cというファイルを作って分離しましょう。...

December 15, 2022

Test first

はじめに いろいろあって,かっこいいブログを作ることができたので,早速なんか書いていこうと思います。今回はテストについてです。 テストファースト ソフトウェア開発において,プログラマはソフトウェアの部品となるデータ構造や関数を一つづつ作っていくという作業をします。この時,関数やデータ構造にはそれぞれ期待される役割を持っています。例えば,int abs(int x)という関数は,絶対値を返すという機能を持っていることを期待されます。このような期待される役割を機能要件と呼ぶこともあります。 機能要件が定まっていたとしても,それだけでプログラムを書いていくのは難しいことが多いと思います。ソフトウェアの期待される機能を実現するには,それを複数の部品に分割していく必要もあるでしょう。このような作業はソフトウェアの設計に含まれます。 ソフトウェアの設計が終わって,分割された一つの関数を実装するという段階になってもまだどのように実装すればいいかわからないということがあります 。このような場合にはどうしたら良いでしょうか。 一つの方法として,まずテストを書くということから始めることができます。関数が果たすべき役割がある程度明確になっていれば,その関数が果たす仕事の典型的なケースについては列挙することができるはずだからです。これができないということは,関数の典型的な振る舞いさえも明らかではない,ということであり,もう一度設計の段階に戻って考えることになります。例えば,abs(x)で言うと以下のようなテストを書くことができます。 #include <stdbool.h> #include <stdio.h> int my_abs(int x); void test(void) { bool result = (my_abs(-100) == 100) // -100の絶対値は100と期待される if (result) puts("ok"); else puts("ko"); } int main(void) { test(); } では,このコードが書かれたファイルをtest.cという名前で保存して,コンパイルしてみましょう。結果は以下のようになります。 あらまぁ,コンパイルできてないみたいですね。なんででしょうか… はい,答えは簡単で,my_absという関数がまだないからですね。test.cにmy_absの実装を追加しましょう。こんな感じでしょうかね。 int my_abs(int x) { return(-x); } コンパイルして,実行してみます。 わーい,okだ! okじゃない しかし,absの典型的な場合とは他にもあります。入力が正の整数の時には,absは,その数をそのまま返さなくてはなりません。この振る舞いについてもテストしましょう。 #include <stdbool.h> #include <stdio.h> int my_abs(int x) { return (-x); } void test(void) { bool result = (my_abs(-100) == 100) && (my_abs(100) == 100); if (result) puts("ok"); else puts("ko"); } int main(void) { test(); } 結果は…...

December 6, 2022

How to build your blog

こんにちは!42tokyo Advent Calendar 2022の14日目を担当する, knittaです。よろしくお願いします。 はじめに 突然ですが,皆さんこういうことを思ったことがないでしょうか。 「あー,なんか有無を言わさずみんなに褒められて,気分良くなりたいなぁ…」 こんなときは,みんなと同じことをやっていてはいけない,ということで私は42Tokyo というところの入学試験(Piscine)を受けて見事合格しました。それから僕は友達を集めて,「ねぇみんな聞いてー!僕,42Tokyoに合格したよ〜〜〜」と言いました。すると,みんなこう言いました。 「なにそれ〜」 この様な結果から次の2点の事実が観察できます。一つは 「42Tokyo の知名度は低く,その価値は社会から認識されていない」 という点。 もう一つは, 「みんなと違うことをやったからと言って,それだけで誉められる訳ではない」 という点です。 今回のテーマ 以上の観察から私はこう思いました。 「42Tokyo の価値をみんなに知ってもらって,かつ,みんなに褒められた〜い」 そのためには, 42Tokyo がどの様な場所で,私がどの様な人間であるかを知ってもらうための情報を発信する方法が必要になると思います。また,なんかかっこいいやり方でそれを実現するのがよいのではないでしょうか。カッコいいほうが自慢できるし,誉められそうなので😄 静的サイトジェネレータ + Github Pages = cool 検索エンジンを駆使して情報を集めていった結果, 静的サイトジェネレータ Github Pages というものを使えば,カッコいいサイトを作って,Github で公開することができそうだぞ,ということがわかりました。これを使って42Tokyoのことや,私が今やっていることについて,coolに情報を発信することができるのではないかな? では,早速やってみましょう! … とその前に一応これらの用語について用語を整理しておきます。 静的サイトジェネレータ … Markdown などの記法で書かれたテキストファイルから,静的なWebページを生成するソフトウェアである Github Pages … 静的サイトのホスティングサービスであり,これを使って静的サイトジェネレータを使って作った Webサイトを運用することができる。 実際にやってみる では,実際に静的サイトジェネレータを使って,Webサイトを作ってから,Github Pages にデプロイしてみましょう。今回私が使う静的サイトジェネレータは,Hugoというものです。 私の環境(M1 Mac)では,Homebrew というパッケージマネージャを使って,Hugo をシステムに導入することができました。 $> brew install hugo これだけで,Hugo を使って静的サイトを作ることができます。まずは,サイトの雛形を作りましょう。以下のようなコマンドを打って実行するだけでいい。 $> hugo new site <サイト名> <サイト名>で指定してディレクトリが新しくできるので,このディレクトリに移動して,tree コマンドを打ってみると以下の様な構成になっていることがわかリます。...

November 30, 2022