D_CUMULATIVE 컬럼은 함정이라고 보면 된다.

문제는 테이블 내 존재하는 역들의 거리를 기준으로 문제를 내고 있기 때문에 테이블에 있지도 않은 것까지 신경쓰지 않아도 된다

 

 

select 
    ROUTE, 
    CONCAT(ROUND(SUM(D_BETWEEN_DIST), 1), 'km') AS TOTAL_DISTANCE,
    CONCAT(ROUND(AVG(D_BETWEEN_DIST), 2), 'km') AS AVERAGE_DISTANCE
from SUBWAY_DISTANCE
group by ROUTE
order by TOTAL_DISTANCE DESC

 

첫 답을 제출했다 코드 실행을 했을 땐 맞았지만 채점을 하니 틀렸다고 하는 것이다

문제를 10번 이상 읽다가

드디어 실마리가 잡혀서 쿼리를 고쳤다

 

select 
    ROUTE, 
    CONCAT(ROUND(SUM(D_BETWEEN_DIST), 1), 'km') AS TOTAL_DISTANCE,
    CONCAT(ROUND(AVG(D_BETWEEN_DIST), 2), 'km') AS AVERAGE_DISTANCE
from SUBWAY_DISTANCE
group by ROUTE
order by SUM(D_BETWEEN_DIST) DESC

보여 주는 것은 소수점 첫째 자리만 보여주지만 정렬은 아니었던 것이다

ex) 51.1 과 51.19가 있다면 후자가 먼저 와야 함

 

처음 D_CUMULATIVE이 함정이다는 것을 발견해서 나머지를 지문을 신경 못 쓴 것이 아닌가 싶다

 

 

 

select CAR_ID, ROUND(avg(DATEDIFF(END_DATE, START_DATE)), 1) as AVERAGE_DURATION 
	from CAR_RENTAL_COMPANY_RENTAL_HISTORY
	group by car_id 
    	having AVERAGE_DURATION >= 7
order by AVERAGE_DURATION desc, car_id desc

 

첫 풀이 쿼리다.

문제가 없어 보였지만 3월 11일에 빌리고 당일에 반납해도 하루 빌린 것이라는 것을 잊고 있었다

 

select CAR_ID, ROUND(avg(DATEDIFF(END_DATE, START_DATE) + 1), 1) as AVERAGE_DURATION 
	from CAR_RENTAL_COMPANY_RENTAL_HISTORY
	group by car_id 
    	having AVERAGE_DURATION >= 7
order by AVERAGE_DURATION desc, car_id desc

+1만 해주니 정답이었다

틀린 것이 뭔지 찾느라 고민 많이 했는데 내 짜증도 +1

 

 

 

 

처음 테이블이 3개가 나와 전부 조인을 해야 하나 싶었지만 결과는 HR_EMPLOYEES와 HR_GRADE만 해도 되는 것을 알았다

select sum(g.score) as SCORE, e.emp_no, e.emp_name, e.position, e.email 
from hr_employees e 
	join hr_grade g 
    		on e.emp_no = g.emp_no 
where g.year = 2022 
group by e.emp_no 
order by SCORE desc limit 1

이렇게 해도 정답 평가 점수 1등이 2명 이상일 수도 있다고 생각이 들어서 아래처럼 수정했다

 

select sum(g.score) as SCORE, e.emp_no, e.emp_name, e.position, e.email 
from hr_employees e 
    join hr_grade g 
        on e.emp_no = g.emp_no
where g.year = 2022 
group by e.emp_no having SCORE = (
    select sum(score)as SCORE
    from hr_grade
    where year = 2022 
    group by emp_no
    order by SCORE desc
    limit 1
    )
order by SCORE desc

 

사실 year 컬럼도 2022라고 지정을 해주지 않아도 정답이고 1등도 한 명뿐이라 limit으로도 되지만 그냥 해보고 싶었다

 기본 명령어


  • k6 run script.js
    • 기본 실행 명령어
  • k6 run --vus 10 --duration 30s script.js
    • VU/시간으로 간단 부하 
    • 10명이 30초 동안 계속 요청
  • k6 run --stage 10s:10 --stage 30s:50 --stage 10s:0 script.js
    • 단계로 램프업/유지/램프다운
    • 10초 동안 10VU까지 증가, 30초 동안 50VU 유지, 10초 동안 0VU으로 감소
  • BASE_URL="http://localhost:8080" TOKEN="abc" k6 run script.js
    • 환경변수 전달 (host, token)

