본문 바로가기

Java/[패스트캠퍼스] 50일 포트폴리오 챌린지

[3일차] 10개 프로젝트로 완성하는 백엔드 웹개발(Java/Spring)

수강 강의명
수강 강의명

테스트 코드를 작성해야하는 이유와 작성 방법, 객체지향 패러다임에 대한 개념을 배웠다.

테스트 코드

테스트 코드를 작성하는 이유?

작성한 코드가 올바르게 동작하는지 확인하려면 테스트 과정을 거쳐야 하는데, 이 때 작성하는 코드가 테스트 코드이다.

테스트 코드가 필요한 건 알겠는데, 작성해야 하는 이유는 뭘까?

1. 문서화 역할
2. 코드의 결함을 발견하기 위함.
3. 리팩토링 시 안정성 확보
4. 낮은 결합도를 갖는 설계를 얻게 된다.

 

테스트 코드 개발 방법

테스트 코드를 개발하는 방법은 크게 TDD(테스트 주도 개발)와 BDD(행위 주도 개발)가 있다.

주로 TDD와 BDD를 혼용하여 사용하는 경우가 많다.

 

TDD(Test Driven Development) : 테스트 주도 개발

프로덕션 코드(main)보다 테스트 코드(test)를 먼저 작성하는 개발 방법

메서드 단위로 기능 동작을 검증한다.

TFD(Test First Development)라 부르기도 한다.

실패 > 성공 > 리팩토링

 

BDD(Behavior Driven Development) : 행위 주도 개발

시나리오 기반으로 테스트 코드를 작성하는 개발 방법

하나의 시나리오는 Given, When, Then 구조를 가진다.

 

테스트 코드 관련 프레임워크 & 라이브러리

테스트 코드를 작성할 때 JUnit5 프레임워크와 AssertJ 라이브러리를 많이 사용한다.

JUnit5
자바에서 가장 많이 사용하는 테스팅 프레임워크
https://junit.org/junit5/docs/current/user-guide/#writing-tests
 

JUnit 5 User Guide

Although the JUnit Jupiter programming model and extension model do not support JUnit 4 features such as Rules and Runners natively, it is not expected that source code maintainers will need to update all of their existing tests, test extensions, and custo

junit.org

AssertJ
테스트 코드의 가독성을 높여주는 자바 라이브러리
https://assertj.github.io/doc/#assertj-core-assertions-guide
 

AssertJ - fluent assertions java library

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, BJ Hargrave, Jeremy Landis, Ashley Scopes, Roland Weisleder , Benedikt Bogason , Andreas Kutschera , Matthew , Chris HeZean , Leo0506 , Zhou Yicheng , Saria , Chunhao Liao , max

assertj.github.io

 

실습 : 비밀번호 유효성 검증기

요구사항
• 비밀번호는 최소 8자 이상 12자 이하여야 한다.
• 비밀번호가 8자 미만 또는 12자를 초과하는 경우 IllegalArgumentException 예외를 발생시킨다.
• 경계 조건에 대한 테스트 코드를 작성해야 한다.

 

프로젝트 설정

실습을 위한 새로운 프로젝트를 생성한다.

New Project
New Project

 

코드를 작성하기 전 프로젝트에 필요한 JUnit5와 AssertJ의 dependencies를 추가해야 한다.

JUnit5는 기본적으로 설정되어 있지만, AssertJ는 별도로 추가해줘야 한다.

testImplementation 'org.assertj:assertj-core:3.23.1'
Ctrl + Shift + O : 빌드 파일에 추가한 라이브러리를 프로젝트에 설치하는 단축키

build.gradle
build.gradle
External Libraries
External Libraries

 

프로덕션 코드(main)의 패키지와 테스트 코드(test)의 패키지를 동일하게 설정해야 한다.

프로덕션 코드 : main패키지 내 코드를 작성하는 부분
테스트 코드 : test패키지 내 코드를 작성하는 부분

프로젝트 구조
프로젝트 구조

 

프로젝트 실습

테스트 메서드를 생성한 다음 @DisplayName 어노테이션을 붙여 테스트 코드의 의도를 나타낸다(문서화).

Alt + Insert > Test Method : 테스트 메서드를 생성하는 단축키(소스 코드 안에서 입력해야 함)

 

요구사항 1 : 실패(TDD)

