Maven 외부 라이브러리 추가하기
외부 라이브러리를 사용하기 위해서 pom.xml에 <dependencies> 태그 안에 dependency를 추가한다.
<dependencies>는 프로젝트가 의존하는 라이브러리들의 정보를 담는 태그이다.
*자주 사용하는 라이브러리
라이브러리 | 기능 | dependency |
Apache Commons DBCP » 2.9.0 |
DataSource 구현체 | <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.9.0</version> </dependency> |
MySQL Connector Java » 8.0.32 |
jdbc mysql 드라이버 | <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.32</version> </dependency> |
SLF4J API Module » 2.0.6 |
logger 인터페이스 | <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.6</version> </dependency> |
Logback Classic Module » 1.3.5 |
logger slf4j 구현체 | <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.5</version> </dependency> |
Jackson Databind » 2.14.2 |
json parser | <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.2</version> </dependency> |
JUnit » 4.13.2 |
테스트 자동화 | <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> |
JUnit
1. JUnit
*Java에서 독립된 단위테스트(Unit Test)를 지원해주는 프레임워크
*테스트 결과는 Test클래스로 개발자에게 테스트 방법 및 클래스의 History를 공유 가능
*단정(assert) 메서드로 테스트 케이스의 수행 결과를 판별
*어노테이션으로 간결하게 지원(JUnit4부터)
2. JUnit 어노테이션
어노테이션 | 설명 |
@BeforeClass | @BeforeClass 어노테이션은 @Test 메소드보다 먼저 한번만 수행되어야 할 경우에 사용하면 된다. |
@Before | @Before가 선언된 메서드는 @Test 메서드가 실행되기 전에 반드시 실행되어진다. @Test메서드에서 공통으로 사용하는 코드를 @Before 메서드에 선언하여 사용하면 된다. |
@Test | @Test가 선언된 메서드는 테스트를 수행하는 메소드가 된다. JUnit은 각각의 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 원칙으로 @Test마다 객체를 생성한다. |
@After | @After가 선언된 메서드는 @Test 메소드가 실행된 후 실행된다. |
@AfterClass | @AfterClass 어노테이션은 @Test 메소드 보다 나중에 한번만 수행되어야 할 경우에 사용하면 된다. |
@Ignore | @Ignore가 선언된 메서드는 테스트를 실행하지 않게 한다. |
3. JUnit 메소드
메소드 | 설명 |
assertEquals(a,b); | 객체 a,b의 값이 일치함을 확인한다. |
assertArrayEquals(a,b); | 배열 a,b의 값이 일치함을 확인한다. |
assertSame(a,b); | 객체 a,b가 같은 객체임을 확인한다. 두 객체의 레퍼런스가 동일한가를 확인한다. |
assertTrue(a); | 조건 a가 참인가 확인한다. |
assertNotNull(a); | 객체 a가 null이 아님을 확인한다. |
4. 예제
package bookapp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class TestPractice {
@Test
public void testSum() {
int a = 10;
int b = 20;
int sum = a + b;
assertEquals(30, sum); // '값'이 같으면 통과
}
@Test
public void testNotEqual() {
int a = 10;
int b = 20;
int sum = a + b;
assertNotEquals(30, sum); // '값'이 다르면 통과
}
@Test
public void testSame() {
Object a = new Object();
Object b = new Object();
assertSame(a, b); // '같은참조'면 통과
}
@Test
public void testNotSame() {
Object a = new Object();
Object b = new Object();
assertNotSame(a, b); // '같은참조'가 아니면 통과
}
@Test
public void testBoolean() {
boolean result = true;
assertTrue(result); // boolean 자료 값이 'true'면 통과
}
}
JUnit DB Test
*Book.java
package bookapp;
public class Book {
private int no;
private String title;
private int price;
public Book() {}
public Book(int no, String title, int price) {
super();
this.no = no;
this.title = title;
this.price = price;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Book [no=" + no + ", title=" + title + ", price=" + price + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + no;
result = prime * result + price;
result = prime * result + ((title == null) ? 0 : title.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Book other = (Book) obj;
if (no != other.no)
return false;
if (price != other.price)
return false;
if (title == null) {
if (other.title != null)
return false;
} else if (!title.equals(other.title))
return false;
return true;
}
}
*BookService.java
package bookapp;
import java.util.List;
public interface BookService {
Book create(Book book);
List<Book> read();
Book update(Book book);
Book delete(int no);
}
*BookServiceImpl.java
package bookapp;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import bookapp.dbutil.ConnectionProvider;
public class BookServiceImpl implements BookService {
private BookDAO dao;
public BookServiceImpl(BookDAO dao) {
super();
this.dao = dao;
}
@Override
public Book create(Book book) {
Connection conn = null;
try {
conn = ConnectionProvider.getConnection();
conn.setAutoCommit(false);
int key = dao.insert(conn, book);
conn.commit();
book.setNo(key);
return book;
} catch (RuntimeException | SQLException e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return null;
}
@Override
public List<Book> read() {
Connection conn = null;
try {
conn = ConnectionProvider.getConnection();
return dao.select(conn);
} catch (RuntimeException | SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return null;
}
@Override
public Book update(Book book) {
Connection conn = null;
try {
conn = ConnectionProvider.getConnection();
conn.setAutoCommit(false);
dao.update(conn, book);
conn.commit();
return book;
} catch (RuntimeException | SQLException e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return null;
}
@Override
public Book delete(int no) {
Connection conn = null;
try {
conn = ConnectionProvider.getConnection();
conn.setAutoCommit(false);
Book book = dao.selectByNo(conn, no);
dao.delete(conn, no);
conn.commit();
return book;
} catch (RuntimeException | SQLException e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return null;
}
}
*BookDAO.java
package bookapp;
import java.sql.Connection;
import java.util.List;
public interface BookDAO {
int insert(Connection conn, Book book);
List<Book> select(Connection conn);
Book selectByNo(Connection conn, int no);
int update(Connection conn, Book book);
int delete(Connection conn, int no);
}
*BookDAOImpl.java
package bookapp;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import bookapp.dbutil.ConnectionProvider;
public class BookDAOImpl implements BookDAO {
@Override
public int insert(Connection conn, Book book) {
String sql = "INSERT INTO book (title, price) VALUES (?, ?)";
try (PreparedStatement stmt = conn.prepareStatement
(sql, Statement.RETURN_GENERATED_KEYS )) {
stmt.setString(1, book.getTitle());
stmt.setInt(2, book.getPrice());
int result = stmt.executeUpdate();
if (result == 1) {
try (ResultSet rs = stmt.getGeneratedKeys()) {
rs.next();
return rs.getInt(1);
}
} else {
throw new RuntimeException("행이 생성되지 않았습니다. ");
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("추가 작업 중 예외 발생", e);
}
}
@Override
public List<Book> select(Connection conn) {
String sql = "SELECT * FROM book";
try (PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
List<Book> list = new ArrayList<>();
while (rs.next()) {
list.add(resultMapping(rs));
}
return list;
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("조회 작업 중 예외 발생", e);
}
}
private Book resultMapping(ResultSet rs) throws SQLException {
Book book = new Book();
book.setNo(rs.getInt("no"));
book.setTitle(rs.getString("title"));
book.setPrice(rs.getInt("price"));
return book;
}
@Override
public Book selectByNo(Connection conn, int no) {
String sql = "SELECT * FROM book WHERE no = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, no);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return resultMapping(rs);
}
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("조회 작업 중 예외 발생", e);
}
return null;
}
@Override
public int update(Connection conn, Book book) {
String sql = "UPDATE book SET title = ?, price = ? WHERE no = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, book.getTitle());
stmt.setInt(2, book.getPrice());
stmt.setInt(3, book.getNo());
return stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("수정 작업 중 예외 발생", e);
}
}
@Override
public int delete(Connection conn, int no) {
String sql = "DELETE FROM book WHERE no = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, no);
return stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("삭제 작업 중 예외 발생", e);
}
}
}
*pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>bookapp</groupId>
<artifactId>bookapp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.9.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
<configuration>
<warSourceDirectory>webapp</warSourceDirectory>
</configuration>
</plugin>
</plugins>
</build>
</project>
*ConnectionProvider.java
package bookapp.dbutil;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
public class ConnectionProvider {
private static DataSource dataSource;
static {
BasicDataSource ds = new BasicDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/my_db");
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUsername("root");
ds.setPassword("root");
dataSource = ds;
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
*데이터베이스 작업 구현 클래스 테스트하기(테스트 목적의 폴더 구성, 배포할때 제외하기)
(1) 프로젝트 우클릭 → Properties → java Build Path → Source 탭 → Add Folder... → create New Folder - Test 로 이름 짓고 완료
(2) Contains test sources 더블 클릭 → Yes로 변경
(3) Output folder 클릭 → target 폴더 안 test폴더(test-classes)로 변경
*TestBookDAO.java
package bookapp;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import bookapp.dbutil.ConnectionProvider;
public class TestBookDAO {
private static BookDAO dao;
private Connection conn;
@BeforeClass
public static void setup() {
dao = new BookDAOImpl();
}
@Before
public void makeConn() throws SQLException {
conn = ConnectionProvider.getConnection();
conn.setAutoCommit(false);
}
@Test
public void daoInit() {
// DAO 객체 생성 테스트
BookDAO dao = new BookDAOImpl();
System.out.println("생성 결과" + (dao != null));
}
@Test
public void daoInsert() {
// insert 메소드 테스트...
Book book = new Book();
book.setTitle("테스트용책이름");
book.setPrice(50000);
BookDAO dao = new BookDAOImpl();
int result = dao.insert(conn, book);
assertNotEquals(0, result);
}
@Test
public void daoSelect() {
List<Book> list = dao.select(conn);
assertNotNull(list);
}
@Test
public void daoUpdate() {
int result = dao.update(conn, new Book(2, "이번책 바꾸기", 3000));
assertNotEquals(0, result);
}
@Test
public void daoDelete() {
int result = dao.delete(conn, 2);
assertNotEquals(0, result);
}
@After
public void rollbackAndClose() throws SQLException {
conn.rollback();
conn.close();
}
}
*TestBookService.java
package bookapp;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
public class TestBookService {
@Test
public void create() {
BookService service = new BookServiceImpl(new BookDAOImpl());
Book book = new Book();
book.setTitle("서비스 객체 테스트");
book.setPrice(30000);
Book result = service.create(book);
assertNotNull(result);
}
}