스크립트에선 이런 방식으로 받음

const BASE_URL = __ENV.BASE_URL;
const TOKEN = __ENV.TOKEN;
  • k6 run --summary-export=summary.json script.js
    • 결과 용약/리포트 파일로 저장
  • docker run --rm -i grafana/k6 run - < script.js
    • Docker로 실행

 

스크립트

 


import http from "k6/http"

- HTTP요청 http.get/post/put/del

export default function () {
  http.get("http://localhost:8080/health");

  http.post("http://localhost:8080/login", JSON.stringify({ id: "a", pw: "b" }), {
    headers: { "Content-Type": "application/json" },
  });

  // 여러 요청을 병렬로
  http.batch([
    ["GET", "http://localhost:8080/a"],
    ["GET", "http://localhost:8080/b"],
  ]);
}

import { check } from "k6";

- 검증

export default function () {
  const res = http.get("http://localhost:8080/health");
  check(res, {
    "status is 200": (r) => r.status === 200,
    "body has ok": (r) => r.body.includes("ok"),
  });
}

import { sleep } from "k6";

- 대기

export default function () {
  sleep(1);
}

 

 

import { group } from "k6";

- 구간 묶기

export default function () {
  group("login", () => {
    const res = http.post("http://localhost:8080/login", JSON.stringify({ id: "a", pw: "b" }), {
      headers: { "Content-Type": "application/json" },
    });
    check(res, { "login 200": (r) => r.status === 200 });
  });

  group("buy ticket", () => {
    const res = http.post("http://localhost:8080/ticket/buy", null);
    check(res, { "buy ok": (r) => r.status === 200 || r.status === 201 });
  });
}

 

thresholds

-성능 합격/불합격 기준

export const options = {
  thresholds: {
    http_req_failed: ["rate<0.01"],        // 실패율 1% 미만
    http_req_duration: ["p(95)<500"],      // p95 500ms 미만
  },
};

 

메트릭 직접 만들기

import { Counter, Rate, Trend } from "k6/metrics";
import http from "k6/http";

const success = new Counter("success_count");
const failRate = new Rate("fail_rate");
const latency = new Trend("api_latency_ms");

export default function () {
  const res = http.get("http://localhost:8080/health");
  latency.add(res.timings.duration);

  const ok = res.status === 200;
  failRate.add(!ok);
  if (ok) success.add(1);
}

 

시나리오(executors): 도착률 기반(초당 N회) 같은 오픈 모델

export const options = {
  scenarios: {
    open_model: {
      executor: "constant-arrival-rate",
      rate: 200,           // 초당 200 iteration
      timeUnit: "1s",
      duration: "30s",
      preAllocatedVUs: 50, // 미리 확보할 VU
      maxVUs: 200,
    },
  },
};

 

setup() / teardown() (테스트 전/후 1번만)

import http from "k6/http";

export function setup() {
  // 예: 로그인해서 토큰 1번만 받아서 공유
  const res = http.post("http://localhost:8080/login", JSON.stringify({ id: "a", pw: "b" }), {
    headers: { "Content-Type": "application/json" },
  });
  return { token: res.json("token") };
}

export default function (data) {
  http.get("http://localhost:8080/me", {
    headers: { Authorization: `Bearer ${data.token}` },
  });
}

export function teardown(data) {
  // 예: 테스트 후 정리 요청
}

 

일반 부하(램프업 → 유지)

import http from "k6/http";
import { check, sleep } from "k6";

const BASE_URL = __ENV.BASE_URL || "http://localhost:8080";

export const options = {
  stages: [
    { duration: "30s", target: 50 },
    { duration: "1m", target: 50 },
    { duration: "10s", target: 0 },
  ],
  thresholds: {
    http_req_failed: ["rate<0.01"],
    http_req_duration: ["p(95)<800"],
  },
};

export default function () {
  const res = http.get(`${BASE_URL}/ticket/list`);
  check(res, { "200": (r) => r.status === 200 });
  sleep(0.3);
}

부하 테스트란 성능 테스트의 하위 개념으로 일반적으로 시스템이 정상 사용량과 최대 사용량에서 어떻게 반응하는지 테스트하는 것을 말하며 주로 응답 속도 저하, 오류, 시스템 충돌 및 기타 문제를 찾아 시스템 성능 저하 없이 처리할 수 있는 사용자 및 트랜잭션 수를 파악하는 것이 목적인 것이다

 

