it.f-frontier.com Open in urlscan Pro
183.181.83.32  Public Scan

Submitted URL: https://t.co/Ioy3EHof4h
Effective URL: https://it.f-frontier.com/2021/11/18/103264/
Submission: On November 18 via api from US — Scanned from DE

Form analysis 3 forms found in the DOM

GET https://it.f-frontier.com/

<form role="search" method="get" class="search-form" action="https://it.f-frontier.com/"><input type="search" name="s" placeholder="Search here..." value""=""><button class="button-search" type="submit"><i class="cb cb-search"></i></button></form>

GET https://it.f-frontier.com/

<form role="search" method="get" class="search-form" action="https://it.f-frontier.com/"><input type="search" name="s" placeholder="Search here..." value""=""><button class="button-search" type="submit"><i class="cb cb-search"></i></button></form>

<form class="gsc-search-box gsc-search-box-tools" accept-charset="utf-8">
  <table cellspacing="0" cellpadding="0" class="gsc-search-box">
    <tbody>
      <tr>
        <td class="gsc-input">
          <div class="gsc-input-box" id="gsc-iw-id1">
            <table cellspacing="0" cellpadding="0" id="gs_id50" class="gstl_50 gsc-input" style="width: 100%; padding: 0px;">
              <tbody>
                <tr>
                  <td id="gs_tti50" class="gsib_a"><input autocomplete="off" type="text" size="10" class="gsc-input" name="search" title="検索" id="gsc-i-id1" dir="ltr" spellcheck="false"
                      style="width: 100%; padding: 0px; border: none; margin: 0px; height: auto; background: url(&quot;https://www.google.com/cse/static/images/1x/ja/branding.png&quot;) left center no-repeat rgb(255, 255, 255); outline: none;"></td>
                  <td class="gsib_b">
                    <div class="gsst_b" id="gs_st50" dir="ltr"><a class="gsst_a" href="javascript:void(0)" title="検索ボックスをクリア" role="button" style="display: none;"><span class="gscb_a" id="gs_cb50" aria-hidden="true">×</span></a></div>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        </td>
        <td class="gsc-search-button"><button class="gsc-search-button gsc-search-button-v2"><svg width="13" height="13" viewBox="0 0 13 13">
              <title>検索</title>
              <path
                d="m4.8495 7.8226c0.82666 0 1.5262-0.29146 2.0985-0.87438 0.57232-0.58292 0.86378-1.2877 0.87438-2.1144 0.010599-0.82666-0.28086-1.5262-0.87438-2.0985-0.59352-0.57232-1.293-0.86378-2.0985-0.87438-0.8055-0.010599-1.5103 0.28086-2.1144 0.87438-0.60414 0.59352-0.8956 1.293-0.87438 2.0985 0.021197 0.8055 0.31266 1.5103 0.87438 2.1144 0.56172 0.60414 1.2665 0.8956 2.1144 0.87438zm4.4695 0.2115 3.681 3.6819-1.259 1.284-3.6817-3.7 0.0019784-0.69479-0.090043-0.098846c-0.87973 0.76087-1.92 1.1413-3.1207 1.1413-1.3553 0-2.5025-0.46363-3.4417-1.3909s-1.4088-2.0686-1.4088-3.4239c0-1.3553 0.4696-2.4966 1.4088-3.4239 0.9392-0.92727 2.0864-1.3969 3.4417-1.4088 1.3553-0.011889 2.4906 0.45771 3.406 1.4088 0.9154 0.95107 1.379 2.0924 1.3909 3.4239 0 1.2126-0.38043 2.2588-1.1413 3.1385l0.098834 0.090049z">
              </path>
            </svg></button></td>
        <td class="gsc-clear-button">
          <div class="gsc-clear-button" title="結果をクリア">&nbsp;</div>
        </td>
      </tr>
    </tbody>
  </table>
</form>

Text Content

Skip to content
ITちゃんねる

仕事に役立つIT業界の最新ニュース





WINDOWS 10でC言語開発をしよう! テストコードを追加する

 * On: 2021/11/18
   

