인터페이스 그리고 TDD

Panda·2022년 7월 12일
0

Spring

목록 보기
28/42

오늘 공부할 내용은
왜 인터페이스로 추상화 시키는것이 중요한지 와
이거를 테스트 코드와 연결을 시켜 이야기해보려고 합니다.

Bank로 알아보는 인터페이스

interface IBank {
    fun getBanks(): Collection<Bank>
    fun getBank(accountNumber : String) : Bank
    fun addBank(bank: Bank): Bank
    fun updateBank(bank: Bank): Bank
    fun deleteBank(accountNumber: String)
}
@Service // 국민은행 서비스라고 가정을 합시다.
class BankService(@Qualifier("mock") private val dataSource : BankDataSource): IBank {
    override fun getBanks(): Collection<Bank> = dataSource.retrieveBanks()

    override fun getBank(accountNumber : String) : Bank = dataSource.retrieveBank(accountNumber)

    override fun addBank(bank: Bank): Bank = dataSource.createBank(bank)

    override fun updateBank(bank: Bank): Bank = dataSource.updateBank(bank)

    override fun deleteBank(accountNumber: String) = dataSource.deleteBank(accountNumber)
}
class BankManager(
    private val bankService: IBank
) {

    fun getBankInfo(accountNumber: String): String {
        val bank = bankService.getBank(accountNumber);
        // 여러가지 은행 정보 처리 알고리즘 이라고 칩시다.
        return bank.accountNumber;
    }
}

BankManager를 살펴보게 되면 BankService를 가지고 다양한 처리를 해주는 Manager 입니다. 보시면 실제 서비스인 BankService를 프로퍼티로 가지고 있는 것이 아니라 IBank로 가지고 있는 것을 확인하실 수 있습니다.

인터페이스를 프로퍼티로 가지고 사용하는 이유들을 살펴보면

  • 실제 인터페이스 구현체에 영향을 받지않는다. (IBank를 여러가지 형태로 갈아끼우는 것이 가능
    ex) 신한, 국민, 기업은행 등등)
  • 테스트 코드 작성에 유용함.

인터페이스를 사용함에 있어서 어떠한 종류의 Bank인지 관계 없이 정해진 기능들을 사용을 할 수 있습니다.
마치 클라이언트가 백엔드 내부에서 뭘하는지 알 필요가 없이 말이죠.

만약 인터페이스로 가지고있는게 아닌 실제 서비스를 가지게 있게 된다면 다른 Bank 서비스로 변경을 못하는 단점도 있겠지만 테스트 시에 실제 환경을 가지고있는 BankService로 테스트를 진행하겠죠.
또 BankManager만 유닛테스트를 진행하고 싶은데 BankService에 영향을 받기때문에 TDD 원칙에 위배됩니다.
이것들은 치명적인 단점입니다.

테스트 코드

internal class BankManagerTest {
    private val bankService: BankService = mockk<BankService>()
    private val bankManager: BankManager = BankManager(bankService)

    @Test
    fun `BankManager 테스트`() {
        // given
        val accountNumber = "1234";
        every { bankService.getBank(accountNumber) } returns Bank(accountNumber, 1.3, 2);

        // when
        val processAccountNumber = bankManager.getBankInfo(accountNumber);
        // then
        assert(processAccountNumber.equals(accountNumber))
    }
}

BankManager를 테스트 하기 위해 BankService를 mock 서비스로 만들어서 BankManager에게만 집중할 수 있도록 하였습니다.

이러한 인터페이스 때문에 테스트 코드에서도 mock 기능을 정의하는 것이 편합니다.
또한 구현체가 구현되지 않았어도 테스트 코드를 짜는 것이 가능하죠.
이는 테스트 코드 뿐만 아니라 실제 협업하는 개발자가 해당 인터페이스만 가지고 다른 서비스를 개발을 하는 것도 가능하다는 엄청난 장점들이 있습니다.

느낀 점

요즘들어 테스트 코드를 많이 작성하고 있고 인터페이스를 기반으로 작업을 하고 있는데
예전에는 인터페이스를 도대체 왜 쓰는지 전혀 이해를 하지 못하여서 사용한적이 단 한번도 없었는데 요즘은 인터페이스를 매우 자주 쓰고 있고 너무 에이스 침대마냥 편합니다.

인터페이스를 사용함에 따라서
다른 객체가 어떻게 작동하는지 알필요가 없어서 좀더 지금 개발하고 있는 객체에 집중을 할 수 있어서 효율이 올라가는 것 같습니다.

테스트 코드 같은 경우는 mock 데이터 또는 객체를 작성하여 넣는건 상관없는데
Result되는 값이 필요하면 일일이 제가 mock 행위를 정의해야되다 보니까 매우 귀찮은 느낌은 있습니다만 테스트 코드로 인해서 테스트 자동화랑 배포가 너무 편해지다 보니까
테스트 코드를 어떻게 하면 더 빠르고 모든 상황들을 테스트 할수있을지 많이 공부해야 될 것 같습니다.

profile
실력있는 개발자가 되보자!

0개의 댓글