프로덕션 코드가 생성되지 않은 상태

public class PasswordValidatorTest {
    @DisplayName("비밀번호가 최소 8자 이상, 12자 이하면 예외가 발생하지 않는다.")
    @Test
    void validatePasswordPassTest() {
        assertThatCode(() -> PasswordValidator.validate("abcdefghijk"))
                .doesNotThrowAnyException();  // 호출될 때 에러가 발생하지 않음.
    }
}

 

요구사항 1 : 성공(TDD)

프로덕션 코드 생성

public class PasswordValidator {
    public static void validate(String password) {}
}

 

요구사항 1 : 리팩토링(TDD)

프로덕션 코드 리팩토링

public class PasswordValidator {
    private static final String WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE = "비밀번호는 최소 8자 이상 12자 이하여야 한다.";

    public static void validate(String password) {
        int length = password.length();

        if (length < 8 || length > 12) {
            throw new IllegalArgumentException(WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE);
        }
    }
}
Ctrl + Alt + V : 직접 작성한 코드를 지역 변수로 만드는 단축키
Ctrl + Alt + C : 직접 작성한 코드를 상수로 만드는 단축키

 

요구사항 2 : 성공

@DisplayName("비밀번호가 8자 미만 또는 12자를 초과하는 경우 IllegalArgumentException 예외를 발생시킨다.")
@Test
void validatePasswordFailTest() {
    assertThatCode(() -> PasswordValidator.validate("abcd"))
            .isInstanceOf(IllegalArgumentException.class)           // 호출될 때 발생하는 예외
            .hasMessage("비밀번호는 최소 8자 이상 12자 이하여야 한다.");  // 메시지 설정
}

 

요구사항 3 : 성공

경계 조건(7자, 13자)에 대한 테스트 코드 작성(요구사항 2 리팩토링)

testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2'

https://www.petrikainulainen.net/programming/testing/junit-5-tutorial-writing-parameterized-tests/

 

JUnit 5 Tutorial: Writing Parameterized Tests

JUnit 5 Tutorial: Writing Parameterized Tests Author: Petri Kainulainen Published: December 19, 2021 This blog post describes how we can write parameterized tests with JUnit 5. After we have finished this blog post, we: Can get the required dependencies wi

www.petrikainulainen.net

@DisplayName("비밀번호가 8자 미만 또는 12자를 초과하는 경우 IllegalArgumentException 예외를 발생시킨다.")
@ParameterizedTest
@ValueSource(strings = {"abcdefg", "abcdefghijklm"})
void validatePasswordFailTest(String password) {
    assertThatCode(() -> PasswordValidator.validate(password))
            .isInstanceOf(IllegalArgumentException.class)           // 호출될 때 발생하는 예외
            .hasMessage("비밀번호는 최소 8자 이상 12자 이하여야 한다.");  // 메시지 설정
}

 

실습 : 비밀번호 유효성 검증기(랜덤 비밀번호 생성)

요구사항
• 비밀번호는 최소 8자 이상 12자 이하여야 한다.
• 비밀번호가 8자 미만 또는 12자를 초과하는 경우 IllegalArgumentException 예외를 발생시킨다.
• 경계 조건에 대한 테스트 코드를 작성해야 한다.

소스 코드

https://github.com/serverwizard/oop-practice/blob/master/src/main/java/org/example/RandomPasswordGenerator.java#L38

 

요구사항 1 : 성공(BDD)

항상 8글자("abcdefgh")를 반환하게 된다.

public class User {
    private String password;
    
    // 패스워드 초기화
    public void initPassword(PasswordGenerator passwordGenerator) {
        String password = passwordGenerator.generatePassword();

        // 비밀번호는 최소 8자 이상 12자 이하여야 한다.
        int length = password.length();

        if (length >= 8 && length <= 12) {
            this.password = password;
        }
    }

