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

1。概要

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

これはTDDの紹介ではないので、それが何を意味するのか、そしてそれをより良くするための持続的な関心について、すでにいくつかの基本的な考えを持っていると想定しています。

簡単に言えば、TDDは設計ツールであり、テストの助けを借りて実装を推進することができます

簡単な免責事項–ここでは効率的な実装の作成に焦点を当てていません–TDDプラクティスを表示するための言い訳として使用するだけです。

2.はじめに

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

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

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

開始するには、これらのメソッドに空のボディを提供するだけです。この方法は、戻り値の型を持っている場合は、私たちのような、その型の任意の値を返すことができ、ヌルのためのオブジェクトまたはのためのブール

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

3.TDDサイクル

TDDを使用して実装を開発するということは、最初テストケース作成する必要があることを意味します。これにより、実装の要件を定義します。唯一の我々は実装コードを作成したり、修正しますこれらのテストに合格するために。

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

  1. テストの作成–テストの形式で要件を定義します
  2. 機能の実装–コードの優雅さにあまり焦点を当てずにテストに合格させる
  3. リファクタリング–コードを改善して、テストに合格しながら読みやすく、保守しやすくします。

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

4.のisEmptyメソッド

isEmptyの方法は、おそらくに定義されている最も簡単な方法であるリストのインターフェース。これが私たちの最初の実装です:

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

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

4.1。最初のサイクル

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

@Test public void givenEmptyList_whenIsEmpty_thenTrueIsReturned() { List list = new CustomList(); assertTrue(list.isEmpty()); }

isEmptyメソッドは常にfalseを返すため、指定されたテストは失敗します。戻り値を反転するだけで合格させることができます。

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

4.2。2番目のサイクル

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

@Test public void givenNonEmptyList_whenIsEmpty_thenFalseIsReturned() { List list = new CustomList(); list.add(null); assertFalse(list.isEmpty()); }

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.サイズ方式

これは、CustomListクラスのコンパイルを可能にするsizeメソッドの最初の実装です。

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

5.1。最初のサイクル

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

@Test public void givenListWithAnElement_whenSize_thenOneIsReturned() { List 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。リファクタリング

サイズメソッドをリファクタリングして、よりエレガントにすることができます。

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

The implementation of this method is now complete.

6. The get Method

Here's the starting implementation of get:

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

6.1. The First Cycle

Let's take a look at the first test for this method, which verifies the value of the single element in the list:

@Test public void givenListWithAnElement_whenGet_thenThatElementIsReturned() { List list = new CustomList(); list.add("baeldung"); Object element = list.get(0); assertEquals("baeldung", element); }

The test will pass with this implementation of the get method:

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

6.2. Improvement

Usually, we'd add more tests before making additional improvements to the get method. Those tests would need other methods of the List interface to implement proper assertions.

However, these other methods aren't mature enough, yet, so we break the TDD cycle and create a complete implementation of the get method, which is, in fact, not very hard.

It's easy to imagine that get must extract an element from the internal array at the specified location using the index parameter:

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

7. The add Method

This is the add method we created in section 4:

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

7.1. The First Cycle

The following is a simple test that verifies the return value of add:

@Test public void givenEmptyList_whenElementIsAdded_thenGetReturnsThatElement() { List list = new CustomList(); boolean succeeded = list.add(null); assertTrue(succeeded); }

We must modify the add method to return true for the test to pass:

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

Although the test passes, the add method doesn't cover all cases yet. If we add a second element to the list, the existing element will be lost.

7.2. The Second Cycle

Here's another test adding the requirement that the list can contain more than one element:

@Test public void givenListWithAnElement_whenAnotherIsAdded_thenGetReturnsBoth() { List 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); }

The test will fail since the add method in its current form doesn't allow more than one element to be added.

Let's change the implementation code:

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

The implementation is elegant enough, hence we don't need to refactor it.

8. Conclusion

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

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

簡潔にするために省略されたテストと実装のメソッドを含む、このチュートリアルの完全なソースコードは、GitHubにあります。