はじめに
ひとりJUnitアドベントカレンダー5日目の記事です。
5日目にして息切れしてきました。ひとりカレンダーしてる人すごい。時間がアホほど溶ける。
@ParameterizedTest
で受け渡される引数の型
この記事が載るアドベントカレンダーの2日目〜4日目までで、
@ParameterizedTest
のさまざまな使い方、特に引数の渡し方を見てきました。
@ParameterizedTest
@ValueSource(ints = {0, 1, 100, 1000})
void test(int number) {
int actual = hogeService.roundDown(number);
assertThat(actual, is(0));
}
上記の例で言えば、@ValueSource
で指定している4つの数値が引数number
に設定され、
1つのテストコードで4回分のテストが実行されます。
@ValueSource
でints
と指定して、引数でもint
を受け取っていますが
必ずしもSource側で指定されている型のみしか受け取れないわけではありません。
String
を渡した場合、JUnitが提供する仕組みにより別の型として受け取ることができます。
基本的な型変換
まずはenumです。
@AllArgsConstructor
@ToString
private enum Nen {
TEN("纏"),
ZETSU("絶"),
REN("練"),
HATSU("発");
private String kanji;
}
@ParameterizedTest
@ValueSource(strings = {"TEN", "ZETSU", "REN", "HATSU"})
void test(Nen nen) {
System.out.println(nen);
}
定義名(要するにenumのname()
の戻り値)を指定し、
引数の型としてenumのクラス名を指定すると、その定義を受け取ることができます。
実行結果は以下の通り。しっかりenumとして受け取れています。
また、プリミティブ型も変換できます。
@ParameterizedTest
@ValueSource(strings = {"true", "false"})
void test(boolean isFoo) {
System.out.println(isFoo);
}
が、以下のように書けばよいだけなので、こちらを使うことはほぼないでしょう。
@ParameterizedTest
@ValueSource(booleans = {true, false})
void test(boolean isFoo) {
System.out.println(isFoo);
}
以下のようなたまに使ったり使わなかったりするクラスにも変換できます。
@ParameterizedTest
@ValueSource(strings = {"2022-12-05", "2023-01-05"})
void test(LocalDate localDate) {
System.out.println(localDate);
}
@ParameterizedTest
@ValueSource(strings = "https://qiita.com/advent-calendar/2022")
void test(URI uri) {
System.out.println(uri);
}
@ParameterizedTest
@ValueSource(strings = {"en", "ja"})
void test(Locale locale) {
System.out.println(locale);
}
全ラインナップはこちら。
staticファクトリメソッドによる型変換
staticファクトリメソッドとは、そのクラスのインスタンスを返すstaticメソッドのことです。
例えば以下のようなもの。
@AllArgsConstructor
@ToString
public class Student {
private String name;
public static Student of(String name) {
return new Student(name);
}
}
public void hoge() {
Student student = Student.of("John");
}
Javaのパッケージで言うと、LocalDateTime#of
やBoolean#valueOf
などがそうです。
メソッド名はof
やvalueOf
の他、from
やgetInstance
等もよく見られます。
JUnit5では、String
の引数一件を受け取るstaticファクトリメソッドがある場合
自動的にそのメソッドを使って型変換を行うことができます。
@AllArgsConstructor
@ToString
public class Student {
private String name;
public static Student of(String name) {
return new Student(name);
}
}
@ParameterizedTest
@ValueSource(strings = {"Bob", "Tom"})
void test(Student student) {
System.out.println(student);
}
じゃあString
を受け取るstaticファクトリメソッドが二つあったらどうなるの?ということで
以下のテストを実行してみました。
@ToString
@Setter
@NoArgsConstructor
public class Student {
private String name;
public Student(String name) {
System.out.println("constructor");
this.name = name;
}
public static Student of(String name) {
Student student = new Student();
student.setName(name);
System.out.println("of");
return student;
}
public static Student valueOf(String name) {
Student student = new Student();
student.setName(name);
System.out.println("valueOf");
return student;
}
}
@ParameterizedTest
@ValueSource(strings = {"Bob", "Tom"})
void test(Student student) {
System.out.println(student);
}
引数ありコンストラクタとstaticファクトリメソッド二種にsysoutを仕込んでいます。
さて、出力されるのはof
か、それともvalueOf
なのか、どちらかというと・・・
実はconstructor
です。
というのも、staticファクトリメソッドが2種類あるとどちらも無視されるという仕様があり
またstaticファクトリメソッドが無くても、引数がString
一つのコンストラクタがあれば
そちらを元に暗黙の型変換が行われるようになっているようなのです。
整理するとこんな感じです。
staticファクトリメソッドの方がコンストラクタよりも優先度は高い。
ただ、メソッドが2件以上あると無視される。ということですね。