●

テストコードを考える

前回は、開発しているソフトウェアにテストコードを追加する例を紹介した。テストコードは自分の身を守ることになる大切なものだ。機能の追加やバグの修正によって、バグが生まれてしまうのはソフトウェア開発にはよくあることだが、テストコードがあればこうした問題を検出し、リリース前に修正できる可能性が高まるのだ。

前回は、Visual Studio Codeを使ってテストコードを追加する方法を取り上げた。テスト用のPowerShellスクリプトを作成し、make
testで実行できるようにし、最後にVisual Studio
Codeからテストを実行できるようにした。テストコードを取り込む方法としてはかなりシンプルなものだ。



しかし、実際にはテストコードが単一ということはほとんどない。テストコードはすべてのケースを網羅していることが理想的だ。やり方はいろいろあるが、前回のやり方を踏襲していくなら、テストごとにスクリプトを作成して追加することになる。どれだけ網羅的なテストコードを用意できるかが、どれだけ将来の自分も助けることになるかにつながっていく。

テストコードは網羅的に

テストコードは同じようなものをたくさん用意しても意味がない。できるだけすべてのケースを網羅するようにテストコードを追加していく。どこまで用意するかはケースバイケースだが、特に見逃しがちなケースを重点的にテストコードに落とし込んでおくとよいだろう。

csv2tsvの場合、変換前のCSVファイルと変換後のTSVファイルを用意しておけばテストコードを作成できる。まず、テストコードの元となるデータを用意する。最初はよくある一般的なCSVファイルだ。

zip.csv

13101,100,1000000,トウキョウト,チヨダク,イカニケイサイガナイバアイ

13101,102,1020072,トウキョウト,チヨダク,イイダバシ

13101,102,1020082,トウキョウト,チヨダク,イチバンチョウ

13101,101,1010032,トウキョウト,チヨダク,イワモトチョウ

13101,101,1010047,トウキョウト,チヨダク,ウチカンダ

13101,100,1000011,トウキョウト,チヨダク,ウチサイワイチョウ

13101,100,1000004,トウキョウト,チヨダク,オオテマチ(ツギノビルヲノゾク)

13101,100,1006890,トウキョウト,チヨダク,オオテマチジェイエイビル(チカイ・カイソウフメイ)

13101,100,1006801,トウキョウト,チヨダク,オオテマチジェイエイビル(1カイ)

13101,100,1006802,トウキョウト,チヨダク,オオテマチジェイエイビル(2カイ)

zip.tsv

13101 100 1000000 トウキョウト チヨダク イカニケイサイガナイバアイ

13101 102 1020072 トウキョウト チヨダク イイダバシ

13101 102 1020082 トウキョウト チヨダク イチバンチョウ

13101 101 1010032 トウキョウト チヨダク イワモトチョウ

13101 101 1010047 トウキョウト チヨダク ウチカンダ

13101 100 1000011 トウキョウト チヨダク ウチサイワイチョウ

13101 100 1000004 トウキョウト チヨダク オオテマチ(ツギノビルヲノゾク)

13101 100 1006890 トウキョウト チヨダク オオテマチジェイエイビル(チカイ・カイソウフメイ)

13101 100 1006801 トウキョウト チヨダク オオテマチジェイエイビル(1カイ)

13101 100 1006802 トウキョウト チヨダク オオテマチジェイエイビル(2カイ)



次に、問題となりそうなケースを用意していく。最初はダブルクォーテーションの処理だ。CSVファイルではダブルクォーテーションがクォートの対象となるため、その扱いに注意する必要がある。そういったケースをテストデータとして先に用意しておく。仕様に則って機能した場合の適切な出力をあらかじめ用意しておくのだ。

doublequotes.csv

13101,”1″”0″”0″,,100 0000

“13101”,”””102″””,,102 0072

13101,”1″”02″,,102 0082

13101,”101″””””,, 10 10032

13101,”””””101″,, 101 0047

,13101,@,”1″”0″”0″,

