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にあります]。