Test Spring framework controller with Junit

Target audience

・ Spring Framework beginners ・ Junit beginners ・ I want to test the Spring framework controller with Junit! !! !!

Project ver

・ Spring framework 4.1.6 ・ Junit 4.1.2 ・ Servlet 3.1.0 ・ Hamcrest 2.2 ・ Momo c and 3.3.3.

File structure

//Main layer
src.main.java
  |
  +--- jp.co.demo                         
        +--- controller 
        +--- dto
        +--- form
        +--- entity
        +--- mapper
        +--- service
  
//Test layer
src.test.java
  |
  +--- jp.co.demo                         
  |     +--- controller 
  |
src.test.resources
  |
  +--- META-INF
  |           |
  |           +--- sql
  |                  |
  |                  +--- insert_data.sql //SQL for creating test data
  |
  +--- spring
            |
            +--- mvc-config.xml //Bean definition file

test

SQL executable

insert_data.sql



DELETE FROM messages;
DELETE FROM users;

INSERT INTO users(id,account,password,name,branch_id,department_id) VALUES (1,'testuser','$2a$10$i0FlsKe6FiEuViMhclA90uCjCCKeLhtcswz01Rwl9qTIsIY1c.ohO','ALH Taro',1,1);

ALTER TABLE messages MODIFY id int not null; --Temporarily auto-increment reset to auto-increment reset
ALTER TABLE messages AUTO_INCREMENT = 1; --Auto increment reset
ALTER TABLE messages MODIFY id int not null auto_increment; --Auto increment grant

INSERT INTO messages(title,text,category,user_id, created_date)VALUES('title','Text','category',1,'2020-05-27 01:02:03'); --3
INSERT INTO messages(title,text,category,user_id, created_date)VALUES('title','Text','category',1,'2020-05-28 02:03:04'); --4
INSERT INTO messages(title,text,category,user_id, created_date)VALUES('title','Text','category',1,'2020-05-23 03:04:05'); --1
INSERT INTO messages(title,text,category,user_id, created_date)VALUES('title','Text','category',1,'2020-05-24 04:05:06'); --2
INSERT INTO messages(title,text,category,user_id, created_date)VALUES('title','Text','category',1,'2020-05-29 05:06:07'); --5

Controller test

DemoControllerTest.java



@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration //Ready to use context
@ContextConfiguration(locations = "classpath:spring/mvc-config.xml") //Specify which bean definition file to use
@Transactional //Defined for rollback after test
public class DemoControllerTest {

	private MockMvc mockMvc;

	@Autowired
	private WebApplicationContext wac; //Prepare context

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Before
	public void setUp() {
		executeScript("/META-INF/sql/insert_data.sql"); //SQL execution
		mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
	}

	@Test
	public void test_1 Home screen display() throws Exception {
		MvcResult mvcResult = mockMvc.perform(get("/home")) //Behavior of GET communication with perform
				.andDo(print()) //Print on console
				.andExpect(status().isOk()) //Check the status code
				.andExpect(view().name("home")) //Does the View match?
				.andExpect(model().attributeExists("allUserMessage")) //Check if the target model exists
				.andExpect(model().attributeExists("userComment")) //Check if the target model exists
				.andExpect(model().attributeExists("deleteCommentForm")) //Check if the target model exists
				.andExpect(model().attributeExists("commentForm")) //Check if the target model exists
				.andExpect(model().attributeExists("userMessageForm")) //Check if the target model exists
				.andReturn(); //Return the result to mvcResult

		ModelAndView mav = mvcResult.getModelAndView();
		int[] expectMessageIdList = { 5, 2, 1, 4, 3 }; //For ascending order confirmation

		@SuppressWarnings(value = "unchecked")
		List<UserMessageForm> actualMessageList = ((List<UserMessageForm>) mav.getModel().get("allUserMessage"));

		int expectMessageListSize = 5;
		assertEquals(expectMessageListSize, actualMessageList.size()); //Does the created data match the List size of the displayed model?

		for (int i = 0; i < 5; i++) {
			assertEquals(expectMessageIdList[i], actualMessageList.get(i).getId()); //Is the message content in ascending order?
		}

	}

	@Test
	public void test_2_1 Can the login screen be displayed?() throws Exception {
		mockMvc.perform(get("/login"))
				.andDo(print())
				.andExpect(status().isOk())
				.andExpect(model().attributeExists("loginForm"));
	}

	@Test
	public void test_2_2 Can you log in?() throws Exception {
		LoginForm loginForm = new LoginForm();
		loginForm.setAccount("testuser");
		loginForm.setPassword("password");

		mockMvc.perform(post("/login").flashAttr("loginForm", loginForm))
				.andDo(print())
				.andExpect(view().name("redirect:/home")) //Transition destination when login is successful
				.andExpect(status().isFound());
	}

