Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Test] TestContainers와 H2로 DB 테스트하기 #92

Merged
merged 24 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f08d209
docs: Swagger 어노테이션 추가
DongminL Dec 6, 2024
ced5dc3
Merge branch 'main' into test/#86-testcontainers와-h2로-db-테스트하기
DongminL Dec 6, 2024
6f3e651
test: Email Test, TestContainers, H2 의존성 추가
DongminL Dec 7, 2024
88403df
test: Test Profile 설정에서 제외
DongminL Dec 7, 2024
7554ae3
test: 각 단계별 테스트 셋팅
DongminL Dec 7, 2024
87636c0
refactor: JPA 테스트 시, 오류 예방
DongminL Dec 7, 2024
857f92a
test: 테스트 환경에서 Redis 설정
DongminL Dec 7, 2024
e301787
test: Redis 사용 시 TestContainer를 사용
DongminL Dec 7, 2024
1b30562
test: 이메일 송수신하는 서비스 테스트
DongminL Dec 7, 2024
260d4fb
refactor: TestContainer를 사용하여 테스트
DongminL Dec 7, 2024
8c7e22b
feat: h2 DB를 사용하도록 설정
DongminL Dec 7, 2024
681ff87
refactor: h2 DB를 사용하고, .sql 파일로 더미 데이터 추가하여 테스트하기
DongminL Dec 7, 2024
c9510b0
refactor: h2 DB를 사용하고, .sql 파일로 더미 데이터 추가하여 테스트하기
DongminL Dec 7, 2024
5d7d67a
test: test용 설정 파일
DongminL Dec 7, 2024
0e81720
style: 테스트 초기 설정 패키지명 변경
DongminL Dec 7, 2024
d844fbd
Merging main onto test/#86-testcontainers와-h2로-db-테스트하기
DongminL Dec 7, 2024
1773320
test: CI 테스트
DongminL Dec 7, 2024
8f7af07
test: test 환경에서 spring batch 설정
DongminL Dec 7, 2024
8429107
test: test 환경에서 spring batch의 DB를 H2로 설정
DongminL Dec 7, 2024
d055f26
fix: spring batch가 h2 db에 적용되도록 설정
DongminL Dec 7, 2024
b0c4567
fix: spring batch가 h2 db에 적용되도록 설정2
DongminL Dec 7, 2024
9452b6d
fix: spring batch가 h2 db에 적용되도록 설정3
DongminL Dec 7, 2024
fde150c
fix: spring batch가 h2 db에 적용되도록 설정4
DongminL Dec 7, 2024
38fc6b6
fix: 실패하는 테스트에 @AutoConfigureTestDatabase 명시
DongminL Dec 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/pr-ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: PR 시 CI 테스트 자동화

on:
pull_request:
branches:
- main

jobs:
test:
name: Test
runs-on: ubuntu-latest

permissions:
checks: write
pull-requests: write

steps:
- name: 레포지토리 체크아웃
uses: actions/checkout@v4
with:
token: ${{ secrets.GIT_TOKEN }}
- run: touch ./src/main/resources/security.properties
- run: echo "${{ secrets.SECURITY_PROP }}" > ./src/main/resources/security.properties

- name: JDK 17 설치
uses: actions/setup-java@v4
with:
distribution: 'oracle'
java-version: '17'
cache: 'gradle'

- name: Gradle 종속성 캐시
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-

- name: Gradle 명령 실행 권한 부여
run: chmod +x ./gradlew
shell: bash

- name: Gradle로 프로젝트 Build
run: ./gradlew build

- name: 테스트 결과를 PR 코멘트로 출력
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: '**/build/test-results/test/TEST-*.xml'