,”13101″,”””102″””,@

,13101,”1″”02″,

@,13101,”101″””””,

,13101,”””””101″,

doublequotes.tsv

13101 1″0″0 100 0000

13101 “102” 102 0072

13101 1″02 102 0082

13101 101″” 10 10032

13101 “”101 101 0047

13101 @ 1″0″0

13101 “102” @

13101 1″02

@ 13101 101″”

13101 “”101

ファイルが空、つまりゼロバイトだった場合の処理もレアケースになる。そのときにも適切に動作してもらう必要があるので、次のように空のファイルを用意して動作確認を行うようにする。

empty.csv

empty.tsv

フィールドが空のケースや、フィールドの前後が空白である場合なども処理を間違えがちだ。この辺りもテストデータとして用意しよう。

spaces.csv

,,,100 0000

,,,102 0072

,,,102 0082

,,, 10 10032

,,, 101 0047

,,,,

,,,

,,,

,,,

,,,

spaces.tsv

100 0000

102 0072

102 0082

10 10032

101 0047

どこまでやるかは用途にも依るが、たとえばフィールド数が1000とか10000といった極端に多いケース、1行あたりのデータサイズがきわめて大きなケース、CSVデータが適切なフォーマットではなかったときの動作など、やろうと思えばどこまでもテストケースを用意することができる。どこまで用意するかはバランス感覚が要求されるところだが、仕様の整理とテストコードの開発を行ったり来たりしながら丁寧に作業してみよう。

●

複数のテストスクリプトと、それをまとめるスクリプト

用意したテストデータを使ってテストを行うPowerShellスクリプトを用意する。先程用意した4種類のデータは次のように4つのスクリプトで動作を確認できる。

test001.ps1

$n = ‘001’

$d = ‘zip’

$f = New-TemporaryFile

.csv2tsv.exe .data${d}.csv > $f

C:WindowsSystem32fc.exe .data${d}.tsv $f > $null

if ($?) {

echo “テスト${n}: 成功”

}

else {

echo “テスト${n}: 失敗”

}

Remove-Item $f

test002.ps1

$n = ‘002’

$d = ‘doublequotes’

$f = New-TemporaryFile

.csv2tsv.exe .data${d}.csv > $f

C:WindowsSystem32fc.exe .data${d}.tsv $f > $null

if ($?) {

echo “テスト${n}: 成功”

}

else {

echo “テスト${n}: 失敗”

}

Remove-Item $f

test003.ps1

$n = ‘003’

$d = ‘spaces’

$f = New-TemporaryFile

.csv2tsv.exe .data${d}.csv > $f

C:WindowsSystem32fc.exe .data${d}.tsv $f > $null

if ($?) {

echo “テスト${n}: 成功”

}

else {

echo “テスト${n}: 失敗”

}

Remove-Item $f

test004.ps1

$n = ‘004’

$d = ‘empty’

$f = New-TemporaryFile

.csv2tsv.exe .data${d}.csv > $f

C:WindowsSystem32fc.exe .data${d}.tsv $f > $null

if ($?) {

echo “テスト${n}: 成功”

}

else {

echo “テスト${n}: 失敗”

}

Remove-Item $f

さて、問題はここからだ。Makefileから直接この4つのテストスクリプトを呼び出す仕組みにしたとしよう。そうすると、例えばテストコードが100個など大量になってきたときに、Makefileに100行のテストコード呼び出し処理が記述されることになる。あまり見通しのよい書き方とは言えない。

そこで、ほかのテストスクリプトを呼び出してすべてのテストを実施するスクリプトを用意し、Makefileからはそのスクリプトのみを実行するといった仕組みにする。次のようなスクリプトを用意して、ほかのテストスクリプトを実行させる。

test.ps1

##########################################################################

# テスト数

##########################################################################

$TESTS = 4

##########################################################################

# テスト実施

##########################################################################

$res = $true

1..$TESTS |

% {

$n = $_.ToString(“000”)

Invoke-Expression ./tests/test${n}.ps1

if (-Not $?) {

$res = $false

}

}

