🦁멋쟁이사자처럼 백엔드 부트캠프 13기 🦁
TIL 회고 - [67]일차
🚀67일차에는 JUnit과 Git에 대해 학습할 수 있었다. JUnit은 이전의 대외활동에서 접했던 적은 있었지만 개념이 안잡힌 채로 테스트를 진행했었기때문에 어려움을 느꼈던 부분이었다. Git 또한 기존에 어느정도 사용도 하고 있었지만 기초 개념이 부족하다는 생각을 했는데 이번 학습기회에 두 개념 모두 잘 배워놔야겠다고 생각했다.
학습 목표 : JUnit을 통해 테스트의 중요성을 깨닫고 Git을 통해 형상관리 및 개발한 파일들의 버전관리를 할 수 있어야함
학습 과정 : 회고를 통해 작성
JUnit

- 이처럼 IntelliJ 상에서 만들어져있는 test 패키지에서 JUnit 테스트 클래스를 만들게 된다.
- TDD(Test Driven Development) : 테스트 기반 개발 방법론이 주 원칙
- TDD의 과정
1) 실패하는 테스트 코드 작성 (Red): 먼저 구현할 기능에 대한 테스트 코드를 작성
2) 테스트 통과를 위한 최소 코드 작성 (Green): 작성한 테스트를 통과하기 위한 최소한의 코드를 작성
(완성도 보단 통과에 집중)
3) 코드 개선 (Refactor): 테스트를 통과한 코드를 리팩토링하여 가독성, 유지보수성, 효율성을 개선
이 과정을 반복하면서 점진적으로 기능을 구현해 나가는 것이 TDD
JUnit 테스트 코드 만들기

- 클래스명에서 Alt + Enter를 누르면 Create Test를 만들수도 있다.


- 테스트할 클래스파일과 동일한 이름으로 test패키지 안에 만들어진다.
테스트 코드의 구조
class CalculatorTest {
@BeforeEach
void setUp() {
System.out.println("각 테스트 메서드가 실행되기 전에 실행.");
}
@AfterEach
void tearDown() {
System.out.println("각 테스트 메서드가 실행된 후 실행.");
}
@Test
void add() {
System.out.println("add test 메서드 실행 ");
}
}
- 각 메소드 별 수행하는 일들은 println()으로 표시
- setUp()
➡️매 실행때마다 초기값을 가지고 실행되고, cal = new Calculator(); 처럼 선언 - tearDown()
➡️실행 끝날 경우에 다시 값을 null로 바꿔주는 과정, cal = null로 구현 가능 - @BeforeAll 같은 어노테이션은 외부에서 불려져서 사용되어야하므로 static해야함
➡️모든 테스트 실행 전에 한번 실행을 도와줌
(=즉 전체의 테스트 실행 전 한번 실행될때 사용하는 테스트 어노테이션)
마찬가지로 @AfterAll도 같은 특징(static해야함)을 가지며
➡️전체의 테스트 실행 후 한번 실행되는 테스트 어노테이션

Assert
- import static org.junit.jupiter.api.Assertions.*;
➡️Assert 사용을 위한 import 문
import를 static하게 가져오고 있음
⭐Assertions → 대문자로 시작하고 있으므로 Assertions 클래스를 의미하며
이 클래스가 가진 static한 메소드들을 모두 import하겠다는 의미

- 기대값과 실제값을 넣어 비교한다.
덧셈 테스트
@Test
void add() {
System.out.println("add test 메서드 실행 ");
assertEquals(7, cal.add(3, 4));
assertEquals(15, cal.add(10, 5));
assertEquals(-1, cal.add(2, -3));
}

