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
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
Perform transaction confirmation test with Spring Boot
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
How to test interrupts during Thread.sleep with JUnit
Easy JUnit test of Elasticsearch 2018 version with embedded-elasticsearch
[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] Configuration split
Spring Framework multilingual support
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
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
[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
About Spring Framework context error
Get started with Spring boot
Hello World with Spring Boot!
Spring with Kotorin --6 Asynchronous processing
Test Active Strage with RSpec
Run LIFF with Spring Boot
SNS login with Spring Boot
Spring Model View Controller Flow
File upload with Spring Boot
Spring Boot starting with copy
Spring with Kotorin ―― 7. Service layer