10.2.3. 機能ごとのテスト実装¶
本節では、レイヤ単位に当てはめられない機能のテスト方法ついて説明する。
10.2.3.1. 入力チェックの単体テスト¶
ここでは、以下の入力チェックの単体テスト実装方法を説明する。
Validationの種類 |
説明 |
---|---|
Bean Validation |
Hibernate validatorを使用して実装した |
Bean Validation |
Spring のDIコンテナを使用して実装した |
Spring Validation |
|
Validator
の単体テストは本来Controller
のテストとして行うが、その場合は試験パターンが多くなるため、テストの実施コストを考慮しController
と切り分けてValidator
単体としてテストを行うこともできる。Validator
単体としてのテスト作成方法を説明する。本節では、Bean Validation
を使用している場合とSpring Validation
を使用している場合のそれぞれについて実装方法を説明する。
10.2.3.1.1. Bean Validationで実装したValidatorの単体テスト¶
Bean Validation
のテスト行う場合、アプリケーションサーバからライブラリが提供されないため、必要な依存ライブラリ追加する必要がある。追加方法については、依存ライブラリの追加を参照されたい。なお、Hibernate Validator
が用意する入力チェック機能についてはテストスコープ外とする。
ここでは、以下の2通りのBean Validationのテスト方法について説明する。
Hibernate validatorを使用した
Bean Validation
Spring のDIコンテナを使用した
Bean Validation
10.2.3.1.1.1. Hibernate validatorを使用したBean Validation
のテスト¶
Hibernate validatorを使用したBean Validation
の単体テストにおいて、作成するファイルを以下に示す。
作成するファイル名 |
説明 |
---|---|
|
|
|
|
ここでは、テスト対象の@FullWidthKatakana
を使用したBeanクラス(FullWidthKatakanaTestBean
)を作成し、
jakarta.validation.ValidatorFactory
から生成したjakarta.validation.Validator
の実装クラスにより
バリデーションチェックを行っている。
以下に、@FullWidthKatakana
アノテーションをフィールドに付与したBeanクラスの作成例を示す。
FullWidthKatakanaTestBean.java
public class FullWidthKatakanaTestBean {
@FullWidthKatakana
private String testString;
public FullWidthKatakanaTestBean() {
// constructor
}
public String getTestString() {
return testString;
}
public void setTestString(String testString) {
this.testString = testString;
}
}
FullWidthKatakanaTest.java
public class FullWidthKatakanaTest {
private static Validator validator;
@BeforeClass
public static void setUpBeforeClass() {
// setup
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
// (1)
validator = validatorFactory.getValidator();
}
@Test
public void testFullWidthKatakana() {
// setup
FullWidthKatakanaTestBean form = new FullWidthKatakanaTestBean();
form.setTestString("デンデン");
// run the test
Set<ConstraintViolation<FullWidthKatakanaTestBean>> violations = validator.validate(form); // (2)
// assert
assertThat(violations, is(empty())); // (3)
}
}
項番 |
説明 |
---|---|
(1)
|
getValidator メソッドにより、Validator を取得する。Validator を取得することで、validate メソッドを使った入力チェックが可能となる。 |
(2)
|
validate メソッドを使い、入力チェックを行う。validate メソッドを実行することで、入力チェックエラーの数だけConstrainViolation のSet が返ってくる。
validate メソッドの引数にはFullWidthKatakanaBean クラスのオブジェクトを指定する。 |
(3)
|
(2)で取得した
Set から、エラーが発生したかどうかを確認する。今回はエラーがないため、空の
Set が返ってくる。 |
Note
バリデーショングループを使用したテスト
バリデーショングループを設定している場合、入力チェックを行なう際のvalidate
メソッド引数に、グループを示す任意のjava.lang.Class
オブジェクトを指定することで、指定したグループのValidator
のみ適用して実行できる。
バリデーショングループについては、バリデーションのグループ化を参照されたい。
以下に、バリデーショングループを使用したForm
例を示す。
テスト対象の
FullWidthKatakanaTestBean.java
public class FullWidthKatakanaTestBean { public interface Search {}; public interface Register {}; // (1) @Size(min = 5, max = 10, groups = Search.class) @FullWidthKatakana(groups = Register.class) @NotNull private String testString; public FullWidthKatakanaTestBean() { // constructor } public String getTestString() { return testString; } public void setTestString(String testString) { this.testString = testString; } }
項番
説明
(1)フィールドに設定するValidator
をグループ化している。FullWidthKatakanaTest.java
public class FullWidthKatakanaTest { // omitted @Test public void testFullWidthKatakana() { // setup FullWidthKatakanaTestBean form = new FullWidthKatakanaTestBean(); form.setTestString("テスト"); // run the test // (1) Set<ConstraintViolation<FullWidthKatakanaTestBean>> violations = validator.validate(form); // assert assertThat(violations, is(empty())); // (2) } }
項番
説明
(1)validate
メソッドの引数に、java.lang.Class
オブジェクトを追加することで、設定したバリデーショングループに対して入力チェックを実行できる。また、java.lang.Class
オブジェクトは例のように複数指定することもできる。(2)エラーが発生したかどうかを確認する。
10.2.3.1.1.2. Spring のDIコンテナを使用したBean Validation
のテスト¶
Spring のDIコンテナを使用したBean Validation
の単体テストにおいて、作成するファイルを以下に示す。
作成するファイル名 |
説明 |
---|---|
|
Spring のDIコンテナを使用した |
|
Spring Testを使用して単体テストを行う際に必要な設定を補うための設定ファイル。 |
作成するファイル名 |
説明 |
---|---|
|
Spring のDIコンテナを使用した |
|
Spring Testを使用して単体テストを行う際に必要な設定を補うための設定ファイル。 |
Spring のDIコンテナを利用したBean Validation
は、org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
からValidator
オブジェクトを生成することでテストすることができる。
@ExistInCodeList
を例にテストの実装方法を説明する。@ExistInCodeList
についての詳細はコードリストを用いたコード値の入力チェックを参照されたい。テストで使用する設定ファイルに、Validator
オブジェクトを生成するためのLocalValidatorFactoryBean
をBean定義する。
TestContextConfig.java
// (1)
/**
* Configure {@link LocalValidatorFactoryBean} bean.
* @return Bean of configured {@link LocalValidatorFactoryBean}
*/
@Bean("validator")
public LocalValidatorFactoryBean localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
項番 |
説明 |
---|---|
(1)
|
@ExistInCodeList でDIコンテナからコードリストBeanを取得するため、
TestContextConfig.java でBean定義したLocalValidatorFactoryBean から生成したValidator を使う必要がある。 |
test-context.xml
<!-- (1) -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
項番 |
説明 |
---|---|
(1)
|
@ExistInCodeList でDIコンテナからコードリストBeanを取得するため、
test-context.xml でBean定義したLocalValidatorFactoryBean から生成したValidator を使う必要がある。 |
以下に、@ExistInCodeList
が使われているForm
クラスの実装例を示す。
TicketSearchForm.java
public class TicketSearchForm implements Serializable {
@NotNull
@ExistInCodeList(codeListId = "CL_AIRPORT") // (1)
private String depAirportCd;
// omitted
}
項番 |
説明 |
---|---|
(1)
|
depAirportCd フィールドに対して、コードリストに存在する値かどうか検証する。 |
以下に、@ExistInCodeList
のテストクラス作成方法を説明する。
ここでは、SampleCodeListConfig.java
またはsample-codelist.xml
に定義したコードリスト(CL_AIRPORT
)に定義していない値を設定し、
インジェクションしたjakarta.validation.Validator
の実装クラスによりバリデーションチェックエラーになることを確認している。
ExistInCodeListTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { SampleEnvConfig.class, SampleInfraConfig.class, SampleCodeListConfig.class,
TestContextConfig.class })
public class ExistInCodeListTest {
// (1)
@Inject
private Validator validator;
@Test
public void testExistInCodeList() {
// setup
TicketSearchForm ticketSearchForm = new TicketSearchForm();
// (2)
ticketSearchForm.setDepAirportCd("AAA");
// omitted
// run the test
Set<ConstraintViolation<TicketSearchForm>> violations = validator
.validate(ticketSearchForm);
// assert
// (3)
assertThat(violations.size(), is(1));
ConstraintViolation<TicketSearchForm> violation = violations.iterator().next();
// (4)
assertThat(violation.getPropertyPath().toString(), is("depAirportCd"));
// (5)
assertThat((String) violation.getInvalidValue(), is("AAA"));
// (6)
assertThat(violation.getMessage(), is("Does not exist in CL_AIRPORT"));
}
}
ExistInCodeListTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"classpath:META-INF/spring/sample-infra.xml",
"classpath:META-INF/spring/sample-codelist.xml",
"classpath:META-INF/spring/test-context.xml" })
public class ExistInCodeListTest {
// (1)
@Inject
private Validator validator;
@Test
public void testExistInCodeList() {
// setup
TicketSearchForm ticketSearchForm = new TicketSearchForm();
// (2)
ticketSearchForm.setDepAirportCd("AAA");
// omitted
// run the test
Set<ConstraintViolation<TicketSearchForm>> violations = validator
.validate(ticketSearchForm);
// assert
// (3)
assertThat(violations.size(), is(1));
ConstraintViolation<TicketSearchForm> violation = violations.iterator().next();
// (4)
assertThat(violation.getPropertyPath().toString(), is("depAirportCd"));
// (5)
assertThat((String) violation.getInvalidValue(), is("AAA"));
// (6)
assertThat(violation.getMessage(), is("Does not exist in CL_AIRPORT"));
}
}
項番 |
説明 |
---|---|
(1)
|
Validator にSpringのLocalValidatorFactoryBean から生成したValidator をDIしている。LocalValidatorFactoryBean から生成したValidator はSpringのDIコンテナ上で動作し、@ContextConfiguration で読み込んだコードリストのBeanを取得することができる。これにより、
@ExistInCodeList を期待通りに動作させることができる。 |
(2)
|
コードリストに存在しないコードを入力し、
@ExistInCodeList でエラーが発生することを期待する。 |
(3)
|
size メソッドを使って入力チェックエラーの数を取得し、エラーが発生したかどうかを確認する。 |
(4)
|
違反したフィールドが想定した箇所であるかを確認する。
|
(5)
|
違反した入力値が想定した値であるかを確認する。
|
(6)
|
発生したエラーのメッセージを確認する。
|
10.2.3.1.2. Spring Validatorで実装したValidatorの単体テスト¶
Validator(Spring Validation)
の単体テストにおいて、作成するファイルを以下に示す。
作成するファイル名 |
説明 |
---|---|
|
|
以下に、テスト対象のクラスを示す。
TicketSearchValidator.java
@Component
public class TicketSearchValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return (TicketSearchForm.class).isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
TicketSearchForm form = (TicketSearchForm) target;
if (!errors.hasFieldErrors("depAirportCd")
&& !errors.hasFieldErrors("arrAirportCd")) {
String depAirport = form.getDepAirportCd();
String arrAirport = form.getArrAirportCd();
if (depAirport.equals(arrAirport)) {
errors.reject(TicketSearchErrorCode.E_AR_B1_5001.code());
}
}
// omitted
}
}
Validator(Spring Validation)
のテストクラス作成方法を説明する。TicketSearchValidator
でエラーになる値をTicketSearchForm
に設定してバリデーションエラーになることと、エラーメッセージが正しいことを確認している。TicketSearchValidatorTest.java
public class TicketSearchValidatorTest {
private static TicketSearchValidator validator;
private TicketSearchForm ticketSearchForm;
private BindingResult result;
@BeforeClass
public static void setUpBeforeClass() {
// setup
validator = new TicketSearchValidator();
}
@Test
public void testTicketSearchValidator() {
// setup
ticketSearchForm = new TicketSearchForm();
result = new DirectFieldBindingResult(ticketSearchForm, "TicketSearchForm");
ticketSearchForm.setFlightType(FlightType.RT);
ticketSearchForm.setDepAirportCd("HND");
ticketSearchForm.setArrAirportCd("HND");
// omitted
// run the test
// (1)
validator.validate(ticketSearchForm, result);
// (2)
assertThat(result.hasErrors(), is(true));
// (3)
ObjectError error = result.getGlobalError();
// (4)
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
// (5)
messageSource.setBasename("i18n/sample-messages");
// (6)
messageSource.setUseCodeAsDefaultMessage(true);
String code = error.getCode();
// assert
// (7)
assertThat(code, is(TicketSearchErrorCode.E_AR_B1_5001.code()));
assertThat(messageSource.getMessage(error, Locale.JAPAN),
is("出発空港と到着空港に同じ空港は指定できません。区間をご確認ください。"));
}
}
項番 |
説明 |
---|---|
(1)
|
validate メソッドの引数に、ticketSearchForm と、BindingResult インターフェースのオブジェクトを指定することで、ticketSearchForm に対する入力チェックの結果が、BindingResult クラスのオブジェクトに格納される。 |
(2)
|
hasErrors メソッドを使って、エラーの有無を判定する。エラーがある場合はtrueが返り値として返り、エラーがない場合はfalseが返り値として返る。
|
(3)
|
getGlobalError メソッドで、エラー内容を取得する。 |
(4)
|
エラーメッセージの内容を確認するために、
MessageSource の実装クラスであるorg.springframework.context.support.ResourceBundleMessageSource のオブジェクトを生成する。クラスの詳細については、ResourceBundleMessageSourceのJavadocを参照されたい。
|
(5)
|
setBasename メソッドに、メッセージが定義されたプロパティファイルを指定して読み込ませる。 |
(6)
|
setUseCodeAsDefaultMessage メソッドにtrueを指定すると、エラーコードに対応するメッセージが定義されていない場合にエラーコードが返される。falseを指定すると、エラーコードに対応するメッセージが定義されていない場合に
NoSuchMessageException が返される。デフォルトではfalseが適用されている。
|
(7)
|
エラーコード、メッセージ内容を検証する。
|