##########################################################################

# テスト結果出力

##########################################################################

if ($res) {

echo ‘全テストをパス’

}

else {

echo ‘テスト失敗’

exit -1

}

PowerShellスクリプト自体の説明はここでは行わないが、上記のスクリプトはtestsディレクトリ以下のテストスクリプトを順番に実行し、1つでも失敗がればこのスクリプトの終了コードも失敗として処理する内容になっている。

●

ファイルとディレクトリの配置構造

CSVファイルをTSVに変換するだけのコマンドを作っているわけだが、テストスクリプトやテストデータも含めるとファイルやディレクトリの配置が少々複雑になってくる。現在は次のような状態になっている。

csv2tsv – ファイルとディレクトリの配置構造

C:.

│ LICENSE

│ main.c

│ main.h

│ Makefile

│ util_csv.c

│ util_file.c

│

├───.vscode

│ launch.json

│ tasks.json

│

├───data

│ doublequotes.csv

│ doublequotes.tsv

│ empty.csv

│ empty.tsv

│ spaces.csv

│ spaces.tsv

│ zip.csv

│ zip.tsv

│

└───tests

test.ps1

test001.ps1

test002.ps1

test003.ps1

test004.ps1

Makefileへのマージ

今回の書き換えをMakefileにも反映させると次のようになる。

今回の開発を反映させたMakefile

CMD= csv2tsv.exe

SRCS= $(wildcard *.c)

OBJS= $(SRCS:.c=.o)

CC= clang

CFLAGS+=-g

build: $(CMD)

$(CMD): $(OBJS)

$(CC) $(CFLAGS) -o $(CMD) $(OBJS)

.c.o:

$(CC) -c $

test: $(CMD)

pwsh .teststest.ps1

clean:

rm -f *.exe

rm -f *.o

rm -f *.ilk

rm -f *.pdb

「make test」と実行すると次のような結果を得ることができる。

テストコードの実行

PS C:UsersdaichiDocumentsvscode-csv2tsv> make test

clang -c main.c -o main.o

clang -c util_csv.c -o util_csv.o

clang -c util_file.c -o util_file.o

clang -g -o csv2tsv.exe main.o util_csv.o util_file.o

pwsh .teststest.ps1

テスト001: 成功

テスト002: 成功

テスト003: 成功

テスト004: 成功

全テストをパス

PS C:UsersdaichiDocumentsvscode-csv2tsv>

開発を進めつつ、時々テストコードを実行して互換性が維持できているかを確認する。この部分を充実させておくことで、将来、楽になれる。実際に使っていて問題が見つかった場合は、それもテストコードとして作成して追加することで、将来のエンバグを減らす可能性を高めることができる。

テストフレームワークへ

このようにテストコードが増えていくと、いくつもの共通点が出てきて、整理していくことでいずれフレームワークのような状態になっていく。自分でフレームワークとして整理してもよいし、そうなってきたら似たような既存のテストフレームワークを探して置き換えるのも手ではないかと思う。

最初からテスト系のフレームワークを使ってもよいのだが、シンプルなものであれば自分で用意すればよく、そのほうが場合によっては見通しがよく移植性も高くなる。フレームワークを自作してもよいだろう。いずれテストフレームワークも取り上げるつもりだ。

●

付録:csv2tsvソースコードほか

tasks.json

{

“version”: “2.0.0”,

“tasks”: [

{

“label”: “Clang”,

“type”: “process”,

“command”: “make”,

“args”: [

“build”

],

“problemMatcher”: [],

“group”: {

“kind”: “build”,

“isDefault”: true

}

},

{

“label”: “Test”,

“type”: “process”,

“command”: “make”,

“args”: [

“test”

],

“problemMatcher”: [],

“group”: {

“kind”: “build”,

“isDefault”: true

}

}

]

}

Makefile

CMD= csv2tsv.exe

SRCS= $(wildcard *.c)

OBJS= $(SRCS:.c=.o)

CC= clang

CFLAGS+=-g

build: $(CMD)

