기존 코드 변경 사항
arr의 타입을 int[] ➡ Integer[]로 변경
👉 다형성, int[]면 배열의 초기값 0, Integer[]이면 null
isEmpty()
Stack이 비어있다면 true, 아니라면 false 리턴
public boolean isEmpty() {
return this.top == 0 ? true : false;
}
🔴 pop()이 비어있을 때 pop을 하면 오류 발생
👉 isEmpty()로 해결가능 (Stack이 비어있을 때 throw new EmptyStackException으로 처리)
peek()
Stack의 맨 위의 값을 가져옴 (top - 1의 값)
👉 peek 역시 Stack이 비어있을 때 EmptyStackException으로 처리
cannot find symbol class 오류 발생
👉 build의 rebuild Project 실행
실습
Modern Java 표현
🟢 왼쪽 코드를 오른쪽으로 사용

JUnit 작동방식
- 테스트 클래스에서 @Test가 붙은 public이고 void형이며 파라미터가 없는 테스트 메소드를 모두 찾는다.
- 테스트 클래스의 오브젝트를 하나 만든다.
- @Before가 붙은 메소드가 있으면 실행한다.
- @Test가 붙은 메소드를 하나 호출하고 테스트 결과를 저장해둔다.
- @After가 붙은 메소드가 있으면 실행한다. (의미는 try catch에서 finally의미)
- 나머지 테스트 메소드에 대해 2~5번을 반복한다.(테스트마다 Before 실행)
- 모든 테스트의 결과를 종합해서 돌려준다.
🟢 왜 2번에서 계속 오브젝트를 만들까? 👉 각 테스트가 독립적으로 실행됨을 보장해주기 위해
과정
- UserDao복붙
- UserDaoTest만들기
- ConnectionMaker Interface만들기
- AwsConnectionMaker를 ConnectionMaker Interface 구현해서 만들기
- UserDao에 ConnectionMaker interface의존하게 변경
- UserDao에 Constructor 2개 추가
- UserDaoFactory만들기
- UserDaoFactory에 awsUserDao() 메소드 만들기
- build.gradle에 mysql, spring-boot-starter-jdbc, spring-boot-starter-test Dependency추가 하기
- UserDaoTest에 ExtendsWith, ContextConfiguration추가하기
- addAndGet() @Test 메소드 추가하기
- @Autowired ApplicationContext추가하기
- 테스트 메소드 구현
- 통과하는지 확인
deleteAll(), getCount()추가 및 테스트
🟢 deleteAll()
public void deleteAll() throws SQLException, ClassNotFoundException {
Connection conn = connectionMaker.getConnection();
PreparedStatement ps = conn.prepareStatement("DELETE FROM users");
ps.executeUpdate();
ps.close();
conn.close();
}
🟢 getCount()
public int getCount() throws SQLException, ClassNotFoundException {
Connection conn = connectionMaker.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT count(*) FROM users");
ResultSet rs = ps.executeQuery();
rs.next();
int count = rs.getInt(1);
rs.close();
ps.close();
conn.close();
return count;
}
🟢 테스트 코드
1. userDao의 DB에 있는 users 테이블 데이터 삭제(deleteAll)
2. 이후 table의 개수를 0과 비교
3. 데이터를 하나씩 추가하며 비교하여 테스트
@Test
void count() throws SQLException, ClassNotFoundException {
UserDao userDao = context.getBean("awsUserDao", UserDao.class);
User user1 = new User("1", "박성철", "1234");
User user2 = new User("2", "이길원", "2345");
User user3 = new User("3", "박범진", "3456");
userDao.deleteAll();
assertEquals(0, userDao.getCount());
userDao.add(user1);
assertEquals(1, userDao.getCount());
userDao.add(user2);
assertEquals(2, userDao.getCount());
userDao.add(user3);
assertEquals(3, userDao.getCount());
}
addAndGet 개선
🟢 User 객체를 생성하여 findbyId 및 asserEquals할때 getter함수를 통해 값을 가져오도록 개선
@Test
void addAndGet() throws SQLException, ClassNotFoundException {
UserDao userDao = context.getBean("awsUserDao", UserDao.class);
User user1 = new User("1", "박성철", "1234");
userDao.add(user1);
assertEquals(1, userDao.getCount());
User selectedUser = userDao.findbyId(user1.getId());
assertEquals(user1.getName(), selectedUser.getName());
assertEquals(user1.getPassword(), selectedUser.getPassword());
}
findbyId 예외처리
🟢 findbyId로 없는 Id를 찾을 경우 예외 발생
👉 rs가 있을 경우에만 값을 가져오고, 없을 경우에는 throw로 예외 처리
public User findbyId(String id) throws SQLException, ClassNotFoundException {
Connection conn = connectionMaker.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT * from users where id = ?");
ps.setString(1, id);
ResultSet rs = ps.executeQuery();
User user = null;
if(rs.next()){
user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password"));
}
rs.close();
ps.close();
conn.close();
if (user == null) throw new EmptyResultDataAccessException(1);
return user;
}
🔴 테스트코드 개선(@BeforeEach, @Autowired 활용)
1. @Autowired에 변수 선언
2. @BeforeEach에는 모든 Test가 중복되는 Connection과 변수 초기화
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = UserDaoFactory.class)
class UserDaoTest {
@Autowired
private ApplicationContext context;
private UserDao userDao;
private User user1;
private User user2;
private User user3;
@BeforeEach
void setUp() {
userDao = context.getBean("awsUserDao", UserDao.class);
user1 = new User("1", "박성철", "1234");
user2 = new User("2", "이길원", "2345");
user3 = new User("3", "박범진", "3456");
}
@Test
void addAndGet() throws SQLException, ClassNotFoundException {
userDao.deleteAll();
assertEquals(0, userDao.getCount());
userDao.add(user1);
assertEquals(1, userDao.getCount());
User selectedUser = userDao.findbyId(user1.getId());
assertEquals(user1.getName(), selectedUser.getName());
assertEquals(user1.getPassword(), selectedUser.getPassword());
}
@Test
void count() throws SQLException, ClassNotFoundException {
userDao.deleteAll();
assertEquals(0, userDao.getCount());
userDao.add(user1);
assertEquals(1, userDao.getCount());
userDao.add(user2);
assertEquals(2, userDao.getCount());
userDao.add(user3);
assertEquals(3, userDao.getCount());
}
}
deleteAll(), getCount() 예외처리
🟢 예외 발생 원인 : Local에서는 문제가 없지만, 서버 환경에서는 서버가 일찍 Down될 수 있음
👉 ps.close, c.close가 실행되지 않아 계속되어 ps, c가 생성될 수 있음
🟢 try catch문을 사용하여 예외처리
👉 try catch 단축키 : Ctrl + Alt + T
🔴 rs, ps, conn가 null이 아니면 close 처리
public void deleteAll() throws SQLException, ClassNotFoundException {
Connection conn = null;
PreparedStatement ps = null;
try{
conn = connectionMaker.getConnection();
ps = conn.prepareStatement("DELETE FROM users");
ps.executeUpdate();
} catch (SQLException e) {
throw e;
} finally { // error가 나도 실행되는 블럭
if(ps != null) {
try {
ps.close();
} catch (SQLException e) {
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
}
}
}
}
public int getCount() throws SQLException, ClassNotFoundException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = connectionMaker.getConnection();
ps = conn.prepareStatement("SELECT count(*) FROM users");
rs = ps.executeQuery();
rs.next();
return rs.getInt(1);
} catch (SQLException e) {
throw e;
} finally {
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
}
}
if(ps != null) {
try {
ps.close();
} catch (SQLException e) {
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
}
}
}
}