송민준의 개발노트

Junit 본문

웹/Spring Framework

Junit

송민준 2020. 1. 8. 17:50

Junit 이란?

1. Java에서 독립된 단위테스트를 지원해주는 프레임워크이다.

2. @RunWith는 JUnit 프레임워크의 테스트 실행방법을 확장할 때 사용하는 어노테이션이다.

   @RunWith(SpringJUnit4ClassRunner.class)

    - SpringJUnit4ClassRunner라는 JUnit용 테스트 컨텍스트 프레임워크 확장 클래스를 지정해 주면

       JUnit이 테스트를 진행하는 중 테스트가 사용할 어플리케이션 컨텍스트를 만들고 관리하는 작업을 해준다.

3. @ContextConfiguration(locations="/applicationContext")

    - 자동으로 만들어줄 어플리케이션 컨텍스트의 설정파일의 위치를 지정할 때 사용한다.

 

예제 경로

 

 

pom.xml : junit이 최초에 4.7로 기본 세팅이 되어 있는데 이대로 실행하면 에러가 나고 4.12로 하향시켜줘야 한다.

        <dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>

_1DataSourceTest.java

package com.portfordev.pro;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import javax.sql.DataSource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class _1DataSourceTest {
	@Autowired
	private DataSource dataSource;
	
	@Test
	public void testConnection() throws Exception {
		try(Connection conn = dataSource.getConnection()) {
			System.out.println("확인용 conn : " + conn);
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	@Test
	public void testQuery() throws Exception {
		try(Connection conn = dataSource.getConnection()) {
			PreparedStatement pstmt = conn.prepareStatement(
					"select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss'), user_name "
					+ "from user_table");
			ResultSet rs = pstmt.executeQuery();
			while(rs.next()) {
				System.out.println("현재 시각 : " + rs.getString(1));
				System.out.println("사용자명 : " + rs.getString(2));
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
}

root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- Root Context: defines shared resources visible to all other web components -->
	
	 <!-- dev local dataSource -->
	<bean class ="org.apache.commons.dbcp.BasicDataSource" id="dataSource" destroy-method="close">
		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
		<property name="username" value="scott"/>
		<property name="password" value="TIGER"/>
	</bean>
	<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
		<property ref="dataSource" name="dataSource"/>
		<property name="configLocation" value="classpath:util/SqlMapConfig.xml"/>
	</bean>
	
	<bean class="org.mybatis.spring.SqlSessionTemplate" id="sqlSessionTemplate">
		<constructor-arg ref="sqlSessionFactory" index="0"/>
	</bean>
	
	<!-- 트랜잭션 처리 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<constructor-arg ref="dataSource"/>
	</bean>
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<!-- 1024*1024*10 bytes : 10MB -->
		<property name="maxUploadSize" value="10485760" />
	</bean>
</beans>

SqlMapConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<setting name="jdbcTypeForNull" value="NULL"/>
	</settings>
	<typeAliases>
		<typeAlias alias="member" type="com.portfordev.pro.domain.Member"/>
	</typeAliases>
	<mappers>
		<mapper resource="sql/member.xml"/>
	</mappers>
</configuration>

sql파일

/* test 파일 */
create table user_table(
	today date,
	user_name varchar2(20)
);

insert into user_table values(sysdate, '홍길동');

select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss'), user_name from user_table;

create table member_test3(
	num varchar2(20),
	name varchar2(30),
	email varchar2(20),
	tel varchar2(20),
	addr varchar2(30),
	today date
);

 

 

Run as Junit 결과(RDS와 테스트를 해보니 속도 차이가 3배 정도 난다.)

----------------------mybatis 연결-------------------------------------

package com.portfordev.pro;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class _1DataSourceTest {
	@Autowired
	private SqlSessionFactory sqlSessionFactory;
	
	@Test
	public void testConnection() throws Exception {
		System.out.println("~~~~sqlSessionFactory : "+
					sqlSessionFactory);
		//sqlSessionFactory : org.apache.ibatis.session.defaults.Default
		//만약에 root-context.xml 파일에 설정한 SqlSessionFactoryBean의 설정
	}
	
	@Test
	public void testQuery() throws Exception {
		try(SqlSession sqlsession = sqlSessionFactory.openSession()) {
			System.out.println("~~~sqlsession : " + sqlsession);
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
}

---------------------------------mybatis 활용----------

1. 먼저 org.slf4j 패키지의 Logger, LoggerFactory 클래스를 import를 해야한다.

   import org.slf4j.Logger;

   import org.slf4j.LoggerFactory;

2. LoggerFactory 클래스의 getLogger() 메소드를 통해 Logger 객체를 생성한다.

3. .class로 클래스 정보를 넘겨주면 그 클래스에 대한 로그를 실행한다.

 

member.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="Members">
	<insert id="insert" parameterType="MyBatisTestVO2">
		insert into member_test3 
		values(
			(select nvl(max(num),0)+1 from member_test3), 
			#{name}, #{email}, #{tel}, #{addr}, sysdate
		)
	</insert>
</mapper>

_1DataSourceTest.java

package com.portfordev.pro;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.portfordev.pro.domain.MyBatisTestVO2;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class _1DataSourceTest {
	private static final Logger logger
				= LoggerFactory.getLogger(_1DataSourceTest.class);
	
	@Autowired
	private SqlSessionTemplate sqlsession;
	
	@Test
	public void testCreate() throws Exception {
		MyBatisTestVO2 vo = new MyBatisTestVO2();
		vo.setName("D반");
		vo.setEmail("D@gmailc.om");
		vo.setTel("010010");
		vo.setAddr("서울시 종로구");
		
		int n = sqlsession.insert("Members.insert", vo);
		logger.info("~~~~~~~~~~~~~~n : " + n + "~~~~~~~~~~~~~~~~~~");
	}
	
}

SqlMapConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<setting name="jdbcTypeForNull" value="NULL"/>
	</settings>
	<typeAliases>
		<typeAlias alias="MyBatisTestVO2" type="com.portfordev.pro.domain.MyBatisTestVO2"/>
	</typeAliases>
	<mappers>
		<mapper resource="sql/member.xml"/>
	</mappers>
</configuration>

MyBatisTestVO2.java

package com.portfordev.pro.domain;

import java.sql.Date;

import lombok.Data;
@Data
public class MyBatisTestVO2 {
	private String num;
	private String name;
	private String email;
	private String tel;
	private String addr;
	private Date date;
}

 

 

결과

-----------------------조회-------------------------------------

_1DataSourceTest.java  에서 select 할 test코드 추가

package com.portfordev.pro;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.portfordev.pro.dao.MemberDAO;
import com.portfordev.pro.domain.MyBatisTestVO2;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class _1DataSourceTest {
	private static final Logger logger
				= LoggerFactory.getLogger(_1DataSourceTest.class);
	
	@Autowired
	private SqlSessionTemplate sqlsession;
	
	
	
	//@Test
	public void testCreate() throws Exception {
		MyBatisTestVO2 vo = new MyBatisTestVO2();
		vo.setName("D반");
		vo.setEmail("D@gmailc.om");
		vo.setTel("010010");
		vo.setAddr("서울시 종로구");
		
		int n = sqlsession.insert("Members.insert", vo);
		logger.info("~~~~~~~~~~~~~~n : " + n + "~~~~~~~~~~~~~~~~~~");
	}
	
	@Test
	public void testSelect() throws Exception {
		
		MyBatisTestVO2 vo = sqlsession.selectOne("Members.testidcheck", "1");
		System.out.println(vo.toString());
	}
	
}

 

member.xml

	 <select id="testidcheck" parameterType="String" resultType="MyBatisTestVO2">
		select *
		from member_test3
		where num = #{inputid}
	</select>

 

결과

------------------------selectList 써보기-------------------------------------

_1DataSourceTest.java 에서 selectList 할 test코드 추가

    @Test
	public void testAllSelect() throws Exception {
		List<MyBatisTestVO2> list = sqlsession.selectList("Members.selectAll");
		list.forEach(item ->{
			System.out.println(item);
		});
	}

member.xml

<select id ="selectAll" resultType="MyBatisTestVO2">
		select * from member_test3
	</select>

결과

-----------------------------톰캣없이 Controller 테스트 --------------------------------

단위 테스트를 위해서 SpringJUnit4ClassRunner라는 Runner클래스를 사용한다.
@RunWith(SpringJUnit4ClassRunner.class)

WAS 없이 MVC 패턴의 COntroller를 단위 테스트 하기 위해서는

@WebAppConfiguration을 사용해야만 한다.(스프링 MVC 테스트할때 사용하는것)

이 어노테이션을 붙이면 Controller 및 web환경에 사용되는 빈들을 자동으로 생성하여 등록합니다.

_4MyBatisTestControllerTestDEPT.java

package com.portfordev.pro;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;


@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/root-context.xml",
"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
public class _4MyBatisTestControllerTestDEPT {
	private static final Logger logger
 	    = LoggerFactory.getLogger(_4MyBatisTestControllerTestDEPT.class);
	
	@Autowired
	private WebApplicationContext wac;
	
	//웹 어플리케이션을 어플리케이션 서버에 배포하지 않고도 스프링 MVC 동작을 재현할 수 있는 클래스
	private MockMvc mockMvc;
	
	// 매번 테스트를 진행할때 마다 테스트 하기전 MockMvc mockMvc 객체를 만들어 주기 위해
	// Before 어노테이션으로 setup() 메소드를 실행
	@Before
	public void setup() {
		this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
		logger.info("setup~~~~");
	}
	
	@Test
	public void testMyBatisTest() throws Exception {
		logger.info("===testMybatisTest() =====");
		try {
			// .perform()을 이용하면 매핑 url로 request(요청)합니다.
			// .andExpect를 이용해서 다양하게 검증할 수 있다.
			// status().isOk()는 응답을 받아 HttpStatus 코드가 "200"인지 체크할 수 있다.
			mockMvc.perform(post("/mybatistest/mybatisDeptinsert")
					.param("deptno","55")
					.param("dname","Dclass")
					.param("loc", "Seoul2"))
			.andDo(print())
			.andExpect(status().isOk()) // 응답을 받아 HttpStatus 코드가 200인지
			//.andExpect(status().isForbidden()) // 403
			.andExpect(model().attributeExists("result")) //result attribute가
			.andExpect(view().name("memberRegisterDept")); //view의 이름이 memberRegisterDept인지
			logger.info("~~~ 수행 성공 ~~~");
		} catch(Exception e) {
			logger.error(">> 수행 실패 : " + e.getMessage());
		}
	}
}

controller.java

	@RequestMapping(value = "/mybatistest/mybatisDeptinsert", method = {RequestMethod.POST})
	public ModelAndView mybatisDeptinsert(HttpServletRequest request, ModelAndView mv) {
		try {
			String deptno = request.getParameter("deptno");
			String loc = request.getParameter("loc");
			String dname = request.getParameter("dname");
			
			HashMap<String, String> paraMap = new HashMap<String, String>();
			paraMap.put("deptno", deptno);
			paraMap.put("loc", loc);
			paraMap.put("dname", dname);
			
			int n = memberservice.memberRegisterDept(paraMap);
			
			String result = "";
			if(n == 1)
				result = "삽입 성공!!";
			else
				result = "삽입 실패!!";
			mv.addObject("result", result);
			mv.setViewName("memberRegisterDept");
		} catch(Exception e) {
			mv.addObject("error", "상ㅂ입 중 오류가 발생했습니다.");
			mv.setViewName("error");
		}
		return mv;
	}

member.xml

<insert id = "registerDept" parameterType = "map">
		INSERT INTO DEPT
		VALUES(#{deptno}, #{dname}, #{loc})
	</insert>

MemberDAO.java

	public int memberRegisterDept(HashMap<String, String> map) {
		return sqlSession.insert("Members.registerDept", map);
	}

 

MemberServiceImpl.java

	@Override
	public int memberRegisterDept(HashMap<String, String> map) {
		return dao.memberRegisterDept(map);
	}

결과

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /mybatistest/mybatisDeptinsert
       Parameters = {deptno=[55], dname=[Dclass], loc=[Seoul2]}
          Headers = {}
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.portfordev.pro.controller.MemberController
           Method = public org.springframework.web.servlet.ModelAndView com.portfordev.pro.controller.MemberController.mybatisDeptinsert(javax.servlet.http.HttpServletRequest,org.springframework.web.servlet.ModelAndView)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = memberRegisterDept
             View = null
        Attribute = modelAndView
            value = ModelAndView [view="memberRegisterDept"; model={result=삽입 성공!!}]
           errors = []
        Attribute = result
            value = 삽입 성공!!

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {Content-Language=[en]}
     Content type = null
             Body = 
    Forwarded URL = /WEB-INF/views/memberRegisterDept.jsp
   Redirected URL = null
          Cookies = []
INFO : com.portfordev.pro._4MyBatisTestControllerTestDEPT - ~~~ 수행 성공 ~~~

 

' > Spring Framework' 카테고리의 다른 글

(Spring) kakao 소셜 로그인(SSO, oauth)  (0) 2020.01.10
프로젝트 import 후 tomcat에 add 안될 때  (0) 2020.01.09
mybatis null값 처리  (0) 2020.01.08
transaction  (0) 2020.01.03
AOP(Aspect Oriented Programming)  (0) 2020.01.02