Javaでリスト実装をTDDする方法

1概要

このチュートリアルでは、テスト駆動開発(TDD)プロセスを使用したカスタムの List 実装について説明します。

これはTDDの紹介ではありません。そのため、TDDの基本的な考え方と、それを改善するための継続的な関心があることをすでに理解しているものとします。

簡単に言えば、 TDDは設計ツールであり、テスト を利用して実装を推進することを可能にします。

簡単な免責事項 - ここでは効率的な実装を作成することに焦点を当てているのではなく、TDDの慣例を表示する言い訳としてそれを使用するだけです。

2入門

まず、クラスのスケルトンを定義しましょう。

public class CustomList<E> implements List<E> {
    private Object[]internal = {};
   //empty implementation methods
}

CustomList クラスは List インタフェースを実装しているため、そのインタフェースで宣言されているすべてのメソッドの実装を含んでいる必要があります。

始めるために、これらのメソッドに空のボディを提供するだけです。メソッドに戻り値の型がある場合、 Object には null boolean には false など、その型の任意の値を返すことができます。

簡潔にするために、よく使用されない必須のメソッドとともに、オプションのメソッドは省略します。

3 TDDサイクル

TDDを使用して実装を開発するということは、最初にテストケースを作成する** 必要があることを意味し、それによって実装の要件を定義します。

これらのテストに合格するには、実装コードを作成または修正するだけです。

非常に単純化された方法では、各サイクルの3つの主なステップは次のとおりです。

  1. 筆記テスト - テスト形式で要件を定義する

  2. 機能の実装 - 集中することなくテストに合格する

コードの優雅さに 。 リファクタリング - 読みやすくするためにコードを改善し、

まだテストに合格している間維持する

最も単純なものから始めて、 List インターフェースのいくつかのメソッドについて、これらのTDDサイクルを見ていきます。

4 isEmpty メソッド

isEmpty メソッドは、おそらく List インタフェースで定義されている最も簡単なメソッドです。これが最初の実装です。

@Override
public boolean isEmpty() {
    return false;
}

この初期メソッド定義はコンパイルするのに十分です。このメソッドの本体は、ますます多くのテストが追加されたときに改善することを "強制"されます。

4.1. 最初のサイクル

リストに要素が含まれていないときに isEmpty メソッドが true を返すことを確認する最初のテストケースを書きましょう。

@Test
public void givenEmptyList__whenIsEmpty__thenTrueIsReturned() {
    List<Object> list = new CustomList<>();

    assertTrue(list.isEmpty());
}

isEmpty メソッドは常に false を返すため、指定されたテストは失敗します。

戻り値を反転させるだけで成功させることができます。

@Override
public boolean isEmpty() {
    return true;
}

4.2. セカンドサイクル

リストが空でないときに isEmpty メソッドが false を返すことを確認するには、少なくとも1つの要素を追加する必要があります。

@Test
public void givenNonEmptyList__whenIsEmpty__thenFalseIsReturned() {
    List<Object> list = new CustomList<>();
    list.add(null);

    assertFalse(list.isEmpty());
}

add メソッドの実装が必要になりました。これは、まず add メソッドです。

@Override
public boolean add(E element) {
    return false;
}

このメソッドの実装は、リストの内部データ構造に変更が加えられていないので機能しません。追加した要素を保存するように更新しましょう。

@Override
public boolean add(E element) {
    internal = new Object[]{ element };
    return false;
}

isEmpty メソッドは拡張されていないため、テストは失敗します。

それをしましょう:

@Override
public boolean isEmpty() {
    if (internal.length != 0) {
        return false;
    } else {
        return true;
    }
}

この時点で、空でないテストに合格します。

4.3. リファクタリング

これまで見てきた両方のテストケースは合格ですが、 isEmpty メソッドのコードはもっと洗練されたものになるでしょう。

リファクタリングしましょう:

@Override
public boolean isEmpty() {
    return internal.length == 0;
}

テストが成功したことがわかりますので、 isEmpty メソッドの実装はこれで完了です。

5 size メソッド

これは、 CustomList クラスがコンパイルできるようにするための size メソッドの最初の実装です。

