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つの主なステップは次のとおりです。
-
筆記テスト –
テスト形式で要件を定義する -
機能の実装 –
集中することなくテストに合格する
コードの優雅さに
。
リファクタリング –
読みやすくするためにコードを改善し、
まだテストに合格している間維持する
最も単純なものから始めて、
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にあります]。