목적을 위해서는 k6와 같은 도구를 이용하여 가상 사용자와 시뮬레이션된 데이터 볼륨(시스템이 처리/저장/전송해야 하는 데이터의 양)으로 시스템을 테스트하고 추가 부하가 성능에 미치는 영향을 확인한다. 또한 응답 시간, 처리량, 리소스 사용률을 모니터링하고 측정하여 시스템을 배포하기 전에 해결해야 할 잠재적인 병목 현상이나 확장성 문제를 감지한다.

 

부하 테스트 vs 성능 테스트

부하 테스트는 앞서 말했듯 가상의 사용자를 시뮬레이션하여 시스템이 증가된 트래픽이나 부하를 얼마나 잘 처리할 수 있는지 확인하는 것이다

 

성능 테스트는 시스템이나 애플리케이션의 전반적인 성능을 측정하는 포괄적인 용어이다

여기에는 속도, 확장성, 안정성 및 리소스 활용도 테스트가 포함되어 개선 영역을 파악할 수 있다

성능 테스트에는 부하 테스트뿐 아니라 브라우저 성능 테스트 및 합성 모니터링과 같은 다른 유형의 테스트까지 포함하는 것을 말한다

 

부하 테스트의 종류


애플리케이션 성능은 특정 시점에 처리하는 트래픽의 양과 지속 시간에 따라 달라진다

예를 들어 100명이 사용할 때 10ms 걸리던 것이 10,000명이 사용한다고 1,000ms로 정직하게 증가하지 않는다

k6에서 말하는 일반적인 부하 테스트 유형은 여섯 개다

 

  • Smoke test(스모크 테스트)
    • 애플리케이션이 최소 부하 상태에서 제대로 작동하는지 확인
    • 기준 성능값을 수집하는 데 사용
    • 소수의 사용자 예를 들어 10명의 가상 사용자가 10초 동안 요청하는 테스트처럼 반복 횟수가 적거나 실행 시간이 몇 초에서 최대 몇 분 정도인 테스트
  • Average-load test(평균 부하 테스트)
    • 애플리케이션이 일반적인 부하 조건에서 어떻게 작동하는지 평가
    • 하루 평균 부하 또는 특정 시간대의 부하를 테스트하며 일상 운영 테스트 또는 볼륨 테스트라고도 한다
    • 실제 운영 환경에서 평균적인 동작을 반영하여 초당 동시 사용자 수와 요청 수를 시뮬레이션 한다
  • Stress test(스트레스 테스트)
    • 러시아워 테스트, 서지 테스트 또는 스케일 테스트라고도 한다
    • 한계치(느려지거나 터지는 지점)를 찾는 것이 목적이다
    • 평균보다 높은 부하로 점진적으로 수치를 올려가며 지점을 찾아가는 방식
  • spike test(스파이크 테스트)
    • 갑작스러운 대규모 사용량 급증 상황에서도 안정적으로 작동하는지 확인
    • 매우 짧은 시간 또는 시간 증가 없이 극도로 높은 부하를 주며 부하 감소 또한 매우 빠르고 시간 증가 없이 진행되므로 테스트는 보통 한 번만 반복된다
  • Soak / Endurance test(소크 / 내구성 테스트)
    • 한계를 파악하는 데 사용된다 용량 / 포인트로드 / 한계 테스트라고도 한다
    • 애플리케이션이 언제 고장날지, 어디서 어떻게 고장나기 시작하는지 미리 파악하는 것이 목적이다
    • 중간 ~ 높은 부하를 오랜 시간 유지하여 특정 지점을 파악한다
  • Breakpoint test(브레이크 포인트 테스트)
    • Average-load test의 변형으로 주요 차이점은 테스트 기간이다
    • 테스트 방식은 수시간 또는 며칠간 지속되고, 부하를 단계적으로 올리며 임계점을 기록한다
    • 시스템의 실제 수용한계 파악, 성능저하 시작지점 확인 및 장애발생 패턴 분석, 시스템 튜닝 포인트 도출이 목적이다
유형 VU(동시 사용자) / 처리량 지속 기간 목적
Smoke 낮음 짧음 (N초 ~ N분) 관련 코드가 변경될 때 기능 논리, 기준 지표 및 편차를 확인
Average-load 실 서비스의 평균 부하만큼 중장시간(5~60분) 평균적인 사용량에서 성능을 유지하는지 자주 확인
Stress 높음(평균 이상) 중장시간(5~60분) 평균 이상의 부하를 받을 때 어떻게 처리하는지 확인
Spike 매우 높음 짦음(N분) 사용량이 갑자기 늘어날 것이 예상되거나 잦은 트래픽 급증을 처리할 때
Soak 평균 장시간 장기간 연속 사용 후 점검 시스템 변경 사항 적용
BreakPoint 장애가  발생할 때까지 필요한 만큼 상한 값을 찾기 위해 여러 번 시도

 

 

