2013-03-19 3 views
2

내 Play2 앱에서 내 db 통합 테스트를 작성하는 방법을 알아 내려고하고 있습니다. 나는 응용 프로그램을 실행할 때 사용되는 xxx_test를 지정 내 사용자 개체에서Play2 및 Scala, 적절한 DB로 실행되도록 통합 테스트를 구성하는 방법

db.xxx_test.driver=com.mysql.jdbc.Driver 
db.xxx_test.url="jdbc:mysql://localhost/xxx_test?characterEncoding=UTF-8" 
db.xxx_test.user="root" 
db.xxx_test.password="" 

db.h2.driver=org.h2.Driver 
db.h2.url="jdbc:h2:mem:play" 
db.h2.user=sa 
db.h2.password="" 

: 내 conf의 파일에서

내가 테스트를 위해 정기적으로 사용하고 수소 DB에 대한 두 개의 데이터베이스, xxx_test을 지정했습니다. 내 테스트에서

def createUser(user: User): Option[User] = { 

    DB.withConnection("xxx_test") { 
     implicit connection => 
     SQL("insert into users(first_name, last_name, email, email_validated, last_login, created, modified, active) values({first_name},{last_name},{email},{email_validated},{last_login}, {created}, {modified}, {active})").on(
      'first_name -> user.firstName, 
      'last_name -> user.lastName, 
      'email -> user.email, 
      'email_validated -> user.emailValidated, 
      'last_login -> user.lastLogin, 
      'created -> user.created, 
      'modified -> user.modified, 
      'active -> true 
     ).executeInsert().map(id => { 
      return Some(User(new Id[Long](id), user.firstName, user.lastName, user.email, user.emailValidated, user.lastLogin, user.created, user.modified, true)) 
     } 
     ) 
    } 
    None 
    } 

나는 새로운 인 메모리 데이터베이스를 생성하고 생성하고 테스트를 위해 내 객체 를 얻기 위해 사용자를 사용합니다. 사용자 객체가 xxx_test 아닌 h2 DB를 사용하기 때문에 물론

class DBEvolutionsTest extends Specification { 

    "The Database" should { 
    "persist data properly" in { 
     running(FakeApplication(additionalConfiguration = inMemoryDatabase())) { 

     User.create(User(Id[Long](1L), "jakob", 
      "aa", 
      "aaa", 
      true, 
      DateTime.now(), 
      DateTime.now(), 
      DateTime.now(), 
      true)) 

     val newUser = User.findBy(Id[Long](1L)) 

     newUser.get.firstName must beEqualTo("jakob") 
     } 
     } 
    } 

} 

이 올바른 방법이 아니다. 이 테스트는 User (DB.withConnection("xxx_test")) 객체에 db를 지정했기 때문에 실제 db에서 사용자를 만들고 메모리에있는 사용자는 생성하지 않습니다. 내가이 일을 몇 가지 현명한 방법이 생각, 나는 당신이이 문제를 해결 한 방법 User.create(User(...), "xxx_test")

같은 응용 프로그램 INTE 주위의 DB 이름을 전달 싶지 않아?

답변

0

inMemoryDatabase 방법은 다음과 같이 정의된다 :

def inMemoryDatabase(
    name: String = "default", 
    options: Map[String, String] = Map.empty[String, String]): Map[String, String] 

내 생각 엔 당신이 name 매개 변수로 xxx_test을 통과해야한다는 것입니다.

0

메모리 데이터베이스에 대한 기본 (xxx_test) 이름 이외의 이름을 정의해야합니다. 다음 스 니펫이 작동해야한다고 생각합니다.

FakeApplication(additionalConfiguration = inMemoryDatabase("h2")) 

도 참조하십시오 https://stackoverflow.com/a/11029324/2153190

3

당신은 스칼라에서 의존성 주입을 수행하는 방법을 체크 아웃 할 수 있습니다. 좋은 해결책은 사용자 모델에서 데이터베이스를 추상화 한 다음 종속성으로 전달하는 것입니다.

간단한 방법은 테스트를 위해 구성 파일을 변경하는 것입니다. 재생하면 specify which config file is used on the command line 수 있습니다. 그러나 이것은 가장 실용적이지 않습니다.

또 다른 해결책은, implicits를 사용하여 함수의 암시 적 매개 변수로 데이터베이스 연결을 정의하는 것입니다 :

def createUser(user: User)(implicit dbName: String): Option[User]= 
    DB.withConnection(dbName) { ... } 

