Stack
Test Code
•
@BeforeEach
@BeforeEach 어노테이션이 달린 구간은 테스트가 실행되기 전에 실행된다.
그래서 @Test 어노테이션이 붙어있는 메사드를 실행하기 전에 실행되는 동작을 설정 할 수 있다.
각 테스트는 독립적으로 실행되어야 하기 때문에 사용한다.
그래서 DB에서 데이터를 지우는 코드나 DB에 데이터를 넣는 코드 등이 이 구간에 들어간다.
•
Test Code에서 할 수 있는 것들
1.
테스트하는 코드를 만들어서 다음에도 실행할 수 있도록 한다.
2.
기능 설계
•
Test Code에서의 Method 자동 완성
option + enter 단축키를 사용한다. (Mac OS)
Method는 test Code에서만 만들도록 한다. ⇒ TDD
•
증감 연산자의 위치에 따른 변화
this.top++은 현재 값을 사용하고 난 후에 값을 넣는다. ex) i++ → 1이 올라감
--this.top은 현재 값에서 먼저 빼고 사용한다.
isEmpty()
•
테스트 코드를 먼저 만들고 메서드를 자동 완성한다.
@Test
void isEmpty() {
assertTrue(stack02.isEmpty()); // 비어 있으면 True
stack02.push(10);
assertFalse(stack02.isEmpty()); // 값이 들어 있으면 False
stack02.pop();
assertTrue(stack02.isEmpty()); // 값을 빼고난 후 True
}
Java
복사
public boolean isEmpty() {
boolean isEmpty = this.top == 0;
return isEmpty;
}
Java
복사
•
Stack이 비어있을 때 pop()을 하면?
ArrayOutOfIndexException 발생 (this.top이 (-)가 되므로)
실제 스택은 어떨까?
@Test
void realStack() {
Stack<Integer> stack = new Stack<>();
stack.pop();
}
Java
복사
⇒ EmptyStackException 발생
•
pop()에 예외 처리 추가
this.top이 (-)가 되지 않도록 스택이 비어 있을 때는 에러를 throw해서 더이상 진행이 안되도록 한다.
public int pop() {
if (this.isEmpty()) {
throw new EmptyStackException();
}
return this.arr[--this.top];
}
Java
복사
예외가 나면 테스트가 통과하지 않는다 ⇒ 테스트 코드에도 예외처리 추가
@Test
void pushAndPop() {
stack02.push(10);
stack02.push(20);
assertEquals(20, stack02.pop());
assertEquals(10, stack02.pop());
// st.pop() 비어있으면?
assertThrows(EmptyStackException.class, () -> { // 람다식
stack02.pop();
});
}
Java
복사
•
테스트 만들 때 원칙
1.
negative Test부터 → 실패할 것 같은 테스트부터 만드는 것이다.
2.
언제 실행해도 동일한 결과가 나오도록 테스트를 구성해야 한다.
peek()
•
pop()과 비슷하지만 값을 빼지는 않고 확인만 하는 기능이다. stack의 맨 위에 있는 값을 이용해 특정 조건 처리를 하고 싶을 때 필요하다.
•
테스트 코드를 먼저 만들고 메서드를 자동 완성 & pop()과 마찬가지로 예외 처리
@Test
void peek() {
// stack 이 비어있는데 peek()을 하면?
assertThrows(EmptyStackException.class, () -> {
stack02.peek();
});
stack02.push(10);
int peeked = stack02.peek();
assertEquals(10, peeked);
}
Java
복사
public int peek() {
if (this.isEmpty()) {
throw new EmptyStackException();
}
return this.arr[this.top - 1];
}
Java
복사
Spring
복습
1.
UserDao 만들기
2.
UserDaoTest 만들기
3.
ConnectionMaker Interface 만들기
4.
AwsConnectionMaker를 ConnectionMaker Interface 구현해서 만들기
5.
UserDao에 ConnectionMaker interface 의존하게 변경
6.
UserDao에 Constructor 2개 추가
7.
UserDaoFactory만들기
8.
UserDaoFactory에 awsUserDao() 메서드 만들기
9.
build.gradle에 mysql, spring-boot-starter-jdbc, spring-boot-starter-test Dependency 추가하기
10.
UserDaoTest에 ExtendsWith, ContextConfiguration 추가하기
11.
addAndGet() @Test 메서드 추가하기
12.
@Autowired ApplicationContext 추가하기
13.
테스트 메서드 구현
14.
통과하는지 확인
토비의 스프링 2장
•
.deleteAll()
DELETE FROM `likelion-db`.users 실행
executeUpdate(); - int 반환
public void deleteAll() throws ClassNotFoundException, SQLException {
Connection con = connectionMaker.makeConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM `likelion-db`.users");
ps.executeUpdate();
ps.close();
con.close();
}
Java
복사
•
.getCount()
SELECT COUNT(*) FROM `likelion-db`.users 실행
executeQuery(); - ResultSet 반환, 결과값을 받아오고 싶을 때 사용한다. ex) get()
public int getCount() throws ClassNotFoundException, SQLException {
Connection con = connectionMaker.makeConnection();
PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM `likelion-db`.users");
ResultSet rs = ps.executeQuery();
rs.next();
count = rs.getInt(1);
rs.close();
ps.close();
con.close();
return count;
}
Java
복사
•
결과값이 없을 때 Exception 처리
결과값이 없으면 rs.next()했을 때 ResultSet에 null값이 들어간다.
그래서 null이 아닐때(= 값이 있을 때)만 User 오브젝트를 생성하도록 수정한다. 그리고 User 오브젝트가 null인채로 끝나면 에러를 던진다.
public User get(String id) throws ClassNotFoundException, SQLException {
Connection con = connectionMaker.makeConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM `likelion-db`.users WHERE id = ?");
ps.setString(1, id);
ResultSet rs = ps.executeQuery();
User user = null;
if (rs.next()) { //rs.next()가 값이 있을 때만 User object 생성
user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password"));
}
// 열었던 순서 역순으로 닫기
rs.close();
ps.close();
con.close();
if (user == null) {
throw new EmptyResultDataAccessException(1); // 에러 던지기~!
}
return user;
}
Java
복사
◦
Test Code로 예외 처리가 잘 이루어지는지 확인
@Test
public void get() {
assertThrows(EmptyResultDataAccessException.class, () -> {
userDao.get("30"); // 없는 id를 get()함으로써 예외 처리 확인
});
}
Java
복사
•
@BeforeEach 사용
각 @Test 메서드가 실행 될 때마다 실행되는 코드가 있어서, 그 부분을 Junit에서 공통화시킬 수 있게 제공하는 기능인 @BeforeEach에 넣으면 편리하다.
@BeforeEach
void setUp() throws SQLException, ClassNotFoundException {
this.userDao = context.getBean("awsUserDao", UserDao.class);
userDao.deleteAll();
this.user1 = new User("1", "seohyeon", "1111");
this.user2 = new User("2", "seohyeon", "2222");
this.user3 = new User("3", "seohyeon", "3333");
}
Java
복사
토비의 스프링 3장
•
예외 처리가 없을 때의 리소스 반환 문제
public void deleteAll() throws ClassNotFoundException, SQLException {
Connection con = connectionMaker.makeConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM `likelion-db`.users");
ps.executeUpdate();
ps.close();
con.close();
}
Java
복사
위의 코드는 Local에서는 전혀 문제가 없다. 하지만 서버환경에서는 서버가 일찍 Down될 수 있는 문제점이 있다. 이유가 무엇일까?
바로 ps.close()와 con.close()가 실행되지 않는 치명적인 문제이다.
1초에 10건, 1분이면 600건, 1시간이면 3600건의 Call이 발생하는데 저 코드 대로라면, connection이 닫히지 않으며 그 결과 서버가 다운된다.
•
Connection, PreparedStatement에 에러가 나더라도 ps.close(), con.close()를 실행하기 위한 처리
◦
.deleteAll()
public void deleteAll() {
Connection con = null;
PreparedStatement ps = null;
try {
con = connectionMaker.makeConnection();
ps = con.prepareStatement("DELETE FROM `likelion-db`.users");
ps.executeUpdate();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (con != null) {
try {
con.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
Java
복사
◦
.getCount()
public int getCount() {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
int count = 0;
try {
con = connectionMaker.makeConnection();
ps = con.prepareStatement("SELECT COUNT(*) FROM `likelion-db`.users");
rs = ps.executeQuery();
rs.next();
count = rs.getInt(1);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (con != null) {
try {
con.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
return count;
}
Java
복사