테스트 가이드


Somke test 선행하기

더 큰 규모의 테스트를 지작하기 전에 작성한 코드가 의도대로 작동하는지, 소수의 사용자가 시스템을 사용할 때 시스템 성능이 양호한지 확인 해야한다

 

그 이후엔 사례마다 다르게 적용하기

티켓 예매 같은 경우엔 순간적으로 트래픽이 급등하는 경우가 많기에 Spike / Breakpoint  같은 테스트들이 선행되어야 한다

 

커뮤니티같이 꾸준히 트래픽이 있는 경우엔 Average-load / Soak / Stress 같은 테스트가 우선시 되어야 한다

 

하지만 커뮤니티도 갑작스럽게 트래픽이 폭증할 수도 있고 티켓의 경우도 꾸준히 증가할 가능성이 없지 않기에 결국엔 전부 테스트하는 것이 좋지만 우선순위를 잡고 하는 것이 좋다

 

단순한 설계와 재현 가능한 결과를 목표로 하자

구체적인 사항은 상황에 따라 각각 다르지만, 공통적인 것은 비교하고 해석할 수 있는 결과를 도출해야 한다는 것이다

 

단순한 부하 패턴을 고수해야한다. 모든 테스트 유형에서 방향성은 비슷하다 (점진적 증가, 유지, 감소)

 

부하가 여러 번 증가하고 감소하는 롤러코스터형 은 오히려 독이 될 수 있다. 이런 경우는 일반적으로 자원을 낭비하고 문제 발생 원인을 파악하기 어렵게 만들기 때문이다

 

정리


결국 부하 테스트를 함으로써 얻는 것은 언제, 어디서, 사용자가 얼마나 몰리면 속도가 느려지는지를 미리 파악하여 대비하고, 스모크 테스트를 이용하여 오류를 잡으며 긍정적인 경험을 사용자에게 주기 위함으로 보인다

 

테스트의 6가지 종류 중 스모크 테스트를 선행하는 것은 어떠한 유형이든 선행이 되어야 하며 서비스의 맞게 나머지 5종류 중 우선순위를 정하여 진행하는 것을 권하고 있다

 

우리 서비스의 맞춘다고 부하를 여러 번 증가 또는 감소시키는 것은 오히려 독이 될 수 있다.

차라리 점진적 증가, 유지, 점진적 감소를 테스트하는 것이 좋으며 그래도 문제점을 못 찾을 때 시도해 봐도 나쁘지 않을 것 같다

'테스트' 카테고리의 다른 글

[JAVA] Mockito, Junit5 annotation 정리  (0) 2026.01.24
[JAVA]Mock  (0) 2026.01.22
JUnit5  (0) 2025.10.15
Unit Test (단위 테스트)  (0) 2025.09.29
TDD  (0) 2025.09.23

DB 동시성


개발을 하다 보면 여러 요청(트랜잭션) 이 같은 시간대에 같은 데이터에 접근(조회, 수정 등) 하는 상황(동시성)이 자주 나온다

 

서버에는 수많은 요청이 동시에 들어오는 상황에서 DB는 빠르게 처리하면서도 데이터가 꼬이지 않게(정합성) 만드는 장치가 필요한데 그 장치를 동시성 제어(Concurrency Control)라고 부른다

 

동시성 제어를 제대로 하지 않는다면 다음과 같은 문제가 생길 수 있다

  • Dirty Read
    • 아직 커밋 되지 않은 데이터를 다른 트랜잭션이 읽는 현상
  • Non-repeatable Read
    • 같은 트랜잭션에서 같은 레코드를 두 번 조회했을 때 값이 달라지는 현상
  • Phantom Read
    • 특정 조건으로 레코드들을 두 번 조회했을 때 중간에 insert/delete같은 작업이 실행되어 레코드의 개수가 달라지는 현상
  • Lost Update
    • 좌석이 1개 남았는데 여러 사용자가 동시에 남은 좌석을 보고 다 결제 성공 처리를 하여 최종 좌석이 0보다 아래로 내려가는 현상