	@Test
	public void test_2_3 When the password is incorrect, an error is output and the login screen is displayed.() throws Exception {
		LoginForm loginForm = new LoginForm();
		loginForm.setAccount("testuser");
		loginForm.setPassword("Wrong password");

		MvcResult mvcResult = mockMvc.perform(post("/login")
				.flashAttr("loginForm", loginForm))
				.andDo(print())
				.andExpect(view().name("login"))
				.andExpect(status().isOk())
				.andExpect(model().attributeHasErrors("loginForm")) //Is there an error in Model?
				.andReturn();

		ModelAndView mav = mvcResult.getModelAndView();
		BindingResult result = (BindingResult) mav.getModel()
				.get("org.springframework.validation.BindingResult.loginForm");

		String actualErrorMessage = result.getFieldError("account").getDefaultMessage();
		String expectErrorMessage = "Wrong login ID or password";
		assertThat(expectErrorMessage, is(actualErrorMessage)); //Check the error message
	}

	@Test
	public void test_2_4 When trying to log in with an account that does not exist, an error is output to the screen and the login screen is displayed.() throws Exception {
		LoginForm loginForm = new LoginForm();
		loginForm.setAccount("Non-existent account");
		loginForm.setPassword("password");

		MvcResult mvcResult = mockMvc.perform(post("/login")
				.flashAttr("loginForm", loginForm))
				.andDo(print())
				.andExpect(status().isOk())
				.andExpect(model().attributeHasErrors("loginForm")) //Is there an error in Model?
				.andReturn();

		ModelAndView mav = mvcResult.getModelAndView();
		BindingResult result = (BindingResult) mav.getModel()
				.get("org.springframework.validation.BindingResult.loginForm");

		String actualErrorMessage = result.getFieldError("account").getDefaultMessage();
		String expectErrorMessage = "Account does not exist";
		assertThat(expectErrorMessage, is(actualErrorMessage)); //Check the error message
	}

	public void executeScript(String file) {
		Resource resource = new ClassPathResource(file, getClass());

		ResourceDatabasePopulator rdp = new ResourceDatabasePopulator();
		rdp.addScript(resource);
		rdp.setSqlScriptEncoding("UTF-8");
		rdp.setIgnoreFailedDrops(true);
		rdp.setContinueOnError(false);

		Connection conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
		rdp.populate(conn);
	}

}

Impressions

It is just a unit test of the controller, and it does not consider acquiring and testing the rendering result on the View side (and no login filter etc. is applied). It seems that you can get the rendering result by using HtmlUnit, so I will list it on Qiita at another time.

References

Test data management by Java + Spring (3) [SpringBoot] How to write a controller test Spring test official document

Recommended Posts

Test Spring framework controller with Junit
Unit test with Junit.
Test controller with Mock MVC in Spring Boot
Test Web API with junit
[Spring] Controller exception output test
[JUnit 5 compatible] Write a test using JUnit 5 with Spring boot 2.2, 2.3
[JUnit 5] Write a validation test with Spring Boot! [Parameterization test]
I wrote a test with Spring Boot + JUnit 5 now
[Java] Test private methods with JUnit
Sample code to unit test a Spring Boot controller with MockMvc
Control test order in Junit4 with enumeration
Major changes related to Spring Framework 5.0 Test
Perform transaction confirmation test with Spring Boot
[Ruby on Rails] Controller test with RSpec
How to test private scope with JUnit
JUnit 5 gradle test fails with lombok annotation
Java automated test implementation with JUnit 5 + Gradle
Form class validation test with Spring Boot
[Java] How to test for null with JUnit
[CircleCI 2.0] [Java] [Maven] [JUnit] Aggregate JUnit test results with CircleCI 2.0
Java automated test implementation with JUnit 5 + Apache Maven
How to test interrupts during Thread.sleep with JUnit
Easy JUnit test of Elasticsearch 2018 version with embedded-elasticsearch
Understanding the MVC framework with server-side Java 3/4 Controller
[Java] Hello World with Java 14 x Spring Boot 2.3 x JUnit 5 ~
Test code using mock with JUnit (EasyMock center)
Make System.out a Mock with Spock Test Framework
Mixin test cases with JUnit 5 and default methods
How to perform UT with Excel as test data with Spring Boot + JUnit5 + DBUnit
Spring Framework multilingual support
Try JUnit test launch
I created an api domain with Spring Framework. Part 2
Integration Test with Gradle
1. Start Spring framework from 1
Spring Framework Summary-About DI
Spring with Kotorin --- 5. Actuator
Test the contents of an Excel file with JUnit
[Rails] Test with RSpec
Play Framework studying test
Test Nokogiri with Rspec.
WebAPI unit test and integration test with SpringBoot + Junit5, 4 patterns
Self-made Validation with Spring
I created an api domain with Spring Framework. Part 1
Automatically test with Gauge
Load test with JMeter
Spring with Kotorin ―― 1. SPRING INITIALIZR
Download with Spring Boot
MOCK constructors of other classes with Spring MVC + PowerMock + Junit
Create a Hello World web app with Spring framework + Jetty
Use Spring Test + Mockito + JUnit 4 for Spring Boot + Spring Retry unit tests
[Java] I want to test standard input & standard output with JUnit
Easy microservices with Spark Framework!
[Personal memo] About Spring framework
Generate barcode with Spring Boot
[Java] JUnit4 test case example
Hello World with Spring Boot
Java Config with Spring MVC
JUnit 5 fails with java.lang.NoSuchMethodError: org.junit.platform.launcher.Launcher.execute
Implement GraphQL with Spring Boot
Spring with Kotorin --8 Repository layer
Spring Framework self-study memo series_1