2013-08-16 2 views
3

우리는 스칼라 2.10.2를 사용하고 있으며, DAO에는 Slick 1.0.1을 사용하고 있습니다. 우리는 ScalaMock으로 DAO를 모의하려고합니다. 나는 조롱 된 DAO를 주입하는 좋은 방법을 찾아 내려하고 있습니다. 몇 년 동안 Java를 사용해 왔지만 2 주 전에 스칼라를 사용하기 시작했습니다.스칼라에 조롱 된 싱글 톤 객체를 삽입하려면 어떻게해야합니까?

(구문 오류를 무시하고, 나는 여전히 타입 시스템을 만족시키는 지 않고 코드를 응축 한)처럼 지금 우리의 코드는

abstract class RichTable[T](name: String) 
     extends slick.driver.MySQLDriver.simple.Table[T](name) { 
    type ItemType = T 
    def id = column[Int]("id", O.PrimaryKey, O.AutoInc) 
    ... 
} 

object Users extends RichTable[User]("users") { 
    def crypted_password = column[String]("crypted_password") 
    ... 
} 

case class User(id: Option[Int] = None, crypted_password: String) { 
    def updatePassword(...) = { 
     Users.where(_.id === id).map{e => e.crypted_password}.update("asdf") 
    } 
} 

의 DAO의 모든 RichTable[T]에서 상속 싱글 개체 보인다

우리는 사용자와 다른 싱글 톤 DAO 객체를 모방 할 수 있기를 원합니다. 지금 모든 단위 테스트가 데이터베이스를 치고 있습니다. 그러나 우리가 다루는 문제는 모의 싱글 톤 객체를 주입하는 방법입니다. 우리가 지금까지 가지고 올 한 솔루션입니다 : 우리는 Users에서 혼란의 과도한 양을 추가하지 않습니다 Users(), 우리의 모든 참조를 변경하고

object DAORepo { 
    var usersDAO : Users.type = Users 
    var anotherDAO : Another.type = Another 
    ... 
} 

object Users extends RichTable[User]("users") { 
    def apply() : Users.type = DAORepos.usersDAO 
} 

def updatePassword(...) = { 
    Users().where(_.id === id).map{e => e.crypted_password}.update("asdf") 
} 

def test = { 
    val mockUsers = mock[Users] 
    DAORepo.usersDAO = mockUsers 
    // run test using mock repo 
} 

. 그러나, DAORepo에 vars를 사용하면 냄새가 좋지 않으며 누구나 개선 할 수있는 제안이 있는지 궁금합니다.

내가 Real-World Scala: Dependency Injection (DI)Component Based Dependency Injection in Scala을 읽었습니다 - 나는 내가 DAORepo,

trait UsersRepo { 
    val usersDAO : Users.type = Users 
} 

trait DAORepo extends UsersRepo with AnotherRepo { } 

trait UsersTestRepo { 
    val usersDAO : Users.type = mock[Users] 
} 

하지만 난 아직도 내가 새로운 특성을 주입하는 것 이해가 안 돼요 같은 것을 구성하는 특성을 사용하는 방법을 이해 생각한다. 나는 object DAOWrapper에서 하나의 VAR과 object DAORepo 두 다스 바르를 대체

class DAORepoImpl extends DAORepo { } 

object DAOWrapper { 
    var repo : DAORepo = new DAORepoImpl 
} 

def test = { 
    DAOWrapper.repo = new DAORepoImpl with UsersTestRepo 
} 

처럼 뭔가를 할 수 있지만, 거기에 어떤 바르없이이 작업을 수행하는 깨끗한 방법이 될한다고처럼 보인다.

+0

감사하지만 전 자바 사용자 –

+0

을에게 설명 주제에 좋은 자원의 부족이있다 Sebastien Lorber 감사합니다. Spring 예제는 큰 도움이되었습니다. –

답변

3

나는 모든 수업과 특성을 이해하지 못합니다.

trait UsersRepo { 
    val usersDAO : Users.type = Users 
} 

trait AnotherRepo { 
    val anotherDAO : Another.type = Another 
} 

trait DAORepo extends UsersRepo with AnotherRepo 

그리고 당신은 진정한 RealDAORepo을 인스턴스화 할 수