- 기대값 결과와 실제값 결과가 일치하지 않는 경우 경고문을 출력
성공한 경우에는 ✅ add() 처럼 완료 표시로 출력
나눗셈 테스트
public double divide(int a, int b){
if(b == 0){
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return (double) a / b;
}
- Calculator에 새로운 메소드를 추가해보고
테스트 클래스 (CalculatorTest)에서 @Test로 테스트할 메소드를 만들어준다. → @Test void divide(){…}
@Test
void divide(){
System.out.println("[divide] test 메서드 실행 ");
assertEquals(0.0, cal.divide(0, 3));
assertEquals(2.0, cal.divide(6, 3));
assertEquals(4.0, cal.divide(20, 5));
assertEquals(-4.0, cal.divide(20, -5));
assertEquals(0.0, cal.divide(20, 0)); // 예외발생 테스트케이스
}

- divide()처럼만 테스트해주면 어떤 것에 대한 테스트인지 헷갈릴 수 있으므로
@DisplayName(”0으로 나누기 예외발생 테스트”)를 활용가능
@Test
@DisplayName("나눗셈 테스트")
void divide(){
System.out.println("[divide] test 메서드 실행 ");
assertEquals(0.0, cal.divide(0, 3));
assertEquals(2.0, cal.divide(6, 3));
assertEquals(4.0, cal.divide(20, 5));
assertEquals(-4.0, cal.divide(20, -5));
}

- @DisplayName 사용으로 기존의 divide()만 출력되던것을 직관적으로 표시되도록 변경 가능
예외발생 테스트
@Test
@DisplayName("0으로 나눈 예외발생 테스트")
void divideByZero(){
// assertEquals(0.0, cal.divide(20, 0)); // 예외발생 테스트케이스
assertThrows(ArithmeticException.class, () -> cal.divide(6, 0));
}
- 예외를 별도로 처리가능하고 이 예외처리가 성공했을 경우
- assertThrows(ArithmeticException.class, () -> cal.divide(6, 0));
6, 0 테스트 값을 입력했을때 ArithmeticException이 발생해야 성공이라는 의미
회원가입 성공 테스트
@SpringBootTest
@Transactional
class UserServiceTest {
@Autowired
private UserService userService; // UserService가 필요하기때문에 필드 주입 (@Autowired)
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Test
@DisplayName("회원가입 - 성공")
void register_Success() {
String email = "test111@exam.com";
String password = "1234";
User response = userService.register(email, password);
// 1. 이메일 테스트
assertNull(response);
assertEquals(email, response.getEmail());
assertTrue(userRepository.findByEmail(email).isPresent());
// 2. 패스워드 테스트
assertTrue(passwordEncoder.matches(password, response.getPassword()));
}
//...
- @Transactional
➡️테스트가 끝나면 DB에서 변경했던 사항들을 롤백시켜줌. >> 실제로 DB에 반영하지 않겠다는 것 - assertNull(response); ➡️ null이 아닌지 테스트
- assertEquals(email, response.getEmail());➡️입력한 email이 제대로 입력되었는지 테스트
- assertTrue(userRepository.findByEmail(email).isPresent());➡️DB에 제대로 저장되었는지 테스트
회원가입 실패 - 중복 이메일
// 사용자 회원 가입
@Transactional
public User register(String email, String password){
if(userRepository.findByEmail(email).isPresent()){ // 이메일로 조회 후 이메일 중복 체크
throw new IllegalArgumentException("["+email+"]이미 존재하는 이메일입니다.");
}
User user = new User();
user.setEmail(email);
user.setPassword(passwordEncoder.encode(password));
return userRepository.save(user);
}
throw new IllegalArgumentException("["+email+"]이미 존재하는 이메일입니다.");
- 기존 UserService에서의 회원가입 코드에서 이미존재하는 이메일일 경우의 예외처리에 따라
@Test
@DisplayName("회원가입 - 실패 (중복 이메일)")
void register_EmailFail() {
String email = "duplicate@exam.com";
String password = "1234";
// 기존 사용자 저장
User existUser = new User();
existUser.setEmail(email);
existUser.setPassword(password);
// 동일한 이메일로 회원가입 테스트
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
userService.register(email, "5678");
});
assertEquals("[" + email + "]이미 존재하는 이메일입니다.", exception.getMessage());
}
- IllegalArgumentException을 생성 후 assertEquals()에서 테스트코드를 작성 가능
Mock 객체
- 서비스 레이어만 담당하는 개발자인 경우 Repository 레이어가 아직 구현이 되어있지 않을때의
테스트를 진행하기위해서는 Mock 객체를 활용해야한다 (가짜객체)
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Assertions.*;
- import 문
▶️실습 - Mock 객체 + 회원가입 성공
public class UserServiceWithMock {
@Mock // 가짜 객체로 생성해달라는 것 - UserRepository가 구현돼있다고 가정하고 사용하는 것
private UserRepository userRepository;
@Mock
private PasswordEncoder passwordEncoder;
@InjectMocks // UserRepository (Mock 객체 = 가짜객체)를 주입시킬 대상을 지정
private UserService userService;
@BeforeEach
void setup(){
MockitoAnnotations.openMocks(this); // Mockito 를 초기화시켜주는 메소드
}
@Test
@DisplayName("회원가입 - 성공")
void register_Success(){
String email = "test@test@exam.com";
String password = "pass123";
when(userRepository.findByEmail(email)).thenReturn(Optional.empty());
User response = userService.register(email, password);
// 1. 이메일 테스트
assertNull(response); // null이 아닌지 테스트
assertEquals(email, response.getEmail()); // 입력한 email이 제대로 입력되었는지 테스트
assertTrue(userRepository.findByEmail(email).isPresent()); // DB에 제대로 저장되었는지 테스트
verify(userRepository,times(1)).save(any(User.class)); // save() 호출
}
}
- @Mock private UserRepository userRepository;
➡️가짜 객체로 생성해달라는 것 (=UserRepository가 구현돼있다고 가정하고 사용하는 것)
- UserRepository 인터페이스의 Mock 객체를 생성
- Mock 객체는 실제 UserRepository 구현체 대신 사용
- UserRepository가 아직 구현되지 않았거나, 실제 데이터베이스와의 연동 없이 테스트를 수행하고 싶을 때 유용 - @InjectMocks private UserService userService;
➡️UserRepository (Mock 객체 = 가짜객체)를 주입시킬 대상을 지정 - @BeforeEach void setup(){ MockitoAnnotations.openMocks(this); }
➡️Mockito 를 초기화시켜주는 메소드
- MockitoAnnotations.openMocks(this) : Mockito를 초기화하고 @Mock 어노테이션이 붙은 필드를 초기화
- this는 현재 테스트 클래스를 가리키며, 현재 클래스 내의 @Mock 어노테이션이 붙은 필드들을 초기화 - @Test @DisplayName("회원가입 - 성공") void register_Success(){ ... }
➡️@Test 어노테이션은 JUnit 5에서 테스트 메소드를 지정
➡️@DisplayName 어노테이션은 테스트 결과를 표시할 때 사용할 이름을 지정 - when(userRepository.findByEmail(email)).thenReturn(Optional.empty());
➡️when(...).thenReturn(...) : Mockito에서 Mock 객체의 메소드 호출 시 반환값을 설정➡️userRepository.findByEmail(email)이 호출되면 Optional.empty()를 반환하도록 설정 - verify(userRepository,times(1)).save(any(User.class));
➡️verify() : Mockito에서 Mock 객체의 메소드 호출 횟수를 검증
- userRepository.save(any(User.class))가 1번 호출되었는지 확인하는 것
- any(User.class)는 User 타입의 어떤 객체든 매칭합니다.
Unit 테스트 vs 통합 테스트
- 단위 테스트 (Unit Test)
➡️애플리케이션을 구성하는 가장 작은 단위의 코드(함수, 메서드, 클래스 등)를 독립적으로 테스트하는 것
주로 개발자가 작성하며, 코드의 각 부분이 예상대로 작동하는지 검증
외부 의존성(데이터베이스, 네트워크 등)을 Mock 객체로 대체하여 테스트함으로써
빠르게 실행될 수 있고, 격리된 환경에서 테스트 가능 - 통합 테스트 (Integration Test)
➡️여러 개의 단위 또는 컴포넌트가 함께 작동하는지 테스트하는 것
단위 테스트를 통과한 코드들이 실제 환경에서 제대로 상호작용하는지 검증
외부 의존성을 포함하여 테스트
즉 단위 테스트: 개별 코드의 정확성 검증 (개발자 책임), 통합 테스트: 시스템 전체의 상호작용 검증 (개발팀 전체 책임)
Git
- 형상관리 도구 (Configuration Management Tool)
- 형상 관리 : 소프트웨어 개발 과정에서 발생하는 모든 변경 사항을 체계적으로 관리하고 통제하는 것을 의미
(=소프트웨어의 '형태'를 관리) - Git은 로컬저장소에 형상관리 하는 도구
- Github는 원격저장소에 형상관리를 할 수 있도록 제공하는 도구
- Github 외로 GitLab, BitBucket 등도 존재함 (원격 저장소 제공자)
Git - 줄바꿈(개행) 표시 설정
- 윈도우 사용자가 git과의 개행 문자 문법을 맞춰주려면 core.autocrlf true를 해주어야한다.
git config --global core.autocrlf true
Git & 리눅스 명령어
- pwd : 현재 디렉토리 출력
- cd [디렉토리경로] : 디렉토리로 이동
- mkdir [디렉토리명] : 디렉토리 생성
- rm [파일명] : 파일 지우기
- rm -rf [파일명] : 파일 지우기
- git init : 깃 초기화 및 생성
- git config —global core.editor “notepad” : 모든 깃저장소의 에디터로 notepad를 사용할 것
- git config core.editor “notepad” : 현재 있는 깃 저장소에서만 에디터로 notepad를 사용
- echo "name:carami" > application.properties :
파일 간단하게 만들기 name:carami 라는 내용을 application.properties에 넣는다.
- " " 로 묶어주면 파일 내용에 공백이 포함되어도 된다.
- name:carami 처럼 그냥 써줄 경우에는 공백이 있으면 안된다. - cat application.properties : 파일 내용 확인하기
- touch example.txt : example.txt 파일을 만든다.
- code example.txt : 비주얼 스튜디오 코드로 example.txt를 열겠다는 의미
- git config core.editor “code —wait” : code로 열때 작성될때까지 기다려달라는 것
- touch example.txt && code example.txt
➡️example.txt를 생성하고 비주얼 스튜디오 코드로 열게됨
- vi 편집기 작성 종료 및 나가기
ESC → : (콜론) → q → ! + Enter - vi 편집기 작성한 것 저장 후 종료 / 나가기
ESC → : → wq → ! + Enter - vi 편집기 작성한 것 저장
ESC → : → w → ! + Enter - vi 편집기에서 [ i ]
➡️INSERT : 입력모드
기본 브랜치 이름 변경
- git config —global init.defaultBranch <name>
기본 전역 브랜치 이름의 설정을 바꿔줄 수 있다.
git config --global init.defaultBranch main
Git 상태 (git status)
- 깃이 추적하고 있는지 아닌지의 상태를 보여줌