$(CMD): $(OBJS)

$(CC) $(CFLAGS) -o $(CMD) $(OBJS)

.c.o:

$(CC) -c $

test: $(CMD)

pwsh .teststest.ps1

clean:

rm -f *.exe

rm -f *.o

rm -f *.ilk

rm -f *.pdb

main.h

int csv2tsv(const char *, int, char *, int);

char *file2str(const char *)

main.c

#include

#include

#include

#include “main.h”

int main(int argc, char *argv[]) {

char *csvdata, *tsvdata;

int csvdata_bytes, tsvdata_bytes;

csvdata = file2str(argv[1]);

csvdata_bytes = strlen(csvdata);

tsvdata_bytes = csvdata_bytes;

tsvdata = calloc(tsvdata_bytes + 1, sizeof(char));

csv2tsv(csvdata, csvdata_bytes, tsvdata, tsvdata_bytes);

printf(“%s”, tsvdata);

return 0;

}

util_csv.c

#include

static bool record_outputed;

static char gettsvchar(const char);

int csv2tsv(const char *ibuf, int ibufsize, char *obuf, int obufsize) {

// When the target is empty, no processing is done.

if (0 == ibufsize)

return 0;

const char *p_i, *end_i;

char *p_o;

int tsv_len = 0;

p_i = ibuf;

end_i = &ibuf[ibufsize – 1];

p_o = obuf;

// Indicates the state during parsing.

typedef enum FIELD_STATUS {

FIELD_END,

IN_FIELD,

IN_QUOTED_FIELD

} record_status;

record_status rs = FIELD_END;

record_outputed = false;

while (1) {

if (‘n’ == *p_i) {

if (!record_outputed) {

// nothing

}

rs = FIELD_END;

*p_o = gettsvchar(‘n’);

++p_o;

++tsv_len;

} else {

switch (rs) {

case FIELD_END:

if (‘,’ == *p_i) {

// nothing

} else if (‘”‘ == *p_i) {

rs = IN_QUOTED_FIELD;

} else {

rs = IN_FIELD;

*p_o = gettsvchar(*p_i);

++p_o;

++tsv_len;

}

break;

case IN_FIELD:

if (‘,’ == *p_i) {

rs = FIELD_END;

} else {

*p_o = gettsvchar(*p_i);

++p_o;

++tsv_len;

}

break;

case IN_QUOTED_FIELD:

if (‘”‘ == *p_i) {

if (p_i == end_i) {

rs = FIELD_END;

} else if (‘,’ == *(p_i + 1)) {

rs = FIELD_END;

++p_i;

} else if (‘”‘ == *(p_i + 1)) {

*p_o = gettsvchar(*p_i);

++p_o;

++tsv_len;

++p_i;

}

} else {

*p_o = gettsvchar(*p_i);

++p_o;

++tsv_len;

}

break;

}

switch (rs) {

case FIELD_END:

*p_o = ‘t’;

++p_o;

++tsv_len;

record_outputed = false;

break;

case IN_FIELD:

case IN_QUOTED_FIELD:

break;

}

}

if (p_i == end_i || tsv_len == obufsize)

break;

else

++p_i;

}

return tsv_len;

}

static char gettsvchar(const char c) {

record_outputed = true;

if (‘t’ == c) {

return ‘ ‘;

} else {

return c;

}

}

util_file.c

#include

#include

#include

char *file2str(const char *filepath) {

struct stat st;

int filesize, c;

char *buf, *p;

FILE *fp;

stat(filepath, &st);

filesize = st.st_size;

buf = calloc(filesize + 1, sizeof(char));

p = buf;

fp = fopen(filepath, “r”);

for (int i = 0; i

c = fgetc(fp);

if (EOF == c) {

break;

}

*p = (char)c;

++p;

}

return buf;

}

○参考

RFC4180 – Common Format and MIME Type for Comma-Separated Values (CSV) Files

Definition of tab-separated-values (tsv), Internet Assigned Numbers Authority

GNU make

--------------------------------------------------------------------------------