object RealDAORepo extends DAORepo { } 

아니면 케이크 패턴 자체를 사용할 수있는 응용 프로그램에서 DAORepo를 주입하는 그런

object MockedDAORepo extends DAORepo { 
    override val usersDAO : Users.type = mock[Users] 
    override val anotherDAO : Another.type = mock[Another] 
} 

조롱 한 그것을하기위한 타입 참조.


저는 스프링의 사람들이 케이크 패턴을 이해할 수 있도록 InfoQ FR에 대한 기사를 곧 발표 할 예정입니다.이 거의 봄 선언과 동일

trait UserTweetServiceComponent { 
    val userTweetService: UserTweetService 
} 

trait UserTweetService { 
    def createUser(user: User): User 
    def createTweet(tweet: Tweet): Tweet 
    def getUser(id: String): User 
    def getTweet(id: String): Tweet 
    def getUserAndTweets(id: String): (User,List[Tweet]) 
} 

trait DefaultUserTweetServiceComponent extends UserTweetServiceComponent { 

    // Declare dependencies of the service here 
    self: UserRepositoryComponent 
     with TweetRepositoryComponent => 

    override val userTweetService: UserTweetService = new DefaultUserTweetService 

    class DefaultUserTweetService extends UserTweetService { 
    override def createUser(user: User): User = userRepository.createUser(user) 
    override def createTweet(tweet: Tweet): Tweet = tweetRepository.createTweet(tweet) 
    override def getUser(id: String): User = userRepository.getUser(id) 
    override def getTweet(id: String): Tweet = tweetRepository.getTweet(id) 
    override def getUserAndTweets(id: String): (User,List[Tweet]) = { 
     val user = userRepository.getUser(id) 
     val tweets = tweetRepository.getAllByUser(user) 
     (user,tweets) 
    } 
    } 
} 

주 : 다음은이 문서에서 코드 샘플의

<bean name="userTweetService" class="service.impl.DefaultUserTweetService"> 
    <property name="userRepository" ref="userRepository"/> 
    <property name="tweetRepository" ref="tweetRepository"/> 
</bean> 

을 그리고 당신이 수행 할 때

trait MyApplicationMixin 
    extends DefaultUserTweetServiceComponent 
    with InMemoryUserRepositoryComponent 
    with InMemoryTweetRepositoryComponent 

그것은 거의 동일합니다 Spring 선언 (타입 보증 된 애플리케이션 컨텍스트를 얻는다.) :

는 그런 다음에 응용 프로그램을 사용할 수 있습니다 :

val app = new MyApplicationMixin { } 

또는

val app = new MyApplicationMixin { 
    override val tweetRepository = mock[TweetRepository] 
} 

봄 콩 재정과 동일합니다 후자 : 그래서에

<import resource="classpath*:/META-INF/application-context-default-tweet-services.xml" /> 
<import resource="classpath*:/META-INF/application-context-inmemory-tweet-repository.xml" /> 
<import resource="classpath*:/META-INF/application-context-inmemory-user-repository.xml" /> 

<!-- 
This bean will override the one defined in application-context-inmemory-tweet-repository.xml 
But notice that Spring isn't really helpful to declare the behavior of the mock, which is much 
easier with the cake pattern since you directly write code 
--> 
<bean id="tweetRepository" class="repository.impl.MockedTweetRepository"/> 

문제가 발생하면 케이크 패턴을 사용하고 응용 프로그램에서 서비스 구성 요소를 만들 수 있습니다. n, DAORepo 특성에 따라 다릅니다.

그리고 당신은 할 수 있습니다 : 다음

trait MyApplicationMixin 
     extends DefaultUserServiceComponent 
     with AnotherServiceComponent 
     with DAORepo 

과 : 응용 프로그램이 구축되면

val app = new MyApplicationMixin { } 

또는

val app = new MyApplicationMixin { 
    override val usersDAO : Users.type = mock[Users] 
    override val anotherDAO : Another.type = mock[Another] 
} 

당신이 그런 식으로 사용할 수 있습니다 :

app.userService.createUser(...) 

내장 된 응용 프로그램은 어렵지 않다 : 애플리케이션 컨텍스트처럼 정말 케이크 패턴 현상금과 행운을 위해