@Override
public int size() {
    return 0;
}

5.1. 最初のサイクル

既存の add メソッドを使用して、 size メソッドの最初のテストを作成し、単一の要素を持つリストのサイズが 1 であることを確認します。

@Test
public void givenListWithAnElement__whenSize__thenOneIsReturned() {
    List<Object> list = new CustomList<>();
    list.add(null);

    assertEquals(1, list.size());
}

size メソッドが 0 を返すため、テストは失敗します。新しい実装で合格させましょう。

@Override
public int size() {
    if (isEmpty()) {
        return 0;
    } else {
        return internal.length;
    }
}

5.2. リファクタリング

よりエレガントにするために size メソッドをリファクタリングできます。

@Override
public int size() {
    return internal.length;
}

このメソッドの実装はこれで完了です。

6. get メソッド

これが get の最初の実装です。

@Override
public E get(int index) {
    return null;
}

6.1. 最初のサイクル

このメソッドの最初のテストを見てみましょう。リスト内の単一要素の値が検証されます。

@Test
public void givenListWithAnElement__whenGet__thenThatElementIsReturned() {
    List<Object> list = new CustomList<>();
    list.add("baeldung");
    Object element = list.get(0);

    assertEquals("baeldung", element);
}

テストはこの get メソッドの実装に合格します。

@Override
public E get(int index) {
    return (E) internal[0];
}

6.2. 改善

通常、 get メソッドをさらに改良する前に、テストを追加します。それらのテストは適切なアサーションを実装するために List インターフェースの他のメソッドを必要とするでしょう。

しかし、これらの他のメソッドはまだ十分に成熟していないので、TDDサイクルを中断し、 get メソッドの完全な実装を作成します。これは実際にはそれほど難しいことではありません。

get は、 index パラメータを使用して、指定された位置にある internal 配列から要素を抽出する必要があることを想像するのは簡単です。

@Override
public E get(int index) {
    return (E) internal[index];
}

7. add メソッド

これは、セクション4で作成した add メソッドです。

@Override
public boolean add(E element) {
    internal = new Object[]{ element };
    return false;
}

7.1. 最初のサイクル

以下は add の戻り値を検証する簡単なテストです。

@Test
public void givenEmptyList__whenElementIsAdded__thenGetReturnsThatElement() {
    List<Object> list = new CustomList<>();
    boolean succeeded = list.add(null);

    assertTrue(succeeded);
}

テストに合格するには、 add メソッドを変更して true を返す必要があります。

@Override
public boolean add(E element) {
    internal = new Object[]{ element };
    return true;
}

テストは成功しましたが、 add メソッドはまだすべてのケースを網羅していません。

リストに2番目の要素を追加すると、既存の要素は失われます。

7.2. セカンドサイクル

これは、リストに複数の要素を含めることができるという要件を追加した別のテストです。

@Test
public void givenListWithAnElement__whenAnotherIsAdded__thenGetReturnsBoth() {
    List<Object> list = new CustomList<>();
    list.add("baeldung");
    list.add(".com");
    Object element1 = list.get(0);
    Object element2 = list.get(1);

    assertEquals("baeldung", element1);
    assertEquals(".com", element2);
}

現在の形式の add メソッドでは複数の要素を追加できないため、テストは失敗します。

実装コードを変更しましょう。

@Override
public boolean add(E element) {
    Object[]temp = Arrays.copyOf(internal, internal.length + 1);
    temp[internal.length]= element;
    internal = temp;
    return true;
}

実装は十分洗練されているので、リファクタリングする必要はありません。

8結論

このチュートリアルでは、カスタムのList実装の一部を作成するためのテスト駆動開発プロセスについて説明しました。 TDDを使用すると、テストカバレッジを非常に高いレベルに保ちながら、段階的に要件を実装できます。また、テストに合格するように作成されているため、実装はテスト可能であることが保証されています。

この記事で作成したカスタムクラスはデモンストレーション目的でのみ使用されているため、実際のプロジェクトでは使用しないでください。

簡潔にするために省略したテストおよび実装方法を含む、このチュートリアルの完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/core-java-collections[over on GitHubにあります]。