［Java / Eclipse］JUnit を利用したテストコード・リファクタリング

## この記事で書くこと
- JUnit を利用したテストコード・リファクタリング

## 題材
下記書籍の内容を題材とした

[新装版 リファクタリング―既存のコードを安全に改善する― (OBJECT TECHNOLOGY SERIES) ](https://amzn.asia/d/3Q0v6Ap)

## JUnit の設定方法
1. Eclipse のプロジェクトファイルを右クリック
1. ビルド・パス > ライブラリの追加をクリック
1. JUnit を選択し、完了をクリック
1. Eclipse のプロジェクトファイルを右クリック > 新規 > その他を選択
1. JUnit > JUnit テスト・ケースをクリック
1. 名前に任意の値を設定し、完了をクリック
1. 追加されたファイルにテストコードを書く

## JUnit テストの実行方法
1. Eclipse のプロジェクトファイルを右クリック > 実行を選択
1. JUnit テストをクリック
1. テスト結果を確認

## コード
### Customer.java （リファクタリング後）
```Java
import java.util.Enumeration;
import java.util.Vector;

public class Customer {
    private String _name;
    private Vector _rentals = new Vector();

    public Customer(String name) {
        _name = name;
    }

    public void addRental(Rental arg) {
        _rentals.add(arg);
    }

    public String getName() {
        return _name;
    }

    public String statement() {
        double totalAmount = 0;
        int frequentRenterPoints = 0;
        Enumeration rentals = _rentals.elements();
        String result = "Rental Record for " + getName() + "\n";

        while (rentals.hasMoreElements()) {
            Rental each = (Rental) rentals.nextElement();
            frequentRenterPoints += each.getFrequentRenterPoint();

            result += each.getMovie().getTitle() + " " + String.valueOf(each.getCharge()) + "\n";
            totalAmount += each.getCharge();
        }

        result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
        result += "You earned " + String.valueOf(getFrequentRenterPoints()) + " frequent renter points";
        return result;
    }
    
    private double amountFor(Rental aRental) {
    	return aRental.getCharge();
    }
    
    private int getFrequentRenterPoints() {
    	int result = 0;
    	Enumeration rentals = _rentals.elements();
    	while(rentals.hasMoreElements()) {
    		Rental each = (Rental)rentals.nextElement();
    		result += each.getFrequentRenterPoint();
    	}
    	return result;
    }
}
```

### CustomerTest.java （Customer クラスのテスト）
```Java
import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

class CustomerTest {

	@Test
	void testStatement() {
        Customer customer = new Customer("Customer1");
        Movie movie1 = new Movie("Movie1", 0);
        Rental rental1 = new Rental(movie1, 14);
        customer.addRental(rental1);
		String expect = "Rental Record for Customer1\n" +
				"Movie1 20.0\n" +
				"Amount owed is 20.0\n" +
				"You earned 1 frequent renter points";
		assertEquals(customer.statement(), expect);
	}
}
```

### Rental.java （リファクタリング後）
```java
public class Rental {
    private Movie _movie;
    private int _daysRented;

    public Rental(Movie movie, int daysRented){
        _movie = movie;
        _daysRented = daysRented;
    }

    public int getDaysRented(){
        return _daysRented;
    }

    public Movie getMovie(){
        return _movie;
    }
    
    double getCharge() {
    	return _movie.getCharge(_daysRented);
    }
    
    int getFrequentRenterPoint() {
        if ((getMovie().getPriceCode() == Movie.NEW_RELEASE) && getDaysRented() > 1) {
            return 2;
        } else {
        	return 1;
        }
    }
}
```

### RentalTest.java （Rental クラスのテスト）
```java
import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

class RentalTest {

	@Test
	void testGetCharge() {
		Movie movie1 = new Movie("movie1", 0);
		Rental rental1 = new Rental(movie1, 0);
		assertEquals(rental1.getCharge(),2.0);
	}
	
	@Test
	void testGetFrequentRenterPoints() {
		Movie movie2 = new Movie("movie2", 0);
		Rental rental2 = new Rental(movie2, 0);
		assertEquals(rental2.getFrequentRenterPoint(),1);
		
		Movie movie3 = new Movie("movie3",1);
		Rental rental3 = new Rental(movie3,2);
		assertEquals(rental3.getFrequentRenterPoint(),2);
	}

}
```

### Movie.java （リファクタリング後）
```java
public class Movie {
    public static final int CHILDRENS = 2;
    public static final int REGULAR = 0;
    public static final int NEW_RELEASE = 1;

    private String _title;
    private Price _price;

    public Movie(String title, int priceCode) {
        _title = title;
        setPriceCode(priceCode);
    }

    public int getPriceCode(){
        return _price.getPriceCode();
    }

    public void setPriceCode(int arg){
        switch(arg) {
        case Movie.REGULAR:
        	_price = new RegularPrice();
        	break;
        case Movie.CHILDRENS:
        	_price = new ChildrensPrice();
        	break;
        case Movie.NEW_RELEASE:
        	_price = new NewReleasePrice();
        }
    }

    public String getTitle(){
        return _title;
    }
    
    double getCharge(int daysRented) {
    	return _price.getCharge(daysRented);
    }
}
```

### MovieTest.java （Movie クラスのテスト）
```java
import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

class MovieTest {

	@Test
	void testGetCharge() {
		Movie movie4 = new Movie("movie4", 0);
		Rental rental4 = new Rental(movie4, 0);
		assertEquals(rental4.getFrequentRenterPoint(),1);
		
		Movie movie5 = new Movie("movie5",1);
		Rental rental5 = new Rental(movie5,2);
		assertEquals(rental5.getFrequentRenterPoint(),2);
	}
	
	@Test
	void testGetPriceCode() {
		Movie movie6 = new Movie("movie6",0);
		assertEquals(movie6.getPriceCode(),0);
	}

}
```

### Price.java
```java
public abstract class Price {
	abstract int getPriceCode();
	
	double getCharge(int daysRented) {
    	double result = 0;
        switch (getPriceCode()) {
        case Movie.REGULAR:
            result += 2;
            if (daysRented > 2) {
                result += (daysRented - 2) * 1.5;
                break;
            }
        case Movie.NEW_RELEASE:
            result += daysRented * 3;
            break;
        case Movie.CHILDRENS:
            result += 1.5;
            if (daysRented > 3) {
                result += (daysRented - 3) * 1.5;
            }
            break;
        }
		return result;
	}
}
```

### ChildrensPrice.java
```java
public class ChildrensPrice extends Price {

	@Override
	int getPriceCode() {
		// TODO 自動生成されたメソッド・スタブ
		return Movie.CHILDRENS;
	}

}
```


### NewReleasePrice.java
```java
public class NewReleasePrice extends Price {

	@Override
	int getPriceCode() {
		// TODO 自動生成されたメソッド・スタブ
		return Movie.NEW_RELEASE;
	}

}
```


### RegularPrice.java
```java
public class RegularPrice extends Price {

	@Override
	int getPriceCode() {
		// TODO 自動生成されたメソッド・スタブ
		return Movie.REGULAR;
	}

}

```

### PriceTest.java （Price クラスに関連するテスト）
```java
import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

class PriceTest {

	@Test
	void testGetChildensPriceCode() {
		Movie childrensMovie = new Movie("ChildrensMovie",2);
		assertEquals(childrensMovie.getPriceCode(),Movie.CHILDRENS);
	}
	
	@Test
	void testGetNewReleasePriceCode() {
		Movie newReleaseMovie = new Movie("NewreleaseMovie",1);
		assertEquals(newReleaseMovie.getPriceCode(),Movie.NEW_RELEASE);
	}
	
	@Test
	void testGetRegularPriceCode() {
		Movie regularMovie = new Movie("RegularMovie",0);
		assertEquals(regularMovie.getPriceCode(),Movie.REGULAR);
	}
	
	@Test
	void testGetCharge() {
		Movie movie7 = new Movie("movie7", 0);
		Rental rental7 = new Rental(movie7, 0);
		assertEquals(rental7.getFrequentRenterPoint(),1);
		
		Movie movie8 = new Movie("movie8",1);
		Rental rental8 = new Rental(movie8,2);
		assertEquals(rental8.getFrequentRenterPoint(),2);
	}
	
	@Test
	void testGetPriceCode() {
		Movie movie9 = new Movie("movie9",0);
		assertEquals(movie9.getPriceCode(),0);
	}

}
```