4

このプログラムをテストする必要がある場合、どちらの方法を使用するのが良いでしょうか。
まず、ユーザーに と の入力をaskUserPathAndWord()求めます。2 つのスレッドがあります。 pathwhatFind

  • 最初のスレッドはフォルダーをスキャンし、読み取り可能なファイルが見つかった場合put()はキューに入れます。
  • 2 番目のスレッドtake()がキューから取得しwhatFind、このファイルを検索します。検索が成功すると、このファイルのパスと単語の頻度がコンソールに出力されます。

これは、マルチスレッド作業との統合依存です。EasyMock の Junit である、このプログラムをより適切にテストできるバリアントはどれですか? EasyMock に関するチュートリアルをいくつか読みましたが、どのような場合に使用するのがよいかわかりません。

コード:

class FolderScan implements Runnable {

    private String path;
    private BlockingQueue<File> queue;
    private CountDownLatch latch;
    private File endOfWorkFile;

    FolderScan(String path, BlockingQueue<File> queue, CountDownLatch latch,
            File endOfWorkFile) {
        this.path = path;
        this.queue = queue;
        this.latch = latch;
        this.endOfWorkFile = endOfWorkFile;
    }

    public FolderScan() {
    }

    @Override
    public void run() {
        findFiles(path);
        queue.add(endOfWorkFile);
        latch.countDown();
    }