- name: 테스트 실패 시, 오류가 발생한 코드 라인에 코멘트 추가
uses: mikepenz/action-junit-report@v4
if: always()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
16 changes: 11 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,35 +36,41 @@ dependencies {

// Mail
implementation 'org.springframework.boot:spring-boot-starter-mail'
testImplementation 'com.icegreen:greenmail-junit5:2.1.1'
testImplementation 'commons-codec:commons-codec:1.17.1'

// Security
implementation 'org.springframework.boot:spring-boot-starter-security'

// DB
implementation 'org.mariadb.jdbc:mariadb-java-client:3.1.2'
runtimeOnly 'com.h2database:h2'

// Redis 추가
// Redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'

//jwt
// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'

// mybatis
// MyBatis
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.4'

// lombok
// Lombok
annotationProcessor 'org.projectlombok:lombok'
compileOnly 'org.projectlombok:lombok'

// test
// Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.testcontainers:testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'

//Querydsl 추가
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
Expand Down
1 change: 0 additions & 1 deletion src/main/java/com/example/shop/ShopApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
@PropertySource("classpath:security.properties")
public class ShopApplication {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import org.springframework.web.bind.annotation.*;

@Tag(name="Auth")
@RestController()
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/api/auth")
public class AuthController {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.shop.global.config.db;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class JpaConfig {
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
Expand All @@ -12,6 +13,7 @@
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Profile("!test")
@Configuration
@EnableRedisRepositories
public class RedisConfig {
Expand Down
2 changes: 2 additions & 0 deletions src/test/java/com/example/shop/ShopApplicationTests.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.example.shop;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
class ShopApplicationTests {

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
package com.example.shop.admin.dao;

import com.example.shop.admin.dto.OrderDeliveryRequest;
import com.example.shop.global.setting.RedisTest;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Set;

import static com.example.shop.batch.util.OrderDeliveryBatchUtil.getOrderKeyYesterday;
import static org.assertj.core.api.Assertions.assertThat;


@SpringBootTest
class OrderDeliveryRepositoryTest {
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
class OrderDeliveryRepositoryTest extends RedisTest {

@Autowired
private OrderDeliveryRepository orderDeliveryRepository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,45 @@
import com.example.shop.domain.order.Order;
import com.example.shop.domain.order.OrderRepository;
import com.example.shop.domain.order.OrderStatus;
import com.example.shop.domain.user.Role;
import com.example.shop.domain.user.User;
import com.example.shop.domain.user.UserRepository;
import com.example.shop.global.setting.ServiceTest;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.jdbc.Sql;

import java.util.ArrayList;
import java.util.List;

import static com.example.shop.batch.util.OrderDeliveryBatchUtil.getOrderKeyYesterday;
import static org.mockito.Mockito.doNothing;

@Transactional
@SpringBootTest
class OrderUpdateSenderWriterTest {

@Autowired
@ExtendWith(MockitoExtension.class)
@Sql(scripts = "classpath:/sql/order-data.sql",
executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
class OrderUpdateSenderWriterTest extends ServiceTest {

@MockBean
private AdminOrderService adminOrderService;
@Autowired
private OrderDeliveryRepository orderDeliveryRepository;
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserRepository userRepository;

@BeforeEach
void setup() {
User user = User.builder()
.email("[email protected]")
.password("test")
.userRole(Role.ROLE_USER)
.orders(new ArrayList<>())
.userName("tester")
.build();

user = userRepository.save(user);

for (long i = 1; i <= 532; i++) {
for (long i = 1; i <= 5; i++) {
orderDeliveryRepository.addOrderEmail(getOrderKeyYesterday(),
new OrderDeliveryRequest(user.getEmail(), i+"test"));
new OrderDeliveryRequest("[email protected]", i+"test"));
}
}

Expand All @@ -70,7 +60,7 @@ void write() {
sqlSession.flushStatements();

} finally {
orderDeliveryRequestList.forEach(adminOrderService::sendDeliveryAlertEmail);
orderDeliveryRequestList.forEach(doNothing().when(adminOrderService)::sendDeliveryAlertEmail);
orderDeliveryRepository.removeOrderEmail(getOrderKeyYesterday(), orderDeliveryRequestList.toArray());
}

Expand Down
30 changes: 30 additions & 0 deletions src/test/java/com/example/shop/global/config/RedisTestConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.shop.global.config;

import com.example.shop.admin.dto.OrderDeliveryRequest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@TestConfiguration
@EnableRedisRepositories
public class RedisTestConfig {

@Bean
public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);

// JSON 형식으로 직렬화
Jackson2JsonRedisSerializer<OrderDeliveryRequest> serializer = new Jackson2JsonRedisSerializer<>(OrderDeliveryRequest.class);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(serializer);

return redisTemplate;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.example.shop.global.config;

import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

@Testcontainers
public class RedisTestContainer {

private static final String REDIS_IMAGE = "redis:7.4.1-alpine";
private static final int REDIS_PORT = 6379;
private static final GenericContainer<?> REDIS_CONTAINER;

static {
REDIS_CONTAINER = new GenericContainer<>(DockerImageName.parse(REDIS_IMAGE))
.withExposedPorts(REDIS_PORT)
.withReuse(true);
REDIS_CONTAINER.start();
}

@DynamicPropertySource
private static void registerRedisProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.redis.host", REDIS_CONTAINER::getHost);
registry.add("spring.data.redis.port", () -> REDIS_CONTAINER.getMappedPort(REDIS_PORT));
}
}
63 changes: 63 additions & 0 deletions src/test/java/com/example/shop/global/setting/ControllerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.example.shop.global.setting;

import com.example.shop.admin.controller.AdminOrderController;
import com.example.shop.admin.controller.AdminProductController;
import com.example.shop.admin.service.AdminOrderService;
import com.example.shop.admin.service.AdminProductService;
import com.example.shop.auth.controller.AuthController;
import com.example.shop.auth.service.AuthService;
import com.example.shop.global.config.auth.JwtAuthenticationFilter;
import com.example.shop.user.controller.CartController;
import com.example.shop.user.controller.OrderController;
import com.example.shop.user.controller.ProductController;
import com.example.shop.user.service.CartService;
import com.example.shop.user.service.OrderService;
import com.example.shop.user.service.ProductService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;

@ActiveProfiles("test")
@ExtendWith(MockitoExtension.class)
@WebMvcTest({
AdminOrderController.class,
AdminProductController.class,
AuthController.class,
CartController.class,
OrderController.class,
ProductController.class
})
public abstract class ControllerTest {

@Autowired
protected MockMvc mockMvc;

@Autowired
protected ObjectMapper objectMapper;

@MockBean
protected JwtAuthenticationFilter jwtAuthenticationFilter;

@MockBean
protected AdminOrderService adminOrderService;

@MockBean
protected AdminProductService adminProductService;

@MockBean
protected AuthService authService;

@MockBean
protected CartService cartService;

@MockBean
protected OrderService orderService;

@MockBean
protected ProductService productService;
}
Loading
Loading