Hello! This is the 5th series to play with Java from scratch. This time, we will make the validation test class that we made last time.
When spoiled, it is the type of Qiita post that has a failure example first. If you want to see an example of the parameterization test quickly, please skip to ◎ Good example
Click here for articles up to the last time ↓
The current validation requirements are as follows. Annotation is added to the form class to check the following character types and the number of characters.
--Available character types: Half-width alphanumeric characters only --Character limit: 4 characters or more
Since we are validating with the form class, we will create a test class for the form class. There are four cases I would like to test this time.
However, even normal systems allow half-width alphanumeric characters, so if you want to perform a more rigorous test, you want a data pattern of only numbers, only alphabets, and alphanumerics. I also want to do data patterns such as hiragana, katakana, kanji, double-byte alphanumeric characters, symbols, etc. for abnormal characters with incorrect character types ... [^ 1]
The following code was created in a hurry with 3 normal patterns, 2 incorrect character count patterns, and 2 incorrect character type patterns. The JUnit test still passes, but the test execution and result verification are very redundant because the same code is repeated (boilerplate code) ... Moreover, abnormal kanji and hiragana, mixed character type patterns, and abnormal types in which both the number of characters and the character type are invalid have not been implemented yet. If you try to complete it as it is, the amount of code will increase further.
[^ 1]: Strictly speaking, the implementation of this form is almost only using the validation function of javax (I have not created my own validation etc.), so there is no need to thoroughly test it on the library user side. If something goes wrong with this, there is a fatal bug in the javax library itself. Please think that it is just an undercard for wanting to introduce the parameterization test (laugh)
EchoFormTest.java
package com.example.form;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
@SpringBootTest
public class EchoFormTest {
@Autowired
Validator validator;
private EchoForm testEchoForm = new EchoForm();
private BindingResult bindingResult = new BindException(testEchoForm, "echoForm");
/**
*Normal system
*/
@Test
public void test_echo_Normal system_4 or more letters() {
//Test preparation
testEchoForm.setName("aaaa");
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertNull(bindingResult.getFieldError());
}
@Test
public void test_echo_Normal system_4 or more numbers() {
//Test preparation
testEchoForm.setName("1111");
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertNull(bindingResult.getFieldError());
}
@Test
public void test_echo_Normal system_4 or more alphanumeric characters() {
//Test preparation
testEchoForm.setName("aa11");
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertNull(bindingResult.getFieldError());
}
/**
*Abnormal system_insufficient number of characters
*/
@Test
public void test_echo_Abnormal system_Half-width English 3 characters() {
//Test preparation
testEchoForm.setName("aaa");
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertThat(bindingResult.getFieldError().toString()).contains("Must be at least 4 characters.");
}
@Test
public void test_echo_Abnormal system_3 single-byte characters() {
//Test preparation
testEchoForm.setName("111");
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertThat(bindingResult.getFieldError().toString()).contains("Must be at least 4 characters.");
}
/**
*Abnormal system_character type invalid
*/
@Test
public void test_echo_Abnormal system_Hiragana() {
//Test preparation
testEchoForm.setName("Ah ah");
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertThat(bindingResult.getFieldError().toString()).contains("Only half-width alphanumeric characters are valid.");
}
@Test
public void test_echo_Abnormal system_Katakana() {
//Test preparation
testEchoForm.setName("Aaaaaaa");
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertThat(bindingResult.getFieldError().toString()).contains("Only half-width alphanumeric characters are valid.");
}
}
"I want to change only the test data with the same code for test execution and result verification ... That's it! Put the test data in an array, and use the for statement to execute and verify the results for each data! "
When you read the Java introductory book, redundant descriptions tend to be solved by for statements, so it's no wonder you think so. It can't be helped. Please say no (← experienced person).
Code that looks fine at first glance ... For statement that can be written shorter than [△ bad example ①] ... And do you know what's wrong with this code that goes through everything even if you actually run it in JUnit? ↓
EchoFormTest.java
/**
*Normal system
*/
@Test
public void test_echo_Normal system() {
String[] data = {"aaaa", "aa11", "1111"};
for(String testCase: data) {
//Test preparation
testEchoForm.setName(testCase);
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertNull(bindingResult.getFieldError());
}
}
Actually, in this example, it is good if all the test data ends normally, but if the test fails with the data in the middle, the test will fail without the subsequent test data being executed. Not all data can be tested.
For example, if you try to make an error occur in the second and third of the three data as shown below ...
EchoFormTest.java
/**
*Normal system
*/
@Test
public void test_echo_Normal system() {
String[] dataPattern = {"aaaa", "err", "bad"}; //Err does not pass this test as 3 characters or less will result in an error
for(String testData: dataPattern) {
//Test preparation
testEchoForm.setName(testData);
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertNull(bindingResult.getFieldError());
}
}
Since the test fails with the second data, the log only shows the second error. Since the third and subsequent data are not tested in the first place, it is not possible to determine whether the subsequent data is normal or abnormal.
org.opentest4j.AssertionFailedError: expected: <null> but was: <Field error in object 'echoForm' on field 'name': rejected value [err]; codes [Size.echoForm.name,Size.name,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [echoForm.name,name]; arguments []; default message [name],2147483647,4]; default message [Must be at least 4 characters.]>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.AssertNull.failNotNull(AssertNull.java:48)
at org.junit.jupiter.api.AssertNull.assertNull(AssertNull.java:37)
at org.junit.jupiter.api.AssertNull.assertNull(AssertNull.java:32)
at org.junit.jupiter.api.Assertions.assertNull(Assertions.java:258)
at com.example.form.EchoFormTest.test_echo_Normal system(EchoFormTest.java:97)
It would be scary to think that there would be 100 or 200 data patterns after that. No matter how redundant it is, it can be said that [△ bad example ①] is still better than this [× bad example ②] because it can fulfill its responsibilities as a test code.
Sorry I made you wait! Here's a good example. Implementation in parameterized test.
EchoFormTest.java
/**
*Normal system
*/
@ParameterizedTest
@ValueSource(strings = {"aaaa", "aa11", "1111"})
public void test_echo_Normal system(String s) {
//Test preparation
testEchoForm.setName(s);
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertNull(bindingResult.getFieldError());
}
There is no redundant code like [△ bad example ①]. Furthermore, what is good about parameterization is that it solves the problems of "subsequent tests are not executed" and "I do not know which test data was executed" that I saw in [× Bad example (2)]. Looking at the execution results below, you can see that all three test data have been completed normally.
Also, increase the data and check if the first and fourth data are normal and the second and third data are incorrect.
EchoFormTest.java
/**
*Normal system
*/
@ParameterizedTest
@ValueSource(strings = {"aaaa", "err", "bad", "1111"}) //err and bad are out of characters
public void test_echo_Normal system(String s) {
//Test preparation
testEchoForm.setName(s);
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertNull(bindingResult.getFieldError());
}
Even after the second error occurred, the third and fourth data were properly verified, and the stack trace came out for each error! JUnit5 is too convenient ...
I will also post an example of an abnormal system.
EchoFormTest.java
/**
*Abnormal system_insufficient number of characters
*/
@ParameterizedTest
@ValueSource(strings = {"aaa", "111", "aa1"})
public void test_echo_Abnormal system_Insufficient number of characters(String s) {
//Test preparation
testEchoForm.setName(s);
//Test implementation
validator.validate(testEchoForm, bindingResult);
//Result verification
assertThat(bindingResult.getFieldError().toString()).contains("Must be at least 4 characters.");
I was able to verify this without any problems!
Please also refer to the following Qiita (I used it as a reference this time!). JUnit 5 parameterization test is super convenient
It's a slapstick, but in the case of JUnit 4, the following site will be helpful.
https://javaworld.helpfulness.jp/post-81/
I implemented it using @RunWith (Parameterized.class)
. Although the amount of code is larger than that of JUnit5, the parameterization test can be fully implemented in JUnit4. You can also use it with another test runner by creating it as an inner class of @ RunWith (Enclosed.class)
.
This time we looked at the parameterization test in JUnit 5. A bad example is how I actually wrote it when I didn't know the existence of the parameterized test itself ... It's actually easy to come up with a test with a for statement, so if you have done it, please consider changing to a parameterized test!
Thank you for reading!
Recommended Posts