    private void findFiles(String path) {

        try {
            File root = new File(path);
            File[] list = root.listFiles();
            for (File currentFile : list) {
                String s = currentFile.getName().toLowerCase();
                if (currentFile.isDirectory()) {
                    findFiles(currentFile.getAbsolutePath());
                } else {
                    if (s.matches("^.*?\\.(txt|pdf|doc|docx|html|htm|xml|djvu|rar|rtf)$")) {
                        queue.put(currentFile);
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

public class FileScan implements Runnable {

    private String whatFind;
    private BlockingQueue<File> queue;
    private CountDownLatch latch;
    private File endOfWorkFile;

    public FileScan(String whatFind, BlockingQueue<File> queue,
            CountDownLatch latch, File endOfWorkFile) {
        this.whatFind = whatFind;
        this.queue = queue;
        this.latch = latch;
        this.endOfWorkFile = endOfWorkFile;
    }

    public FileScan() {
    }

    @Override
    public void run() {

        while (true) {
            try {
                File file;
                file = queue.take();

                if (file == endOfWorkFile) {
                    break;
                }

                scan(file);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        latch.countDown();
    }

    private void scan(File file) {
        Scanner scanner = null;
        int matches = 0;

        try {
            scanner = new Scanner(file);
        } catch (FileNotFoundException e) {
            System.out.println("File Not Found.");
            e.printStackTrace();
        }

        while (scanner.hasNext())
            if (scanner.next().equals(whatFind)) {
                matches++;
            }

        if (matches > 0) {
            String myStr = String.format(
                    "File: %s - and the number of matches " + "is: %d",
                    file.getAbsolutePath(), matches);
            System.out.println(myStr);
        }
    }

    public void askUserPathAndWord() {

        BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(System.in));
        String path;
        String whatFind;
        BlockingQueue<File> queue = new LinkedBlockingQueue<File>();

        try {
            System.out.println("Please, enter a Path and Word"
                    + "(which you want to find):");
            System.out.println("Please enter a Path:");
            path = bufferedReader.readLine();
            System.out.println("Please enter a Word:");
            whatFind = bufferedReader.readLine();

            if (path != null && whatFind != null) {

                File endOfWorkFile = new File("GameOver.tmp");
                CountDownLatch latch = new CountDownLatch(2);

                FolderScan folderScan = new FolderScan(path, queue, latch,
                        endOfWorkFile);
                FileScan fileScan = new FileScan(whatFind, queue, latch,
                        endOfWorkFile);

                Executor executor = Executors.newCachedThreadPool();
                executor.execute(folderScan);
                executor.execute(fileScan);

                latch.await();
                System.out.println("Thank you!");
            } else {
                System.out.println("You did not enter anything");
            }

        } catch (IOException | RuntimeException e) {
            System.out.println("Wrong input!");
            e.printStackTrace();
        } catch (InterruptedException e) {
            System.out.println("Interrupted.");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {  
        new FileScan().askUserPathAndWord();
    }
}

質問:

  • この場合、どの種類のテストが最もよくカバーされますか?
  • 両方のバリアントで契約上の義務をテストするにはどうすればよいでしょうか?
  • 答えがEasyMockである場合、これを正しく行うにはどうすればよいでしょうか?
  • の場合Junit、どのようにvoidメソッドをテストしますか?
4

2 に答える 2

2

JUnitまたはEasyMock?-答えは、両方です!1つは単体テストフレームワークであり、もう1つはオブジェクトのモックを可能にするため、より優れたテストを作成できます。したがって、JUnit任意のモックフレームワークを使用することは素晴らしいアイデアです(あなたが提案するEasyMock、私が使用するMockitoか、3番目のフレームワークがあります)。jMock

どの種類のテストが最適なカバレッジになりますか?-モックを使用すると、単体テストするコードの領域に焦点を合わせることができるため、以前よりも多くのコードをテストすることになります。重い操作(データベースへの書き込みやファイルシステムからの読み取りなど)を行うコードをモックすることは特に便利です。したがって、JUnitでEasyMockを使用すると、JUnit単独よりも優れたカバレッジ(および優れたテスト)が得られるはずです。

両方のバリアントで契約上の義務をテストする方がよいでしょうか。-すべてのパブリックメソッドがテストされていることを確認してください。次に、各テストの最後に、期待を徹底的に主張して検証します。

答えがEasyMockの場合、これを正しく行うにはどうすればよいですか?-verifyメソッドを使用して、テスト中にモックオブジェクトが正しく呼び出されたことを確認します。

Junitの場合、voidメソッドをどのようにテストしますか?-結果が期待どおりであることを他の方法で主張する必要があります。おそらく、voidメソッドにパラメーターとしてオブジェクトを渡したか、結果を取得する別の方法(getterなど)があります。

幸運を!

于 2013-02-24T17:43:10.760 に答える
2

このプログラムのテストについて考えるときは、単体テストとは何かを念頭に置いてください。ウィキペディアによると、 「単体テストは、ソース コードの個々の単位をテストして、使用に適しているかどうかを判断する方法です」。

また、マルチスレッド プログラムのテストは難しいため、少し深いところまで踏み込んでいることに注意してください。可能な限り、機能のテストからスレッドの相互作用の複雑さを取り除く必要があります。

あなたのプログラムを見ると、既にかなり良いカプセル化と懸念の分離が行われているので、順調に進んでいます。*Scanここでの私の戦略は、スレッドとは別に両方のオブジェクトをテストすることです。そのためには、彼らの役割が何であるかを頭に入れておきます。私は次のように言います:

  • FolderScanディレクトリ構造をたどって特定のタイプのファイルを見つけ、フィルターを通過したファイルをキューに入れます。ディレクトリ構造を使い果たすと、特定のファイルをキューに入れ、ラッチをカウントダウンして終了します。
  • FileScanファイルのキューを消費し、それらに対して操作を行い、出力をコンソールに出力します。特定のファイルにヒットすると、ラッチをカウントダウンして終了します。

おそらく多かれ少なかれ動作するコードがすでにあるので、(コードを書きながらコードを書くのではなく) テストを後付けするときに、テストに合格するためにソースの変更をできるだけ少なくしたいと考えています。 . その後、コードをリファクタリングしたいと思うかもしれませんが、テストによって自信を持ってそうすることができます。

FolderScan テスト

最初のステップとして、 の新しい JUnit テストを作成しますFolderScan。いくつかのテストを書くことができますが、大まかに言えば、それらのそれぞれはディレクトリ構造にいくつかのファイルを設定する必要があります。少なくとも、これらの各ケースをテストします。

  1. フィルターを通過するファイルを含む単一のフォルダー。
  2. フィルターを通過しないファイルを含む単一のフォルダー。
  3. それぞれにファイルがあるネストされたフォルダー。

テストが細かくなるほど、より良い結果が得られます。各テストは単に の新しいインスタンスを作成しFolderScan、指定されたフォルダーを指すように定義した引数を与えます。run() を呼び出して、次のことを確認します。

  1. キューには、File期待どおりのオブジェクトが含まれています。
  2. CountDownLatch減少しました。
  3. キューの最後の要素は 'trigger'Fileです。

ファイルスキャン テスト

大まかに言えば、これに対するテストは明確になっているはずです。いくつかのテキストFileオブジェクトを作成し、それらとマーカーを使用してキューに入力し、それらを新しい FileScan オブジェクトに渡します。繰り返しになりますが、粒度が細かいほど良いので、少なくとも次のようになります。

  1. 文字列が一致するファイル
  2. 一致する文字列がないファイル
  3. 複数のファイル

ただし、このクラスには問題があります。これは、古典的な「シングルトンをテストする方法」の問題です。このオブジェクトの結果は、実際には にパイプされSystem.outます。結果を確認するには、これに何らかの方法で接続する必要があります。最初のパスとして、コンストラクターをリファクタリングして を渡すことをお勧めしPrintStreamます。本番環境では を渡しSystem.out、テストでは確認できるものを渡します:new PrintStream(new ByteArrayOutputStream()) の内容を確認できるもの、またはさらに良いのはモックです。

最後に、各テストで以下を確認する必要があります。

  1. キューは空です。
  2. CountDownLatch減少しました
  3. 指定されPrintStreamた には、期待される内容が書き込まれています。

FileScanこれで、と の両方がFolderScan広告どおりに機能することに大きな自信が持てるはずです。

* askUserPathAndWord のテスト *

この関数/クラスを記述どおりにテストする簡単な方法がわかりません。単一責任の原則に違反しており、単純にやりすぎです。次の責任を新しいメソッドまたはクラスに抽出します。

  • ユーザーに単語とパスを尋ねる
  • 単語をFileScan指定して、正しいパラメータ (ファクトリ) を作成します。
  • パスをFolderScan指定して、正しいパラメーター (ファクトリー) を作成します。
  • 2 つのランナブルとラッチを指定して、 を作成しExecutorService、それらをキューに入れ、ラッチを待機します。

その後、それらを個別にテストできます。

* 次のステップ *

これらのテストを持つことの良い点の 1 つは、一度テストを行うと、自由にリファクタリングでき、問題が発生していないことを確信できることです。たとえば、Callableの代わりに を調べることができます。これにより、参照Runnableを処理しFuture、 から出力パラメータFolderScanを削除し、 をCountDownLatch完全に削除できます。

ハッピーテスト!

于 2013-02-26T15:55:59.610 に答える