당신은 여전히 ​​통화에서 위쪽으로 매개 변수를 전달해야합니다,하지만 당신은 그것을 숨길 수 : 데프 importUsers (csvFile : 파일) (암시 적 DBNAME : 문자열) 서열 [사용자] = {CONN => ... User.createUser (U) ... }

당신은 그것을 호출 할 때 상단에서 :

implicit dbName = "test" 
importUsers(...) 

이것은 스칼라로 작성되므로 설치가 매우 쉽고이를 지원하는 많은 상용구가 필요하지 않습니다.개인적으로, 나는 implicits가 코드를 불분명하게 만들고이 프리젠 테이션에 제시된 해결책을 선호한다고 생각합니다. Dead-Simple Dependency Injection.

그 주안점은 createUser과 데이터베이스 연결에 의존하는 다른 모든 메소드가 결과에 국한되지 않고 연결에 따라 함수를 리턴하도록하는 것입니다. 여기에 귀하의 예를 적용하는 방법이 나와 있습니다.

1- 연결을 구성하는 연결 특성을 만듭니다. 간단한 형태는 다음과 같습니다

trait ConnectionConfig { 
    def dbName: String 
} 

2 그 설정에 따라 당신의 방법은 함수 반환

def createUser(user: User): ConnectionConfig => Option[User] = { conn => 

    DB.withConnection(conn.dbName) { ... } 

} 

3 다른 방법으로는 createUser를 사용하는 방법은 연결에 의존하게 너무 , 그래서 당신은 예를 들어, 함수 반환 유형 ConnectionConfig에 대한 종속성을 반환하여 등으로 표시 : 그것은 당신의 코드에서 알 수있는 바와 같이

def importUsers(csvFile: File): ConnectionConfig => Seq[User] = { conn => 
    ... 
    User.createUser(u)(conn) 
    ... 
} 

이가 가지고있는 좋은 습관이다 어떤 메소드가 데이터베이스에 연결되어 있는가에 따라 달라지며 연결을 쉽게 바꿀 수 있습니다.

class RealConncetionConfig extends ConnectionConfig { 
    val dbName = "xxx_test" 
} 

하지만 테스트 파일에

, 당신은 테스트 DB의 설정을 만들 :

class DBEvolutionsTest extends Specification { 

    class TestDBConfig extends ConnectionConfig { 
     val dbName = "h2" 
    } 

    val testDB = new TestDBConfig() 

    "The Database" should { 
    "persist data properly" in { 
     running(FakeApplication(additionalConfiguration = inMemoryDatabase())) { 

     User.create(User(Id[Long](1L), "jakob", 
      "aa", 
      "aaa", 
      true, 
      DateTime.now(), 
      DateTime.now(), 
      DateTime.now(), 
      true))(testDB) 

     val newUser = User.findBy(Id[Long](1L)) 

     newUser.get.firstName must beEqualTo("jakob") 
     } 
     } 
    } 

} 

이 그것의 요점은 그래서, 당신의 주요 응용 프로그램, 당신은 실제 연결을 만들 것입니다. 앞에서 설명한 프레젠테이션과 슬라이드를 확인하십시오.이 코드를 추악하게 만드는 (conn) 인수를 풀 수 있도록 모든 것을 추상화하는 좋은 방법이 있습니다.

내가 당신 인 경우 측면 주석으로 DB 형식을 추상화합니다. 따라서 User 모델 객체에 SQL을 추가하는 대신 별도의 구현으로이 방법을 사용하면 데이터베이스 유형을 쉽게 전환 할 수 있습니다 (mongodb, dynamo ... 사용).

trait ConnectionConfig { 
    def createUser(user: User): Option[User] 
} 

및 사용자 모델 객체에 : 코드의 일부를 테스트 할 때

def createUser(user: User): ConnectionConfig => Option[User] = { conn => 
    conn.createUser(user) 
} 

이 방법으로, 사용자 모델에 따라 그것은 단지 앞의 코드에서 연장이 같은 것 , 메모리 데이터베이스를 사용하지 않고도 createUser가 항상 작동하고 예상 결과를 반환하는 mock DB를 만들 수 있습니다 (실제 SQL 연결에 대한 테스트가 여전히 필요하지만 다른 부분을 테스트 할 수 있습니다). 앱의 :

trait ConnectionConfig { 
    def createUser(user: User): Option[User] = Some(user) 
} 
+0

프리젠 테이션에 제시된 방법을 사용하는 경우 독자적으로 'createUser (u) (conn)'를 수행하지 않아도된다는 점에 유의하십시오. – Mortimer

+0

자세한 내용은 [여기] (https://coderwall.com/p/neukwa) 및 [여기] (https://coderwall.com/p/kh_z5g) – Mortimer