포스팅에 앞서
이 책이 도움이 많이 되었다는 말을 정말 많이 들었는데
이번에 쉬기 전에 빌려서 드디어 첫 장을 펴보았다.
(+지금 보고 있는 게 2016에 출간된 책)
일단 첫부분은 아마도 김영한님의 Spring입문 강의에서 봤던 내용들로
되어있다.
1. 기초-중급 단계의 과정들과 책 소개
+깃헙 https://github.com/slipp/jwp-book
책 정리는 따로 안할 예정이고 오늘 배운 내용 바로 정리
2. 중급 단계
* 테스트와 리팩토링
: main() 메소드를 활용한 테스트의 문제점
public class Calculator {
int add(int i, int j) {
return i+j;
}
int substract(int i, int j) {
return i-j;
}
int multiply(int i, int j) {
return i*j;
}
int divide(int i, int j) {
return i/j;
}
public static void main(String[] args){
Calculator cal = new Calculator();
System.out.println(cal.add(8,4);
System.out.println(cal.substaract(8,4);
System.out.println(cal.multiply(8,4);
System.out.println(cal.divide(8,4);
}
일반의 계산기 코드(위)는 실제 서비스를 담당하는 프로덕션 코드 production code
정상적으로 동작하는지 확인하기 위한 테스트 목적인 main()으로 나뉜다.
사실, main()은
- 프로그래밍을 실행하기 위한 목적
- 프로덕션 코드가 정상적으로 동작하는지 확인하는 테스트 목적
두개가 있는데 위와 같은 코드(2번 목적에 해당하는 코드와 프로덕션 코드가 합쳐진)는 좋은 코드가 아니다.
이 코드를 프로덕션 코드와 테스트 코드로 나눌 수 있다.
프로덕션 코드(production code)
public class Calculator {
int add(int i, int j) {
return i+j;
}
int substract(int i, int j) {
return i-j;
}
int multiply(int i, int j) {
return i*j;
}
int divide(int i, int j) {
return i/j;
}
}
테스트 코드(test code)
public class test{
public static void main(String[] args){
Calculator cal = new Calculator();
System.out.println(cal.add(8,4);
System.out.println(cal.substaract(8,4);
System.out.println(cal.multiply(8,4);
System.out.println(cal.divide(8,4);
}
}
좋은 코드가 아닌 이유는
하나의 main()메소드에서 프로덕션 코드의 여러 메소드를 동시에 테스트 하는 것은 프로덕션 코드의 복잡도가 증가하면 증가할수록 main() 메소드의 복잡도도 증가하고 결과적으로 main()메소드를 유지하는데 부담이 된다.
테스트 코드를 메소드별로 분리해 정리한다면
public class Test{
public static void main(String[] args){
Calculator cal = new Calculator();
add(cal);
substract(cal);
multiply(cal);
divide(cal);
}
private static void add(Calculator cal) {
System.out.println(cal.add(8,4));
}
private static void substract(Calculator cal){
System.out.println(cal.substract(8,4));
}
private static void multiply(Calculator cal){
System.out.println(cal.substract(8,4));
}
private static void divide(Calculator cal){
System.out.println(cal.substract(8,4));
}
}
가 되지만 이또한 완벽한 해결책이 아니다.
내가 구현하는 메소드에만 집중하고 싶은데 이건 클래스가 가지고 있는 모든 메소드를 테스트 할 수 밖에 없다.
이러한 main()메소드를 활용한 테스트의 문제점을 해결하기 위해 등장한 라이브러리가
JUnit이다.
로직 실행 후 결과 값을 확인을 프로그래밍을 통해 자동화하는 것이 가능하다.
이클립스 환경에서의 JUnit(https://www.nextree.co.kr/p11104/) : 이제 이클립스는 잘 안열어서 혹시나 프로젝트하게 되면 참고할려고
JUnit 활용한 단위 테스트 코드 작성법(https://mangkyu.tistory.com/144) : 깔끔하게 정리되어 있어서 링크를 걸어둔다.
JUnit
import org.junit.Test;
public class Test{
@Test
public void add() {
Calculator cal =new Calculator();
System.out.println(cal.add(8,4));
}
@Test
public void substract() {
Calculator cal =new Calculator();
System.out.println(cal.substract(8,4));
}
}
assertEquals()메소드 활용
static메소드라 import static으로 메소드를 import한 후에 구현할 수 있다.
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class Test{
@Test
public void add() {
Calculator cal = new Calculator();
assertEquals(12, vcacal.add(8,4));
}
@Test
public void substract() {
Calculator cal =new Calculator();
assertEquals(2, cal.substract(8,4));
}
}
assertEquals()의 첫번째 인자는 기대하는 결과값 , 두번째 인자는 실행한 결과값이다.
실행하면,
초록색 : 성공
빨간색 : 실패
assert 클래스
- assertEquals()
- assertTrue() : true 확인
- assertFalse() : false 확인
- assertNull(), assertNotNull() : null 확인
- assertArrayEquals() : 배열값이 같은지 검증
JUnit의 Assert를 사용하기보다 테스트의 의도를 더 쉽게 파악할 수 있는 기능을 제공하는 AssertJ 도 많이 사용한다
테스트 코드 중복 제거
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class Test{
// Calculator cal = new Calculator(); 대신 @Before어노테이션 활용
private Calculator cal;
@Before
public void setUp() {
cal = new Calculator();
System.out.println("before");
}
@Test
public void add() {
assertEquals(12, vcacal.add(8,4));
System.out.println("add");
}
@Test
public void substract() {
assertEquals(2, cal.substract(8,4));
System.out.println("substract");
}
@After
public void teardown(){
System.out.println("teardown");
}
}
//////////
/*
결과값
before
substract
teardown
before
add
teardown
*/
리팩토링
소스코드의 가독성을 높이고 유지보수를 편하게 하기위해 소스코드의 구조를 변경하는 것을 의미한다.
기능상의 결과가 변경되는 것이 아니라 작업 이전과 똑같은 기능을 해야한다.
3가지 사항만 지키면 리팩토링을 하면 된다.
- 메소드가 한가지 책임만 가지도록 구현하기
- 인덴트(들여쓰기, indent) 깊이를 1단계로 유지하기
* while문과 if문을 사용할 경우 인덴트 깊이가 1씩 증가한다.
ex. 밑의 코드는 깊이 2단계다
void someMethod() {
while (true) {
if(true) {
}
}
}
- else를 사용하지 말기
느낀점
'김영한님의 spring 입문강의'를 들으면서도 이해가 안가는 부분이 있었는데
겸해서 보니까 좋다. 추천!
내가 전에 썼던 글(https://hxxxxxl.tistory.com/54?category=1034019)에서 이해안가던 부분이
assertj 부분(JUnit의 Assert를 사용하기보다 테스트의 의도를 더 쉽게 파악할 수 있는 기능을 제공하는 AssertJ 도 많이 사용한다)과 @BeforeEach, @AfterEach 부분(*@Before, @After // 책은 2016년도(JUnit4까지 나온 시기)고 지금은 jUnit5를 쓴다)도 위의 코드를 보면서 이해가 갔다.
내일은 spring 입문 끝내고 next step책도 정리해서 올려야겠다.
그리고 추가로 리팩토링 관련한 소스들을 찾아보고 싶다. indent 부분이 아직 완벽히 이해가지 않는다.
+
JUnit은 컴파일 타임에 JAR로서 링크된다. 프레임워크는 JUnit 3.8 이하의 경우 junit.framework 패키지 밑에 상주하며, JUnit 4 이상의 경우 org.junit 패키지 밑에 상주한다.(위키)
(https://github.com/junit-team/junit5)
아.. 이래서 공식문서를 보라고 하는구나...하고 직접깨달았지만 아직 신입도 안된 나는.. 허둥지둥 블로그정리글을 보기 바빴는데 어제 백기선님 유튜브영상 올라온거 보고 블로그 정리글의 치명적 단점을 알게 되었다..영어... 조금씩 해봐야겠다!_!
'Back > Java' 카테고리의 다른 글
[열혈 java 프로그래밍, 윤성우] 강의 3-5강 정리 (0) | 2022.02.28 |
---|---|
[열혈 java 프로그래밍, 윤성우] 자바 강의 1~2강 정리 (0) | 2022.02.25 |
[Java / Collection] 컬렉션 개념 이해 및 정리(*) (0) | 2022.02.17 |
[Java / 개념이해 ] 자바의 정석 : 자바 언어 특징, JVM, 자바개발환경구축 (0) | 2022.02.15 |
[programming basic] 메소드와 클래스, 객체, 추상화, 필드 (+캡슐화) (0) | 2021.07.11 |