技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

ループ処理のテスト観点

ループ処理を単体テストする時の小ネタです。
ループ処理を最初から最後まで全件チェックするのは大変なので、以下のように誤りが生じやすい箇所に絞ってテストを行うと効率良くテストを行うことができます。
(ループはN回行うものとします)
 
・ループの1回目をチェックする
 ループの開始時点を誤るバグはよくあるバグなので、それを発見する
 
・ループのN回目をチェックする
 ループの終了条件を誤るバグもよくあるバグなので、それを発見する
 
・ループの2~N-1回目の中から1回分をサンプリングしてチェックする
 前レコードの情報が現レコードの読み込みに影響を与えていないことを確認する
 
----
 
例えば、以下のCSVファイルについて、全件読み込んだ後、ヘッダレコード(1レコード目)とトレーラレコード(最終レコード)を除いて2項目目を取得する処理があるとします。
 
・C:\tmp\test.csv
1,20210602
2,0000001,ほげ商品,100
2,0000002,ふが商品,250
2,0000003,ぴよ商品,1000
9,3
 
これをjavaで実装する場合、正しい実装は以下です。
(実行結果はソースコード中に記載しています)
 
・FileReadTest.java
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class FileReadTest {

  public static void main(String args) {

    // 1バイトずつ読込
    BufferedReader br = null;

    // 読み込んだレコードをメモリ(配列)に保持
    ArrayList<String> list = new ArrayList<>();

    try {
      // 入出力ファイルパス
      br = new BufferedReader
        (new InputStreamReader
        (new FileInputStream("C:\\tmp\\test.csv"),"MS932"));

      // データ読み込みループ
      while (true){

        // 1行読み込み
        String str = br.readLine();
        if (str == null) {
          break;
        }

        // メモリに保持
        list.add(str);

      }

    // 例外処理
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        br.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    // ループ処理で中間レコードの2項目目を取得
    for (int i = 1; i < list.size() - 1; i++) {
      System.out.println(list.get(i).split(",")[1]);
    }

    // 結果
    /*
    0000001
    0000002
    0000003
    */

  }
}
 
----
 
上記の例において、ループの開始時点のバグ、ループの終了時点のバグ、ループ途中のバグが埋め込まれると、以下のようなコード・処理結果になります。
 
・ループの開始時点のバグ
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class FileReadTest {

  public static void main(String args) {

    // 1バイトずつ読込
    BufferedReader br = null;

    // 読み込んだレコードをメモリ(配列)に保持
    ArrayList<String> list = new ArrayList<>();

    try {
      // 入出力ファイルパス
      br = new BufferedReader
        (new InputStreamReader
        (new FileInputStream("C:\\tmp\\test.csv"),"MS932"));

      // データ読み込みループ
      while (true){

        // 1行読み込み
        String str = br.readLine();
        if (str == null) {
          break;
        }

        // メモリに保持
        list.add(str);

      }

    // 例外処理
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        br.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    // ループ処理で中間レコードの2項目目を取得
    // 1レコード目除外忘れ
    for (int i = 0; i < list.size() - 1; i++) {
      System.out.println(list.get(i).split(",")[1]);
    }

    // 結果
    /*
    20210602
    0000001
    0000002
    0000003
    */

  }
}
 
・ループの終了時点のバグ
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class FileReadTest {

  public static void main(String args) {

    // 1バイトずつ読込
    BufferedReader br = null;

    // 読み込んだレコードをメモリ(配列)に保持
    ArrayList<String> list = new ArrayList<>();

    try {
      // 入出力ファイルパス
      br = new BufferedReader
        (new InputStreamReader
        (new FileInputStream("C:\\tmp\\test.csv"),"MS932"));

      // データ読み込みループ
      while (true){

        // 1行読み込み
        String str = br.readLine();
        if (str == null) {
          break;
        }

        // メモリに保持
        list.add(str);

      }

    // 例外処理
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        br.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    // ループ処理で中間レコードの2項目目を取得
    // 最終レコード除外ミス
    for (int i = 1; i <= list.size() - 1; i++) {
      System.out.println(list.get(i).split(",")[1]);
    }

    // 結果
    /*
    0000001
    0000002
    0000003
    3
    */

  }
}
 
・ループ途中のバグ
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class FileReadTest {

  public static void main(String args) {

    // 1バイトずつ読込
    BufferedReader br = null;

    // 読み込んだレコードをメモリ(配列)に保持
    ArrayList<String> list = new ArrayList<>();

    try {
      // 入出力ファイルパス
      br = new BufferedReader
        (new InputStreamReader
        (new FileInputStream("C:\\tmp\\test.csv"),"MS932"));

      // データ読み込みループ
      while (true){

        // 1行読み込み
        String str = br.readLine();
        if (str == null) {
          break;
        }

        // メモリに保持
        list.add(str);

      }

    // 例外処理
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        br.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    // ループ処理で中間レコードの2項目目を取得
    for (int i = 1; i < list.size() - 1; i++) {
      // 次レコード取得ミス
      System.out.println(list.get(1).split(",")[1]);
    }

    // 結果
    /*
    0000001
    0000001
    0000001
    */

  }
}