본문 바로가기
스터디정리방

[기록] 211215

by gogumi 2021. 12. 18.

1. JUnit의 동작 방식

JUnit에는 생명주기가 있다. @BeforeClass부터 @AfterClass까지 정해진 순서를 따른다. 동일한 어노테이션이 반복된다면, 그들간의 순서는 보장받지 못한다. 이것은 각 테스트가 최소 단위로 진행된다는 것을 전제하기 때문이다.

  1. 테스트 클래스에서 @Test가 붙은 public이고 void형이며 파라미터가 없는 테스트 메소드를 모두 찾는다.
  2. 테스트 클래스의 오브젝트를 하나 만든다.
  3. @Before가 붙은 메소드가 있으면 실행한다.
  4. @Test가 붙은 메소드를 하나 호출하고 테스트 결과를 저장해둔다.
  5. @After가 붙은 메소드가 있으면 실행한다.
  6. 나머지 테스트 메소드에 대해 2~5번 을 반복한다.
  7. 모든 테스트의 결과를 종합해서 돌려준다.

추가로 @BeforeClass와 @AfterClass는 각각 전체 테스트가 실행되기 전, 테스트가 종료된 후에 딱 한 번씩 호출된다.

 

2. 중첩 클래스

중첩 클래스는 클래스 내부에 선언한 클래스로 내부 클래스라고도 불린다. 중첩 클래스를 사용하면 가독성이 좋아지고 외부 클래스의 필드 접근에 용이하다. 궁극적으로 캡슐화를 위해 사용한다.

public class OuterClass {
	class A { }		//내부 클래스
	static class B { }		//정적 중첩 클래스
    
	void method() {
    	class C {}		//로컬 클래스
    }
}
  • 내부 클래스 : 일반 클래스 내부에 생성된다. 자신의 밖에 있는 클래스의 자원을 직접 사용할 수 있다.
  • 정적 중첩 클래스 : 내부 클래스와 비슷하나, static으로 선언한다. 밖에 있는 클래스의 변수와 메소드 중에 static이 붙은 것들은 사용할 수 있다.
  • 로컬 클래스 : 메소드 내부에 클래스를 정의하여 마치 메소드 내의 지역변수처럼 쓰인다. 메소드 내부에서만 사용할 수 있다.

Reference

https://sjh836.tistory.com/145

https://brownbears.tistory.com/526

 

 

3. 템플릿

: 스프링에 적용된 템플릿 기법을 살펴보고, 이를 적용해 완성도 있는 DAO 코드를 만드는 방법을 알아보자.

2장에서는 DBConnection 에 대한 예제로 1장에서 다뤘던 아래의 디자인 패턴들로 try-catch-finally 패턴에서 close
즉, 예외발생시 리소스를 제대로 반납하지 않아 커넥션풀 문제로 서버가 다운될 수 있는 상황에 대해 설명하고 있다.

 

4. 디자인패턴 & 리팩토링

-개방 폐쇄 원칙
: 확장에는 자유롭게 열려 있고 변경에는 굳게 닫혀 있다는 객체지향 설계의 핵심 원칙이다.

-메소드 추출
: 변화되거나 공통되는 부분을 메소드로 빼서 가독성 및 재사용이 가능하도록 한다.

-템플릿 메소드 패턴 & 전략 패턴 example
: https://github.com/yky03/toby-spring/blob/main/v1/01-ObjectAndDependencyRelationship.md

-템플릿으로 효과적인 DI 적용을 위한 방법
: 클라이언트/컨텍스트를 분리하고 전략클래스, 로컬클래스, 익명 내부 클래스(람다로 사용 가능)를 활용하자.

 

Tip

: try catch 보다는 try with resource를 사용하라! -> 이펙티브 자바

 

[AS-IS]
-> Bubble Style로 가독성 떨어짐.

