본문 바로가기
서버/Kotlin-Spring_Boot

Kotlin-Spring_Boot 강의 정리) 5. Service Layer

by HDobby 2023. 1. 29.

https://youtu.be/Icnb3zvya-w

기본 프로젝트 아래에 service 폴더와 BankService class 파일을 생성해 줍니다.

Service 작업은 비즈니스 로직(데이터 생성, 저장, 가공 등)과 Repository Layer를 호출하는 작업입니다.

 

package com.study.hello_world.service

import org.springframework.stereotype.Service

@Service
class BankService {
	
}

BankService에 @Service 어노테이션을 달아줍시다.

이번엔 Service를 테스트하기 위한 테스트 파일을 작성해줍니다.

package com.study.hello_world.service

import com.study.hello_world.datasource.BankDataSource
import org.junit.jupiter.api.Test

internal class BankServiceTest {

	private val dataSource: BankDataSource
	private val bankService = BankService()

	@Test
	fun `should call its data source to retrieve banks`() {
		// given


		// when
		val banks = bankService.getBanks()

		// then


	}

}

test 템플릿을 사용해 위와 같이 변경 해줍니다.

bankService.getBanks()의 경우 아직 함수가 선언되어 있지 않아 에러가 나오게 됩니다.

dataSource가 필요할지 아닐지 모르지만 미리 선언을 해줍니다.

package com.study.hello_world.service

import com.study.hello_world.datasource.BankDataSource
import org.springframework.stereotype.Service

@Service
class BankService(private val dataSource: BankDataSource) {

}

BankService의 인자로 dataSource를 추가 해줍니다.

이를 사용하기 위해 dependencies를 수정해줘야 하므로 build.gradle.kts로 이동해줍니다.

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web")
	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
	testImplementation("org.springframework.boot:spring-boot-starter-test")
	testImplementation("io.mockk:mockk:1.10.4")
}

dependencies에 mockk를 사용하기 위해 testImplementation("io.mockk:mockk:1.10.4")를 추가해줍니다.

Ctrl+Shift+O를 눌러 Load Gradle Change를 해줍니다.(그래야 변경한 dependency 라이브러리를 다운받습니다.)

internal class BankServiceTest {

	private val dataSource: BankDataSource = mockk()
	private val bankService = BankService(dataSource)

	@Test
	fun `should call its data source to retrieve banks`() {
		// given


		// when
		val banks = bankService.getBanks()

		// then


	}

}

BankServiceTest로 이동해 dataSource 부분을 수정해줍니다.

mockk는 이전에 사용했었던 Assertions 처럼 데이터 검증을 하기 위한 라이브러리 입니다.

@Service
class BankService(private val dataSource: BankDataSource) {
	fun getBanks(): Collection<Bank> {
		return emptyList()
	}
}

BankService에 getBanks 함수를 추가해줍니다.

@Test
	fun `should call its data source to retrieve banks`() {
		// given


		// when
		val banks = bankService.getBanks()

		// then
		verify(exactly = 1) { dataSource.retrieveBanks() }

	}

then 부분에

verify(exactly = 1) { dataSource.retrieveBanks() }

가 추가 되었습니다. 

정확하게 1번만 실행하기 위해 exactly = 1을 넣어 주고, dataSource.retrieveBanks()가 동작하는지를 검증해줍니다.

 

테스트를 실행해 봅시다.

emptylist를 return 해줬었기 때문에 에러가 발생했습니다.

@Service
class BankService(private val dataSource: BankDataSource) {
	fun getBanks(): Collection<Bank> {
		return dataSource.retrieveBanks()
	}
}

BankService의 내용을 고치고 다시 실행을 해보면?

또다시 에러가 발생합니다.

 

우리가 만들었던 BankDataSource 가 빈 오브젝트인 mockk여서 작동이 되지 않은 겁니다.

mockk 오브젝트의 동작을 정의해주게 되면 에러가 발생하지 않게됩니다.

@Test
	fun `should call its data source to retrieve banks`() {
		// given
		every { dataSource.retrieveBanks() } returns emptyList()

		// when
		val banks = bankService.getBanks()

		// then
		verify(exactly = 1) { dataSource.retrieveBanks() }

	}

given 부분에 생성한 dataSource의 함수 동작 부분을 정의해주면

위와 같이 테스트에 통과하게 됩니다.

 

given 블럭이 없다면 어떻게 해야 할까요?

mockk를 생성할때 relaxed = true 옵션을 추가해주면 됩니다.

internal class BankServiceTest {

	private val dataSource: BankDataSource = mockk(relaxed = true)
	private val bankService = BankService(dataSource)

	@Test
	fun `should call its data source to retrieve banks`() {
		// when
		val banks = bankService.getBanks()

		// then
		verify(exactly = 1) { dataSource.retrieveBanks() }

	}

}

이렇게 코드를 변경하면 given 블럭 없이 우리가 기존에 정의했던 getBanks대로 동작하게 되어 테스트를 통과하게 됩니다.

728x90

댓글