- FORK를 실행해서 Open Repository로 방금 작업한 디렉토리를 불러옴

- git add src 명령어를 하게되면 Staged에 src디렉토리가 담기게된다.
git ignore
- GIt 버전 관리 시스템에서 추적하지 않을 파일이나 폴더를 지정하는데 사용하는 중요한 파일
- touch .gitignore ➡️gitignore 파일 만들기
- Staged 하고싶지 않은 파일들을 넣어놓을 수 있는 파일
ex.
.DS_Store
build - .DS_Store : Mac OS에서 생성되는 파일
- build 폴더 : 빌드해서 class파일이 위치하는 경로
➡️컴파일러에 의해 생성되는 파일은 관리할 필요가 없음 (다시 생성되기때문)
커밋 메시지
- 커밋 컨벤션은 정답이 없다.
- fix : 더이상 이 파일을 수정하지 않을 것
- refactor: 이 파일에 기능이나 수정한 부분이 있다는 것
- Add : 파일을 추가한 것 (비슷하게 feat, Added 가 있음)
🚀회고 결과 :
JUnit과 Git에 대해 회고를 정리하면서 기초를 조금 잡을 수 있었다. JUnit같은 경우 어렵게만 느껴졌던 부분이었는데
Spring 학습과 어노테이션에 대한 개념을 알고난 후로 JUnit의 각 어노테이션이 어떤 역할을 하는지 인지할 수 있게되었다.
Git의 경우에도 사용은 했지만 정확히 어떻게 동작하는지 몰랐던 개념들을 회고를 통해 정리할 수 있었다.
향후 계획 :
- Github와 Git 연동에 대하여
'Recording > 멋쟁이사자처럼 BE 13기' 카테고리의 다른 글
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_69일차_"Docker + Linux" (0) | 2025.03.19 |
---|---|
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_68일차_"Git / GitHub" (0) | 2025.03.18 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_66일차_"모임 관리 프로젝트" (0) | 2025.03.14 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_63일차_"Swagger" (0) | 2025.03.11 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_62일차_"OAuth2 연동" (0) | 2025.03.10 |
🦁멋쟁이사자처럼 백엔드 부트캠프 13기 🦁
TIL 회고 - [67]일차
🚀67일차에는 JUnit과 Git에 대해 학습할 수 있었다. JUnit은 이전의 대외활동에서 접했던 적은 있었지만 개념이 안잡힌 채로 테스트를 진행했었기때문에 어려움을 느꼈던 부분이었다. Git 또한 기존에 어느정도 사용도 하고 있었지만 기초 개념이 부족하다는 생각을 했는데 이번 학습기회에 두 개념 모두 잘 배워놔야겠다고 생각했다.
학습 목표 : JUnit을 통해 테스트의 중요성을 깨닫고 Git을 통해 형상관리 및 개발한 파일들의 버전관리를 할 수 있어야함
학습 과정 : 회고를 통해 작성
JUnit