(Windows 10でC言語開発をしよう! テストコードを追加する)

LineTwitterFacebookHatenaEmail


こんな記事も読まれています

探索的テストのデメリットを克服するには? 対策事例を紹介

本記事では、ソフトウェアテスト専門会社 バルテス株式会社のフィリピン子会社 VALTES ADVAN…

テストデコレーション実装のVISUAL STUDIO CODE用拡張機能「JAVA EXTENSION PACK」

Microsoftは現地時間2021年8月9日、Visual Studio Code用拡張機能「Ja…

VISUAL STUDIOがWINDOWS 11のMICROSOFT STOREからインストール可能に

TheWindowsClubは8月27日(現地時間)、「Visual Studio finally …


投稿ナビゲーション

Prev Post
Next Post

LineTwitterFacebookHatenaEmail




注目ニュース

 * 未明にGoogle Cloudがダウン、SpotifyやDiscordなどに影響。「Apex鯖落ち」がトレンドに
 * ゼロからはじめるExcel VBA+Webサービス 第2回 通勤経路から運賃を自動で取得してみよう【駅すぱあとWebサービス】
 * 「入社難易度ランキング2021」 2位のBCGを抑えた堂々の1位は?
 * ソニーが「アイボの七五三」を神田明神で開催 「二礼二拍手一礼」を覚えたaiboも公開、境内はオーナー達で賑わう
 * ピクセラがHDD不要のクラウド録画に対応したワイヤレステレビチューナー発表 PC・スマホ・タブレットで視聴可能な「Xit
   AirBox」新モデルとテレビに接続して使える「Xit Base」
 * 「イカゲーム」大ヒットの裏で「共感できない」声続出のワケ
 * 装飾に本物のティラノサウルスの歯を使ったカスタムメイドiPhone「Tyrannophone」 価格は8610ドル(約98万円)
 * コストコで家族型ロボット「LOVOT」と触れ合える!幕張倉庫店と川崎倉庫店にLOVOTポップアップストアが登場!購入特典も用意
 * 【速報】「動く実物大ガンダム」のコクピットをリアル体験 ソフトバンク5GのVRドーム「GUNDAM Pilot View」を見逃すな
 * 入院中の子どもたちが分身ロボット「OriHime」で外の世界を体験 最終日にはリモート交流 アクサ損害保険が実施


新着ニュース

 * 心身の健康と幸せのために、もっと実践した方がいい4つのこと
 * 『551 蓬莱』の創業者 羅 邦強の孫が監修!「羅家 東京豚饅」がオープンへ
 * 腕時計の芸術作品。立体構造を鑑賞して楽しむトゥールビヨン「GRANTS」
 * スマホ中毒から抜け出す5つのアプローチ
 * iPhone購入時、AppleCare+に加入しないほうがいい?
 * 高さ約62cm「レゴ スター・ウォーズ AT-AT」ホスの戦いを完全再現!ルークなど9体のミニフィグとアクセサリー付き
 * Sonosのコンパクトなサウンドバー「Sonos Beam(Gen 2)」が発売 スカパー!のサブスク「PLUSY」での年内提供も発表
 * iPad miniと組み合わせて使いたい。防水防塵仕様&iOSショートカットキーを装備したロジクールのBluetoothキーボード
 * Windows 10でC言語開発をしよう! テストコードを追加する
 * アトラックラボ、深谷市農家の主導で「キャベツ搬送ロボット」を開発 200kg積載 ジョイスティック操作も可能




過去のITニュース

 * 2021年11月 (671)
 * 2021年10月 (1062)
 * 2021年9月 (1056)
 * 2021年8月 (983)
 * 2021年7月 (966)
 * 2021年6月 (1016)


今月のITニュース

2021年11月 月 火 水 木 金 土 日 1234567 891011121314 15161718192021 22232425262728 2930  

« 10月    
×

検索
 

カスタム検索





(c)ITちゃんねる All rights reserved. Cream Blog by Themebeez

✓
共有ありがとうございます !
AddToAny
詳細…