Research libraries & Apply for play-scala unit test

Updated:
Categories: Playframework
Tags: #Scala #TDD #Play framework

OverviewPermalink

Play framework v2.5 for scala 에서 unit test를 적용을 위한 라이브러리 조사 및 초기 적용 방법 소개

유닛 테스트(unit test)는 컴퓨터 프로그래밍에서 소스 코드의 특정 모듈이 의도된 대로 정확히 작동하는지 검증하는 절차 From wiki

LibrariesPermalink

ScalaTestPermalink

  • ScalaTest는 Scala 에코 시스템에서 가장 유연하고 유명한 testing tool
  • 테스트할 수 있는 대상: Scala, Scala.js, Java code

Spec2Permalink

scalacheck-shapelessPermalink

  • Github
  • libraryDependencies += "com.github.alexarchambault" %% "scalacheck-shapeless_1.13" % "1.1.5" in build.sbt

scalatestplus-playPermalink

  • Github
  • libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "1.5.1" % Test in build.sbt

ConditionPermalink

아래 조건들은 테스트 케이스를 적용할 때에 고민한 지극히 개인적인 의견임.

  • A. DAO 함수들을 테스트할 경우, 연결되어 있는 개발 DB에 저장하고 싶지 않다.
    • 개발 DB의 역할을 생각해보면, 굳이 테스트할 때 개발 DB에 저장하는 것을 피할 필요는 없다고 생각
    • 하지만, DAO의 함수 기능 테스트에서 개발 DB에 저장되다면 개발 서버 API에 문제 발생하여 협업 문제 발생. Service단에서 여러 DAO 함수들을 함께 사용하기 때문.
  • B. 테스트 데이터는 지정된 값만을 테스트하는 것이 아니라, 각 테스트하는 함수의 제한 조건에 충족하는 데이터들을 랜덤 생성하고 싶다.
    • 이름이 50자 이하면 유의미한 이름을 넣는 것이 아니라, 길이가 50 이하인 랜덤 String 생성
    • 지정된 값만 테스트할 경우, 예외 상황이 생기는 것을 검사할 수 없을 것 같음

ImplementationPermalink

  • 조건 A를 만족하기 위해, DB config을 바꾼 서버 어플리케이션(fakeApp)을 실행하여, 테스트.
import play.api.db.slick.DatabaseConfigProvider
import play.api.inject.guice.GuiceApplicationBuilder
import scala.reflect.ClassTag
// Apply to the other object by using Cake Pattern
trait Inject {
// Switch DB config for testing
private val testDBConfig = Map(
"slick.dbs.default.driver" -> "slick.driver.H2Driver$",
"slick.dbs.default.db.driver" -> "org.h2.Driver",
"slick.dbs.default.db.url" -> "jdbc:h2:mem:db_scheme_name;DATABASE_TO_UPPER=false;MODE=MYSQL;INIT=runscript from './test/db/create.sql'\\;runscript from './test/db/req_init.sql'"
)
lazy val appBuilder: GuiceApplicationBuilder = new GuiceApplicationBuilder().configure(
testDBConfig.toSeq: _*
)
lazy val injector = appBuilder.injector()
lazy val fakeApp = appBuilder.build()
lazy val dbConfigProvider: DatabaseConfigProvider = injector.instanceOf[DatabaseConfigProvider]
def inject[T : ClassTag]: T = injector.instanceOf[T]
}
view raw Inject.scala hosted with ❤ by GitHub
  • test할 DAO들을 가지고 있는 TestDaos. Inject를 Mixin 함
import kim.jungbin.daos.Dao1
import kim.jungbin.daos.Dao2
import play.api.Application
// fakeApp은 trait Inject을 Mixin하여 사용
trait TestDaos extends Inject {
// Test할 DAO 객체 fakeApp에 inject
// trait Inject의 fakeApp은 DB설정이 dao unit test를 위한 환경으로 변해있음
lazy val testDao1: Dao1 = Application.instanceCache[Dao1].apply(fakeApp)
lazy val testDao2: Dao2 = Application.instanceCache[Dao2].apply(fakeApp)
}
view raw TestDaos.scala hosted with ❤ by GitHub
  • 조건 B를 만족하기 위해, 테스트 함수의 입력 데이터 랜덤 생성
    • 실제 적용시, 함수 입력 값 중 하나가 Scala Enumeration으로 되어 있었는데, 그 중 하나 값에서 에러 발생하는 경우가 있었음. 그래서 테스트할 때 Success, Fail이 랜덤하게 발생. 테스트를 한번만 해봐서는 알 수 없다는 것도 느낌.
import kim.jungbin.models.Function1InputModel
import org.scalacheck.{Arbitrary, Gen}
class Dao1ArbitraryModels {
def function1InputModel: Arbitrary[Function1InputModel] = Arbitrary {
for {
name <- Gen.listOfN(50, Gen.alphaNumChar)
width <- Gen.choose(1, 500)
height <- Gen.choose(1, 500)
sizeType <- Gen.oneOf(SizeTypeEnum.values.toSeq) // SizeTypeEnum
} yield Function1InputModel(
name = name.mkString,
width = width,
height = height,
size_type = sizeType
)
}
}
  • 테스트 통과/실패 조건 설정
    • Given/When/Then 구조를 이용한 테스트
import org.specs2.mock.Mockito
import play.api.test.PlaySpecification
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
class Dao1Spec extends PlaySpecification with Mockito with TestDaos {
// Inject random generate data models for Dao1
lazy val dao1ArbitraryModels = inject[Dao1ArbitraryModels]
"DAO1" >> {
"function1" >> {
// Given random generate data
val fakeFunction1Input = dao1ArbitraryModels.function1InputModel.arbitrary.sample.get
// When
val futureRes = testDao1.function1(fakeFunction1Input)
val res = await(futureRes)
// Then
s"return $res" >> {
res must not be empty
}
}
}
}
view raw Dao1Spec.scala hosted with ❤ by GitHub
  • 테스트 실행
# 모든  테스트 클래스 실행
$ sbt test

# 테스트하고 싶은 테스트 클래스만 실행
$ sbt testOnly *TestClassName

testOnly 참고

Comments