- 이처럼 IntelliJ 상에서 만들어져있는 test 패키지에서 JUnit 테스트 클래스를 만들게 된다.
- TDD(Test Driven Development) : 테스트 기반 개발 방법론이 주 원칙
- TDD의 과정
1) 실패하는 테스트 코드 작성 (Red): 먼저 구현할 기능에 대한 테스트 코드를 작성
2) 테스트 통과를 위한 최소 코드 작성 (Green): 작성한 테스트를 통과하기 위한 최소한의 코드를 작성
(완성도 보단 통과에 집중)
3) 코드 개선 (Refactor): 테스트를 통과한 코드를 리팩토링하여 가독성, 유지보수성, 효율성을 개선
이 과정을 반복하면서 점진적으로 기능을 구현해 나가는 것이 TDD
JUnit 테스트 코드 만들기

- 클래스명에서 Alt + Enter를 누르면 Create Test를 만들수도 있다.


- 테스트할 클래스파일과 동일한 이름으로 test패키지 안에 만들어진다.
테스트 코드의 구조
class CalculatorTest {
@BeforeEach
void setUp() {
System.out.println("각 테스트 메서드가 실행되기 전에 실행.");
}
@AfterEach
void tearDown() {
System.out.println("각 테스트 메서드가 실행된 후 실행.");
}
@Test
void add() {
System.out.println("add test 메서드 실행 ");
}
}
- 각 메소드 별 수행하는 일들은 println()으로 표시
- setUp()
➡️매 실행때마다 초기값을 가지고 실행되고, cal = new Calculator(); 처럼 선언 - tearDown()
➡️실행 끝날 경우에 다시 값을 null로 바꿔주는 과정, cal = null로 구현 가능 - @BeforeAll 같은 어노테이션은 외부에서 불려져서 사용되어야하므로 static해야함
➡️모든 테스트 실행 전에 한번 실행을 도와줌
(=즉 전체의 테스트 실행 전 한번 실행될때 사용하는 테스트 어노테이션)
마찬가지로 @AfterAll도 같은 특징(static해야함)을 가지며
➡️전체의 테스트 실행 후 한번 실행되는 테스트 어노테이션

