テスト駆動開発を知って実際にやってみたくなった。
先日、「テスト駆動開発(TDD/Test Driven Development)」というプラクティスを知りました。そして、以下の記事を書きました。
実際にやってみたくなったので、c言語の課題で実践してみました。見様見真似でやっているので、本来のTDDとは違ったり、もっと工夫や改善できる点がありましたら教えてください。
実際にやってみた。
1.「次の目標を考える」
課題自体は、著作権やその他諸々公開できない理由があるので、To Doリストに落とした形から始めます。
– [ ] get_next_line関数を繰り返し呼び出すと、fdで指定されたテキストファイルを1行ずつ読み取る
– [ ] ファイルを1行ずつ読み込んで、その読み取った行を終端に\n文字を含めて返す。
– [ ] \n文字で終わっていない場合、返された行には、終端の\n文字を含まないようにする
– [ ] ファイルの終わりに達した場合、返された行には、終端の\n文字を含まないようにする
– [ ] ファイルの終わりに達した場合、NULLを返す
– [ ] エラーが発生した場合、NULLを返す
– [ ] 読み取るものが何もない場合、NULLを返す
– [ ] ファイルの終わりに達した場合、返された行には、終端の\n文字を含まないようにする
– [ ] \n文字で終わっていない場合、返された行には、終端の\n文字を含まないようにする
– [ ] 標準入力でも読み取れるようにする
– [ ] BUFFER_SIZEの値が9999、10000000、1の場合に機能するようにする
– [ ] バイナリファイルを読み取るときの、未定義の動作を実装する
– [ ] read()がファイルの末尾までいっていない最後の呼び出し以降に、fdが指すファイルが変更された場合の、未定義の動作を実装する
– [ ] できるだけ少ない量を読み取るようにする(ファイル全体を読み取ってから各行を処理しない)
– [ ] -D BUFFER_SIZEフラグを使用して、コンパイルできるようにする
2.「その目標を示すテストを書く」
– [ ] get_next_line関数を繰り返し呼び出すと、fdで指定されたテキストファイルを1行ずつ読み取る
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 42
void test_read_file(void)
{
int fd;
char *answer[] = {"1234567890\n", NULL};
char *line;
int i;
i = 0;
fd = open("test.txt", O_RDONLY);
if (fd == -1)
{
perror("open error");
return ;
}
while (answer[i] != NULL)
{
line = get_next_line(fd);
assert(strcmp(line, answer[i]) == 0);
i++;
}
close(fd);
return ;
}
int main(void)
{
test_read_file();
return (0);
}
3.「そのテストを実行して失敗させる」
char *get_next_line(int fd)
{
(void)fd;
return (0);
}
=> % ./a.out
zsh: segmentation fault ./a.out
作成したテストが失敗して、「RED」。
4.「目的のコードを書く」
char *get_next_line(int fd)
{
(void)fd;
return ("1234567890\n");
}
5.「2で書いたテストを成功させる」
これで成功はするが、get_next_line関数ではreadを行っていない。
readした文字列をmallocで確保した領域に格納して返したい。
6.「テストが通るままでリファクタリングを行う」
—test.txt————
1234567890
aaaaaaaaaa
————————-
#define BUFFER_SIZE 11
char *get_next_line(int fd)
{
char *line;
ssize_t bytes_read;
line = (char *)malloc(sizeof(char) * (BUFFER_SIZE + 1));
if (line == NULL)
return (NULL);
bytes_read = read(fd, line, BUFFER_SIZE);
if (bytes_read == -1)
{
free(line);
return (NULL);
}
line[bytes_read] = '\0';
return (line);
}
void test_read_file(void)
{
int fd;
char *answer[] = {"1234567890\n", "aaaaaaaaaa\n", NULL};
char *line;
int i;
i = 0;
fd = open("test.txt", O_RDONLY);
if (fd == -1)
{
perror("open error");
return ;
}
while (answer[i] != NULL)
{
line = get_next_line(fd);
assert(strcmp(line, answer[i]) == 0);
free(line);
i++;
}
close(fd);
return ;
}
7.「1〜6を繰り返す」
上記までで、以下の二つができた(はず)。
– [x] get_next_line関数を繰り返し呼び出すと、fdで指定されたテキストファイルを1行ずつ読み取る
– [x] ファイルを1行ずつ読み込んで、その読み取った行を終端に\n文字を含めて返す。
1の「次の目標を考える」は、「スキルだから練習しないとできるようにならない。練習すればできるようになる。」とt_wadaさんがおっしゃっていたが、実際にやってみるとやはり難しい。一行読み取ったことをテストするということは、テスト容易性が低い(テストすることが難しい)のかもしれない。タスクに分解する過程も、重要度やテスト容易性を軸に最初のタスクを選ぶことも、どちらも難しい。
以降、進捗次第、追記していきます。
次にやることは、「1行ずつ読み取る、つまり、BUFFER_SIZEに依らず改行文字までを読みとる」こと。(上記二つのタスクは正確には終わっていなかった…。)