동시성 제어에는 여러 가지 방법이 있지만 오늘은 Lock 통한 제어를 정리해 보았다

낙관적 락(Optimistic Lock)


낙관적 락이란 버전 컬럼을 두어 수정될 때마다 count를 증가시키거나 타임스탬프를 최신화하는 방식이다

이를 통해 레코드를 조회하고 최종적으로 수정하기 직전 같은 레코드를 조회하여 버전을 비교 후 일치하다면 수정, 아니라면 예외를 발생시킨다

  1. 수정할 레코드 조회
  2. 수정 직전 동일 레코드 조회
    • 버전이 일치하다면 수정
    • 아니라면 예외

작업 끝에 충돌 감지를 하는 낙관적 락은 일반적으로 문을 걸어 잠그고 시작하는 비관적 락에 비해 빠르다

보통 충돌이 거의 일어나지 않을 것으로 예상되는 테이블에 적용하는 것이 좋다

그 이유는 충돌이 일어났을 경우 재시도를 하는 로직이나 UI 레벨에서 다른 사람이 먼저 수정했음을 알리고 다시 시작하는 정책이 필요한데 충돌이 자주 발생된다면 빠르다는 강점이 퇴색될 수 있기 때문이다

 

비관적 락(Pessimistic Lock)


트랜잭션에서 데이터를 조회하면서 잠금을 얻고 트랙잭션이 끝날 때 반납하는 것을 말한다

낙관적 락보다 강한 수준으로 다른 트랜잭션의 접근을 차단한다

 

보통 충돌이 자주 나는 상황 / 반드시 순서대로 처리해야 하는 상황/ 한 번에 성공, 실패를 확정해야 하는 상황에서 주로 쓰이며

주의할 점으로는

  • 트랜잭션을 최대한 짧게 잡기
    • 락을 얻고 외부 API를 호출하거나 긴 로직을 돌린다면 대기가 매우 길어짐
  • 데드락/타임아웃 설계
    • 여러 레코드/테이블을 잠글 때는 항상 같은 순서로 잠그기
    • 락 타임아웃(대기 제한)
  • 최소한의 범위만 잡기
    • 필요보다 범위를 넓게 잡는다면 병목이 커짐

비교


구분 비관적 락 낙관적 락
충돌 처리 지점 시작(읽기)에서 대기/차단 끝(쓰기/커밋)에서 충돌 감지
성능 / 확장성 경쟁이 심할수록 급격히 악화(대기열) 경쟁이 낮으면 매우 유리(락 거의 없음)
실패 형태 타임아웃 / 데드락 / 대기 증가 OptimisticLockException 등 충돌 예외
구현 포인트 락을 잡은 후 최대한 짧게 끝내기 버전 컬럼, 충돌 시 재시도 전략
잘 맞는 상황 같은 레코드를 많은 트랜잭이 동시에 수정 대부분 읽기, 가끔 충돌

'CS > DB' 카테고리의 다른 글

트랜잭션 격리 수준 (Transaction Isolation Level)  (1) 2025.10.13
[DB]인덱스 (B-Tree, B+Tree)  (0) 2025.09.13

Mockito


@Mock

  • Mockito가 제공하는 단순 mock 객체 생성
  • Spring container와는 무관
  • 순수 자바 환경에서의 단위 테스트용

@InjectMocks

  • 테스트 객체에 생성자나 setter를 통해 mock 필드를 주입
  • A, B, C에 의존성을 가지고 있는 객체 D가 있고 A 객체 에만 @Mock annotation을 붙인 경우 나머지 B, C는 null이 들어감(B, C를 사용하려 할 경우 예외)

@MockitoBean

  • @Mock과 비슷하지만 Spring 환경에서 의존성을 대체할 때 쓰임
  • 실제 Srping context에서 bean을 mock으로 대체

@ExtendWith(MockitoExtension.class)

  • Junit5 이상에서 사용가능 그 밑으로는 RunWith사용
  • 위에 @Mock, @InjectMocks와 추가로 @Spy, @Captor에 자동으로 객체를 만들거나 주입해 주는 역할
  • 이 annotation을 사용하지 않고 아래 사진처럼 사용할 수도 있다

 

Junit


 

@SpringBootTest

  • Spring boot application을 실제로 부팅해서 ApplicationContext를 만든 뒤 그 위에서 테스트를 하겠다는 의미
  • 부트 외부 설정, 로깅, 자동설정, 웹서버 옵션까지 포함해 실제 Runtime에 가까운 형태로 테스트할 때 사용