Assert
- import static org.junit.jupiter.api.Assertions.*;
➡️Assert 사용을 위한 import 문
import를 static하게 가져오고 있음
⭐Assertions → 대문자로 시작하고 있으므로 Assertions 클래스를 의미하며
이 클래스가 가진 static한 메소드들을 모두 import하겠다는 의미

- 기대값과 실제값을 넣어 비교한다.
덧셈 테스트
@Test
void add() {
System.out.println("add test 메서드 실행 ");
assertEquals(7, cal.add(3, 4));
assertEquals(15, cal.add(10, 5));
assertEquals(-1, cal.add(2, -3));
}

- 기대값 결과와 실제값 결과가 일치하지 않는 경우 경고문을 출력
성공한 경우에는 ✅ add() 처럼 완료 표시로 출력
나눗셈 테스트
public double divide(int a, int b){
if(b == 0){
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return (double) a / b;
}
- Calculator에 새로운 메소드를 추가해보고
테스트 클래스 (CalculatorTest)에서 @Test로 테스트할 메소드를 만들어준다. → @Test void divide(){…}
@Test
void divide(){
System.out.println("[divide] test 메서드 실행 ");
assertEquals(0.0, cal.divide(0, 3));
assertEquals(2.0, cal.divide(6, 3));
assertEquals(4.0, cal.divide(20, 5));
assertEquals(-4.0, cal.divide(20, -5));
assertEquals(0.0, cal.divide(20, 0)); // 예외발생 테스트케이스
}

- divide()처럼만 테스트해주면 어떤 것에 대한 테스트인지 헷갈릴 수 있으므로
@DisplayName(”0으로 나누기 예외발생 테스트”)를 활용가능
@Test
@DisplayName("나눗셈 테스트")
void divide(){
System.out.println("[divide] test 메서드 실행 ");
assertEquals(0.0, cal.divide(0, 3));
assertEquals(2.0, cal.divide(6, 3));
assertEquals(4.0, cal.divide(20, 5));
assertEquals(-4.0, cal.divide(20, -5));
}

- @DisplayName 사용으로 기존의 divide()만 출력되던것을 직관적으로 표시되도록 변경 가능
예외발생 테스트
@Test
@DisplayName("0으로 나눈 예외발생 테스트")
void divideByZero(){
// assertEquals(0.0, cal.divide(20, 0)); // 예외발생 테스트케이스
assertThrows(ArithmeticException.class, () -> cal.divide(6, 0));
}
- 예외를 별도로 처리가능하고 이 예외처리가 성공했을 경우
- assertThrows(ArithmeticException.class, () -> cal.divide(6, 0));
6, 0 테스트 값을 입력했을때 ArithmeticException이 발생해야 성공이라는 의미
회원가입 성공 테스트
@SpringBootTest
@Transactional
class UserServiceTest {
@Autowired
private UserService userService; // UserService가 필요하기때문에 필드 주입 (@Autowired)
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Test
@DisplayName("회원가입 - 성공")
void register_Success() {
String email = "test111@exam.com";
String password = "1234";
User response = userService.register(email, password);
// 1. 이메일 테스트
assertNull(response);
assertEquals(email, response.getEmail());
assertTrue(userRepository.findByEmail(email).isPresent());
// 2. 패스워드 테스트
assertTrue(passwordEncoder.matches(password, response.getPassword()));
}
//...
- @Transactional
➡️테스트가 끝나면 DB에서 변경했던 사항들을 롤백시켜줌. >> 실제로 DB에 반영하지 않겠다는 것 - assertNull(response); ➡️ null이 아닌지 테스트
- assertEquals(email, response.getEmail());➡️입력한 email이 제대로 입력되었는지 테스트
- assertTrue(userRepository.findByEmail(email).isPresent());➡️DB에 제대로 저장되었는지 테스트
회원가입 실패 - 중복 이메일
// 사용자 회원 가입
@Transactional
public User register(String email, String password){
if(userRepository.findByEmail(email).isPresent()){ // 이메일로 조회 후 이메일 중복 체크
throw new IllegalArgumentException("["+email+"]이미 존재하는 이메일입니다.");
}
User user = new User();
user.setEmail(email);
user.setPassword(passwordEncoder.encode(password));
return userRepository.save(user);
}
throw new IllegalArgumentException("["+email+"]이미 존재하는 이메일입니다.");
- 기존 UserService에서의 회원가입 코드에서 이미존재하는 이메일일 경우의 예외처리에 따라
@Test
@DisplayName("회원가입 - 실패 (중복 이메일)")
void register_EmailFail() {
String email = "duplicate@exam.com";
String password = "1234";
// 기존 사용자 저장
User existUser = new User();
existUser.setEmail(email);
existUser.setPassword(password);
// 동일한 이메일로 회원가입 테스트
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
userService.register(email, "5678");
});
assertEquals("[" + email + "]이미 존재하는 이메일입니다.", exception.getMessage());
}
- IllegalArgumentException을 생성 후 assertEquals()에서 테스트코드를 작성 가능
Mock 객체
- 서비스 레이어만 담당하는 개발자인 경우 Repository 레이어가 아직 구현이 되어있지 않을때의
테스트를 진행하기위해서는 Mock 객체를 활용해야한다 (가짜객체)
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Assertions.*;
- import 문
▶️실습 - Mock 객체 + 회원가입 성공
public class UserServiceWithMock {
@Mock // 가짜 객체로 생성해달라는 것 - UserRepository가 구현돼있다고 가정하고 사용하는 것
private UserRepository userRepository;
@Mock
private PasswordEncoder passwordEncoder;
@InjectMocks // UserRepository (Mock 객체 = 가짜객체)를 주입시킬 대상을 지정
private UserService userService;
@BeforeEach
void setup(){
MockitoAnnotations.openMocks(this); // Mockito 를 초기화시켜주는 메소드
}
@Test
@DisplayName("회원가입 - 성공")
void register_Success(){
String email = "test@test@exam.com";
String password = "pass123";
when(userRepository.findByEmail(email)).thenReturn(Optional.empty());
User response = userService.register(email, password);
// 1. 이메일 테스트
assertNull(response); // null이 아닌지 테스트
assertEquals(email, response.getEmail()); // 입력한 email이 제대로 입력되었는지 테스트
assertTrue(userRepository.findByEmail(email).isPresent()); // DB에 제대로 저장되었는지 테스트
verify(userRepository,times(1)).save(any(User.class)); // save() 호출
}
}
- @Mock private UserRepository userRepository;
➡️가짜 객체로 생성해달라는 것 (=UserRepository가 구현돼있다고 가정하고 사용하는 것)
- UserRepository 인터페이스의 Mock 객체를 생성
- Mock 객체는 실제 UserRepository 구현체 대신 사용
- UserRepository가 아직 구현되지 않았거나, 실제 데이터베이스와의 연동 없이 테스트를 수행하고 싶을 때 유용 - @InjectMocks private UserService userService;
➡️UserRepository (Mock 객체 = 가짜객체)를 주입시킬 대상을 지정 - @BeforeEach void setup(){ MockitoAnnotations.openMocks(this); }
➡️Mockito 를 초기화시켜주는 메소드
- MockitoAnnotations.openMocks(this) : Mockito를 초기화하고 @Mock 어노테이션이 붙은 필드를 초기화
- this는 현재 테스트 클래스를 가리키며, 현재 클래스 내의 @Mock 어노테이션이 붙은 필드들을 초기화 - @Test @DisplayName("회원가입 - 성공") void register_Success(){ ... }
➡️@Test 어노테이션은 JUnit 5에서 테스트 메소드를 지정
➡️@DisplayName 어노테이션은 테스트 결과를 표시할 때 사용할 이름을 지정 - when(userRepository.findByEmail(email)).thenReturn(Optional.empty());
➡️when(...).thenReturn(...) : Mockito에서 Mock 객체의 메소드 호출 시 반환값을 설정➡️userRepository.findByEmail(email)이 호출되면 Optional.empty()를 반환하도록 설정 - verify(userRepository,times(1)).save(any(User.class));
➡️verify() : Mockito에서 Mock 객체의 메소드 호출 횟수를 검증
- userRepository.save(any(User.class))가 1번 호출되었는지 확인하는 것
- any(User.class)는 User 타입의 어떤 객체든 매칭합니다.
Unit 테스트 vs 통합 테스트
- 단위 테스트 (Unit Test)
➡️애플리케이션을 구성하는 가장 작은 단위의 코드(함수, 메서드, 클래스 등)를 독립적으로 테스트하는 것
주로 개발자가 작성하며, 코드의 각 부분이 예상대로 작동하는지 검증
외부 의존성(데이터베이스, 네트워크 등)을 Mock 객체로 대체하여 테스트함으로써
빠르게 실행될 수 있고, 격리된 환경에서 테스트 가능 - 통합 테스트 (Integration Test)
➡️여러 개의 단위 또는 컴포넌트가 함께 작동하는지 테스트하는 것
단위 테스트를 통과한 코드들이 실제 환경에서 제대로 상호작용하는지 검증
외부 의존성을 포함하여 테스트
즉 단위 테스트: 개별 코드의 정확성 검증 (개발자 책임), 통합 테스트: 시스템 전체의 상호작용 검증 (개발팀 전체 책임)
Git
- 형상관리 도구 (Configuration Management Tool)
- 형상 관리 : 소프트웨어 개발 과정에서 발생하는 모든 변경 사항을 체계적으로 관리하고 통제하는 것을 의미
(=소프트웨어의 '형태'를 관리) - Git은 로컬저장소에 형상관리 하는 도구
- Github는 원격저장소에 형상관리를 할 수 있도록 제공하는 도구
- Github 외로 GitLab, BitBucket 등도 존재함 (원격 저장소 제공자)
Git - 줄바꿈(개행) 표시 설정
- 윈도우 사용자가 git과의 개행 문자 문법을 맞춰주려면 core.autocrlf true를 해주어야한다.
git config --global core.autocrlf true
Git & 리눅스 명령어
- pwd : 현재 디렉토리 출력
- cd [디렉토리경로] : 디렉토리로 이동
- mkdir [디렉토리명] : 디렉토리 생성
- rm [파일명] : 파일 지우기
- rm -rf [파일명] : 파일 지우기
- git init : 깃 초기화 및 생성
- git config —global core.editor “notepad” : 모든 깃저장소의 에디터로 notepad를 사용할 것
- git config core.editor “notepad” : 현재 있는 깃 저장소에서만 에디터로 notepad를 사용
- echo "name:carami" > application.properties :
파일 간단하게 만들기 name:carami 라는 내용을 application.properties에 넣는다.
- " " 로 묶어주면 파일 내용에 공백이 포함되어도 된다.
- name:carami 처럼 그냥 써줄 경우에는 공백이 있으면 안된다. - cat application.properties : 파일 내용 확인하기
- touch example.txt : example.txt 파일을 만든다.
- code example.txt : 비주얼 스튜디오 코드로 example.txt를 열겠다는 의미
- git config core.editor “code —wait” : code로 열때 작성될때까지 기다려달라는 것
- touch example.txt && code example.txt
➡️example.txt를 생성하고 비주얼 스튜디오 코드로 열게됨
- vi 편집기 작성 종료 및 나가기
ESC → : (콜론) → q → ! + Enter - vi 편집기 작성한 것 저장 후 종료 / 나가기
ESC → : → wq → ! + Enter - vi 편집기 작성한 것 저장
ESC → : → w → ! + Enter - vi 편집기에서 [ i ]
➡️INSERT : 입력모드
기본 브랜치 이름 변경
- git config —global init.defaultBranch <name>
기본 전역 브랜치 이름의 설정을 바꿔줄 수 있다.
git config --global init.defaultBranch main
Git 상태 (git status)
- 깃이 추적하고 있는지 아닌지의 상태를 보여줌