    public String getPassword() {
        return password;
    }
}
public class UserTest {
    @DisplayName("패스워드를 초기화한다.")
    @Test
    void initPasswordTest() {
        // given
        User user = new User();

        // when
        user.initPassword(new CorrectFixedPasswordGenerator());
        // user.initPassword(() -> "abcdefgh");

        // then
        assertThat(user.getPassword()).isNotNull();
    }
}
// PasswordGenerator 인터페이스 구현체
public class CorrectFixedPasswordGenerator implements PasswordGenerator {
    @Override
    public String generatePassword() {
        return "abcdefgh";  // 8글자
    }
}
// RandomPasswordGenerator를 제어하기 위한 인터페이스
public interface PasswordGenerator {
    String generatePassword();
}
public class RandomPasswordGenerator implements org.example.password.PasswordGenerator {

 

요구사항 2 : 성공(BDD)

// PasswordGenerator 인터페이스 구현체
public class WrongFixedPasswordGenerator implements PasswordGenerator {
    @Override
    public String generatePassword() {
        return "abcd";
    }
}
public class UserTest {
    @DisplayName("패스워드가 요구사항에 부합되지 않아 초기화가 되지 않는다.")
    @Test
    void nonInitPasswordTest() {
        // given
        User user = new User();

        // when
        user.initPassword(new WrongFixedPasswordGenerator());
        // user.initPassword(() -> "abcd");

        // then
        assertThat(user.getPassword()).isNull();
    }
}
낮은 결합도를 갖는 설계를 얻게 된다.
• 내부 생성 → 강한 결합
• 외부 생성 → 낮은 결합

 

객체지향 패러다임

객체에게 책임을 할당하여 서로 메시지를 주고 받으며 협력하도록 하는 것
점점 증가하는 SW의 복잡도를 낮추기 위해 등장하게 된 패러다임이다.

클래스가 아닌 객체에 초점을 맞춰야 한다.
객체들에게 적절한 역할과 책임을 할당하는게 중요하다.
객체지향 세계에서는 모든 객체가 능동적인 존재이다.

 

절차지향 프로그래밍 vs. 객체지향 프로그래밍

절차지향 프로그래밍 : 책임이 한 곳에 집중된 방식(A 객체에게 모든 처리 집중)
객체지향 프로그래밍 : 책임이 여러 객체로 적절히 분산된 방식(A 객체 (협력) B 객체)

 

객체지향 특징

추상화(Abstraction) : 불필요한 부분을 제거함으로써 필요한 핵심만을 나타낸다(복잡성을 낮추기 위함).
다형성(Polymorphism) : 다양한 형태를 갖는다.
캡슐화(Encapsulation) : 객체 내부의 세부 사항을 외부로부터 감춘다.
상속(Inheritance) : 부모로부터 물려 받는다.

 

객체지향 설계 원칙 5가지(SOLID)

SRP : Single Responsibility Principle(단일 책임의 원칙) = 하나의 책임
OCP : Open/Closed Principle(개방 폐쇄의 원칙) = 개방 : 확장, 폐쇄 : 변경(코드를 변경하지 않고 기능 추가)
LSP : Liskov’s Substitution Principle(리스코프 치환의 원칙) = 상위 타입의 객체를 하위 타입의 객체로 치환 가능
ISP : Interface Segregation Principle(인터페이스 분리의 원칙) = 클라이언트에게 필요한 인터페이스만 구현
DIP : Dependency Inversion Principle(의존성 역전의 원칙) = 변경이 거의 일어나지 않는 쪽에 의존

 

객체지향 설계 및 구현

1. 도메인을 구성하는 객체에는 어떤 게 있을지 파악한다.
2. 객체들 간의 관계를 고민한다.
3. 동적인 객체를 정적인 타입으로 추상화하여 도메인을 모델링한다.
4. 협력을 설계한다.
5. 객체들을 포괄하는 타입(클래스)에 적절한 책임을 할당한다.
6. 이를 구현한다.

 

수강 화면수강 화면
수강 화면

이번 테스트 코드 실습을 통해 테스트 코드를 작성하는 방법에 대한 감이 잡히게 되었다.

설명하시는 코드를 따라 치다보니 흐름은 이해가 가지만, 처음부터 끝까지 코드를 짜려면 막막할 거 같다.

어떠한 상황을 만들어 직접 코드를 작성해봐야겠다.

객체지향 패러다임을 적용한 설계를 단계별로 제시해주셔서 설계에 대한 근심이 어느정도 해소 되었다.

 

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.
https://bit.ly/43z0P6S

 

패스트캠퍼스 [직장인 실무교육]

프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.

fastcampus.co.kr

 
#패스트캠퍼스 #포트폴리오 #직장인자기계발 #환급챌린지 #포트폴리오챌린지 #패스트캠퍼스후기 #초격차패키지 #오공완