finally {
    if(bufferedInputStream != null) {
        try {
            bufferedInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    try {
        if(fileInputStream == null) {
            fileInputStream.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

[TO-BE]
-> 복잡한 구문 없이 자원 반납 가능.

try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))){
    ...
} catch ( ) {
    ...
}
 

--------------------------- p.231 ---------------------------

 

5. JUnit

 

JUnit은 왜 public 메소드만을 테스트 메소드로 허용하고 있는가?

JUnit은 테스트 클래스의 테스트 메소드를 자바의 리플렉션 API를 이용해서 호출을 해준다. Pluggable Selector 패턴을 이용했기 때문이다. 그런데 JUnit이 처음 만들어지던 때 사용했던 JDK1.1에서는 리플렉션에서 public 메소드만 접근을 허용했었다. 따라서 JUnit의 테스트 메소드는 모두 public일 수밖에 없었다. 이것이 관례가 되어 지금까지 내려온 것이다.

하지만 JDK1.2부터는 리플렉션에서 public외의 모든 접근레벨을 허용하기 시작했고, private 메소드 호출이 가능해졌다. 심지어 final 변수의 값도 수정이 가능해졌다.

어쨌든 JUnit은 전통을 유지해서 JUnit 4.x에서도 여전히 public 메소드만 테스트로 허용하고 있다.

 

추가) JUnit5에서는 더 이상 테스트 클래스와 메소드의 접근제어자를 public으로 선언하지 않아도 동작한다. 그러나 private으로 선언하면 안 된다.

 

출처 : JUnit 테스트 메소드는 왜 public이어야 할까요? (google.com)

Pluggable Selector 패턴 참조 : Effective Programming :: Pluggable Selector 패턴 (tistory.com)

 

6. 빌드 스크립트

 

C/C++, Java 같은 컴파일 언어는 기본적으로 컴파일러 명령어를 사용해서 컴파일을 한다. 문법 공부를 하거나 간단한 프로그램일 경우 컴파일할 파일들을 쭉 나열해서 하나씩 컴파일을 할 수도 있지만 프로그램이 커질수록 파일이 많아지고, 각 파일 간에 의존성도 생기게 된다.
이걸 매번 손으로 직접하기 귀찮으니 쉘 스크립트 같은 걸로 명령어를 미리 작성해서 명령어 하나만 실행하면 전체 프로젝트가 컴파일 되도록 만든 것이 빌드 스크립트이다.

이 빌드 스크립트를 작성하는 것도 쉘에 맞춰서 하기 귀찮으니 미리 정의된 형식의 파일을 만들어서 실행시키면 빌드가 되도록 만든 것이 Makefile같은 빌드 툴이다.

여기서 조건(하드웨어, 소프트웨어 버전)에 따라 다른 결과물을 뽑거나 외부 라이브러리를 일일이 다운받아서 관리하지 않도록 발전한 것이 Ant, Maven, Gradle 같은 빌드 툴이다.

출처 : JUnit 테스트 메소드는 왜 public이어야 할까요? (google.com)

만약 빌드 툴이 없었다면?

빌드 도구 없이 자바 프로그램을 배포하려면 개발 도구로 컴파일하고 설정 파일 등 리소스를 복사하고, 필요하면 운영 환경에 맞게 값을 수정하고 버전 정보를 고치고, API문서를 생성하고 메뉴얼을 PDF로 뽑아내고 등등의 작업을 해야 한다. 그리고 그런 결과물을 디렉토리 구조에 맞게 잘 취합하고 압축을 묶어 WASFTP로 전송해서 배포하거나 설치 프로그램을 돌려서 결과물을 다운로드 받을 수 있는 웹서버에 올리는 등의 작업을 해야 한다.

출처 : OKKY - 빌드 스크립트가 뭔가요?

 

7. JUnit의 특징

이전 버전의 JUnit과는 달리 JUnit5는 세가지의 모듈로 구성됩니다.

 

JUnit5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

JUnit Platform : JVM에서 실행되는 테스트 프레임워크로 테스트 프레임워크를 개발하기 위한 TestEngine API를 정의합니다.

 

JUnit Jupiter : JUnit5의 쓰기 테스트와 확장을 위한 새로운 프로그래밍 모델과 확장 모델의 조합으로 테스트를 실행할 수 있는 TestEngine 을 제공합니다.

 

JUnit Vintage : JUnit 3 JUnit 4 기반 테스트를 실행하기 위한 TestEngine을 제공합니다. (클래스 경로 또는 모듈 경로에 JUnit 4.12 이상이 있어야 합니다.)

 

8. JUnit4 -> JUnit5 마이그레이션

            • @Before, @After 가 사라지고, @BeforeEach @AfterEach로 변경되었습니다.
            • @BeforeClass, @AfterClass가 사라지고, @BeforeAll @AfterAll로 변경되었습니다.
            • @Ignore 는 더이상 존재하지 않습니다. @Disabled 또는 다른 기본 제공 실행 조건 중 하나를 사용하세요.
            • @Category가 사라지고, @Tag로 변경되었습니다.
            • @RunWith이 사라지고 @ExtendWith으로 변경되었습니다.
            • @Rule 및 @ClassRule이 사라지고 @ExtendWith 및 @Register로 변경되었습니다.
            • Junit5에서는 Assertion 의 위치가 org.junit.jupiter.api.Assertions으로 변경되었습니다. AssertJ, Hamcrest, Trust에서 제공하는 org.junit.Assert는 그대로 사용할 수 있습니다.
Annotations 설명
@Test 테스트임을 나타내는 어노테이션입니다
@BeforeEach JUnit4 @Before 어노테이션과 유사하며, 해당 주석이 달린 메소드가 @Test, @RepeatedTest, @ParameterizedTest @TestFactory 이전에 실행되어야 함을 알리는 어노테이션입니다
@AfterEach JUnit4 @After 어노테이션과 유사하며, 해당 주석이 달린 메소드가 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 이후에 실행되어야 함을 알리는 어노테이션입니다
@BeforeAll JUnit4 @BeforeClass 어노테이션과 유사하며 해당 주석이 달린 메소드가 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 이전에 실행되어야 함을 알리는 어노테이션입니다
@AfterAll JUnit4 @AfterClass어노테이션과 유사하며 해당 주석이 달린 메소드가 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 이후에 실행되어야 함을 알리는 어노테이션입니다
@Disabled 테스트 클래스 또는 테스트 메서드를 비활성화하는 데 사용되며, JUnit 4 @Ignore 어노테이션과 유사합니다.
@ExtendWith 확장을 선언적으로 등록하는 데 사용됩니다. 스프링을 사용하려면 @ExtendWith(SpringExtension.class)를 선언하여 사용합니다
@DisplayName 테스트 클래스 또는 테스트 메서드의 사용자가 지정할 이름을 선언합니다.
@TestMethodOrder 주석이 달린 테스트 클래스에 대한 테스트 방법 실행 순서를 구성하는 데 사용되며, JUnit 4 @FixMethodOrder와 유사합니다.

코드 커버리지(Code Coverage)

코드 커버리지는 소프트웨어의 테스트를 논할 얼마나 테스트가 충분한가를 나타내는 지표중 하나다. 그대로 코드가 얼마나 커버되었는가이다. 소프트웨어 테스트를 진행했을 코드 자체가 얼마나 실행되었냐는 것이다.

코드의 구조를 이루는 것은 크게 구문(Statement), 조건(Condition), 결정(Decision)이다. 이러한 구조를 얼마나 커버했느냐에 따라 코드커버리지의 측정기준은 나뉘게 된다.

 

이를 사용하여 테스트가 어느정도 구현이 되었는지 판단이 가능하고 JaCoCo 같은 라이브러리를 사용하면 최소한의 테스트코드 퍼센트를 정의할 있습니다. 이렇게 정한 최소한의 퍼센트를 넘지않으면 Build 실패하게끔 룰을 정할 있습니다.

 

9. 테스트 방식

웹 화면을 통한 테스트 방식은 결과를 확인하는 가장 흔히 쓰이는 방법이지만, DAO뿐만 아니라 서비스 클래스, 컨트롤러, JSP 뷰 등 모든 레이어의 기능을 다 만들고 나서야 테스트가 가능하다는 점이 가장 큰 문제다. 이 경우 오류가 있을 때 빠르고 정확하게 대응하기가 힘들다는 문제가 있다. 이러한 작업으로 테스트를 계속하다보면 코드를 수정하거나 개선하려는 마음을 접어버리고 넘어갈수도 있다. 이러한 문제점을 개선해주는 방법이 JUnit 프레임워크이다.

 

이후 스터디원들과 토론했던 주제는 테스트하는 방식의 공유였다.

 

답변 1 : 테스트 커버리지로 관리하면서 수치가 70%가 넘어간 후 테스트케이스를 작성하여 테스트를 한다.

답변 2 : 개발용 디비와 개발용 환경을 구축하여 테스트를 한다.

답변 3 : 경우의 수를 스스로 생각해보며 테스트를 한다.

 

 

10. 템플릿메소드패턴과 전략패턴의 차이점

템플릿 메소드 패턴은 상속을 통해 기능을 확장해서 사용한다. 
변하지 않는 부분은 슈퍼클래스에 두고 사용하고, 변하는 부분은 추상 메소드로 정의
하여 각 서브클래스에서 오버라이드하여 새롭게 정의해 사용하도록 하는 것이다.
템플릿 메소드 패턴보다 더 유연하고 확장성이 뛰어나다. 오브젝트를 아예 둘로 분리하고
클래스 레벨에서는 인터페이스를 통해서만 의존 하도록 만든다.
OCP 관점에서, 확장에 해당하는 부분을 별도의 클래스로 만들어 추상화된 인터페이스를 통해 위임하는 방식이다.

참고 : https://lion-king.tistory.com/entry/Spring-Design-pattern-Template-Strategy

 

실제로 실무에서 자주쓰는 메소드들은 .js 파일에 미리 메소드들을 정의해놓고 필요한 메소드만 쓰는 전략패턴을 사용하였고, 스크립트단에서 몇개안되는 기능들을 만들때에는 템플릿메소드패턴을 사용하여 각 메소드들의 코드를 작성했었던 방식을 사용하였다. 해당 디자인패턴들을 공부하니 실무에서의 디자인패턴이 하나둘씩 눈에 보이게된다는 것을 각자 공감하였다.

'스터디정리방' 카테고리의 다른 글

[기록] 220112  (0) 2022.01.16
[기록] 220105  (0) 2022.01.09
[기록] 211229  (0) 2022.01.02
[기록] 211222  (0) 2021.12.26
[기록] 211208  (0) 2021.12.02

댓글