@Testcontainers

  • 클래스에 붙이면 Testcontainers가 테스트 라이프 사이클에 맞춰 @Container로 선언된 컨테이너를 자동으로 시작하고 종료해 준다

@Container

  • @Testcontainers가 붙은 테스트 클래스에서 리플렉션으로 필드를 훑음
  • @Container가 붙은 필드를 찾고 그 컨테이너에 라이프사이클 메서드(start(), stop() 등)를 Junit 테스트 라이프사이클에 맞춰 자동 호출
  • @Container를 주로 사용하는 이유
    • 테스트 실행 환경에서 Redis, Postgres/ Kafka 등을 자동으로 띄움
      1. 로컬에 설치돼있지 않아도 테스트가 알아서 인프라를 준비
      2. Docker 위에서 실행한다면 Docker는 띄워져 있어야 한다
    • 포트 충돌 회피
      1. 컨테이너 내부 포트는 그대로지만, 호스트에는 랜덤 포트로 매핑돼 충돌이 줄어든다
    • 테스트 격리/청결성
      1. 클래스 A에서 a,b,c가 같은 redis를 사용하면 전 메서드에서 들어온 데이터가 남아있을 수 있다
      2. 그걸 원치 않는다면 static이 아닌 필드로 선언해 주면 된다 상관없다면 static을 사용해 오버헤드를 줄일 수 있다
    • 배포 환경과 동일한 버전으로 테스트

 

 

 

'테스트' 카테고리의 다른 글

[k6] 부하 테스트  (0) 2026.02.13
[JAVA]Mock  (0) 2026.01.22
JUnit5  (0) 2025.10.15
Unit Test (단위 테스트)  (0) 2025.09.29
TDD  (0) 2025.09.23

Mocking이란?


mock 이란 모조품, 가짜, 흉내 등의 뜻이며 테스트를 하면서 객체가 의존하는 모듈을 mock으로 대체하는 기법을 말한다.

 

예를 들면 회원가입 기능에서 실제 DB를 mock으로 대체하여 회원가입 로직만을 테스트하고 싶을 때 사용한다

ex) 비밀번호 암호화, ID 중복 확인 등

 

mock 으로 굳이 대체하는 이유는?


만약 컨트롤러부터 데이터베이스까지 매끄럽게 동작하는 것을 테스트하는게 목적이라면 꼭 쓰지 않아도 된다

하지만 특정 기능(비밀번호 암호화, ID 중복 확인 등)을 테스트 하는게 목적이라면 mock을 사용하는 것이 좋다.

 

 

비밀번호 암호화의 대한 코드를 테스트 하기 위해서 DB(Repository)까지 값을 넣는 것은 좋지않다

 

데이터 베이스 입출력으로 인해서 테스트 시간이 늘어나기도 하고, 데이터 베이스 서버의 상태나 연결 상태 등 고려해야 할 상황도 많아지기 때문에 시간적 비용이나 테스트 난이도도 함께 상승한다

 

그렇기 때문에 mock으로 대체하는 것인데 내가 id와 password를 주면 데이터 베이스에 잘 들어갔다고 치고 값을 반환해 줘 라는 개념으로 이해하면 될 것 같다

 

Mockito


JAVA에서 이렇게 mock을 사용할 수 있게 해주는 오픈소스 프레임워크를 Mockito라고 부른다

 

  • Mockito 수행과정
    • 모의 객체 생성 : mock
      1. Mockito를 사용하여 테스트에 필요한 모의 객체를 생성
    • 메서드 호출 예상 동작 설정 : Stub
      1. save() 라는 메서드를 동작할 때 실제로 DB에 저장됐다 치고 값을 반환
      2. 메서드에 들어갈 매개변수와 반환될 값은 미리 설정할 수 있다 (save(member) 가 호출된다면 1L을 반환해 줘)
    • 메서드 호출 검증Verify
      1. 모의 객체에 대해 특정 메서드가 호출되었고 예상된 값이 함께 호출되었는지를 검증
      2. save()가 호출되었는지, 한 번 호출되었고 1L을 반환했는지 등

'테스트' 카테고리의 다른 글

[k6] 부하 테스트  (0) 2026.02.13
[JAVA] Mockito, Junit5 annotation 정리  (0) 2026.01.24
JUnit5  (0) 2025.10.15
Unit Test (단위 테스트)  (0) 2025.09.29
TDD  (0) 2025.09.23

+ Recent posts