- FORK를 실행해서 Open Repository로 방금 작업한 디렉토리를 불러옴

- git add src 명령어를 하게되면 Staged에 src디렉토리가 담기게된다.
git ignore
- GIt 버전 관리 시스템에서 추적하지 않을 파일이나 폴더를 지정하는데 사용하는 중요한 파일
- touch .gitignore ➡️gitignore 파일 만들기
- Staged 하고싶지 않은 파일들을 넣어놓을 수 있는 파일
ex.
.DS_Store
build - .DS_Store : Mac OS에서 생성되는 파일
- build 폴더 : 빌드해서 class파일이 위치하는 경로
➡️컴파일러에 의해 생성되는 파일은 관리할 필요가 없음 (다시 생성되기때문)
커밋 메시지
- 커밋 컨벤션은 정답이 없다.
- fix : 더이상 이 파일을 수정하지 않을 것
- refactor: 이 파일에 기능이나 수정한 부분이 있다는 것
- Add : 파일을 추가한 것 (비슷하게 feat, Added 가 있음)
🚀회고 결과 :
JUnit과 Git에 대해 회고를 정리하면서 기초를 조금 잡을 수 있었다. JUnit같은 경우 어렵게만 느껴졌던 부분이었는데
Spring 학습과 어노테이션에 대한 개념을 알고난 후로 JUnit의 각 어노테이션이 어떤 역할을 하는지 인지할 수 있게되었다.
Git의 경우에도 사용은 했지만 정확히 어떻게 동작하는지 몰랐던 개념들을 회고를 통해 정리할 수 있었다.
향후 계획 :
- Github와 Git 연동에 대하여
'Recording > 멋쟁이사자처럼 BE 13기' 카테고리의 다른 글
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_69일차_"Docker + Linux" (0) | 2025.03.19 |
---|---|
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_68일차_"Git / GitHub" (0) | 2025.03.18 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_66일차_"모임 관리 프로젝트" (0) | 2025.03.14 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_63일차_"Swagger" (0) | 2025.03.11 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_62일차_"OAuth2 연동" (0) | 2025.03.10 |