loicdescotte
3/21/2012 - 7:02 PM

Example Cake and Play 2.0 ver 2

Example Cake and Play 2.0 ver 2

/*********************************************************
 * Setup Domain object, repositories and service objects *
 ********************************************************/
// Identity.scala
case class Identity(id: Int = 0, var slug: String, var email: String, references: Symbol)

// IdentityRepostiory.scala: my interface that isolates concrete instances
trait IdentityRepository {
  def findBySlug(slug: String): Option[Identity]
  def findByEmail(email: String): Option[Identity]
  
  def save(identity: Identity): Identity 
}

// RepostioryFactory.scala: my repository factory trait, where implementers will return the real IdentityRepository
trait RepositoryFactory {
  def identityRepository : IdentityRepository
}

// RepositoryFactoryImpl.scala a concrete implementation that returns my concrete IdentityRepository factory
trait RepositoryFactoryImpl extends RepositoryFactory {
  val identityRepository = new IdentityRepositoryImpl
}

// IdentityService.scala: Basic interface that sets declare which operations are supported by my service
trait IdentityService {
  def isSlugAvailable(name: String): Boolean
  def findBySlug(name: String): Option[Identity]
}

// IdentityServiceImpl.scala: my concrete implementation of my IdentityService trait.
// It uses constructor injection to get the correct instance of the repository factory.
class IdentityServiceImpl(val repositoryFactory: RepositoryFactory)  extends IdentityService {
  def findBySlug(slug: String): Option[Identity] = repositoryFactory.identityRepository.findBySlug(slug)

  def isSlugAvailable(name: String) = findBySlug(name) match {
    case Some(_) => false
    case None => true
  }
}

// ServiceFactory.scala: the contract for my service object abstract factory
trait ServiceFactory {
  this: RepositoryFactory => // sets up the cake pattern dependency on a repository factory
  def identityService: IdentityService
}

// ServiceFactoryImpl.scala: my concrete implmenetation of the service factory.  Which uses my concrete 
// implementation of my repository factory
trait ServiceFactoryImpl extends ServiceFactory with RepositoryFactoryImpl {
  def identityService: IdentityService = new IdentityServiceImpl(this)
}

/***************************************************************************
 * Play 2.0 Application controller, where I'm setting up the dependencies! *
 * package controllers                                                     *
 **************************************************************************/
import play.api._
import play.api.mvc._
import impl._
import views.html.application.login
import libs.json._

trait ApplicationController extends Controller { // my controller declared as a trait with implementation
  this: ServiceFactory => // wire dependency

  def slugAvailable(slug:String) = Action {
    val result = new JsObject(Seq(
          "isAvailable" -> JsBoolean{
            this.identityService.isSlugAvailable(slug) // use my service factory from the self type annotation
          }
        ))
    Ok(result)
  }
}

// Create my Application object to be used by the Play framework, specifying my concrete Service Factory implementation
object Application extends ApplicationController with ServiceFactoryImpl

/***********************************
 * Unit Testing: Configuring Mocks *
 **********************************/
// MckServiceFactory.scala
trait MckServiceFactory extends Mockito with ServiceFactory  { this: RepositoryFactory =>
  val identityService: IdentityService = mock[IdentityService]
}
// MckRepositoryFactory.scala
trait MckRepositoryFactory extends Mockito with RepositoryFactory {
  val identityRepository = mock[IdentityRepository]
}

/*********************************************
 * Unit Testing: ApplicationController trait *
 ********************************************/
import org.specs2.mock.Mockito
import org.specs2.mutable.Specification
import play.api.test.Helpers._
import play.api.test.FakeRequest
import test.MckServiceFactory
import controllers.ApplicationController
import test.MckRepositoryFactory

class ApplicationSpec extends Specification with Mockito {  
  import controllers.ApplicationController // my controller trait
    
  "Application.slugAvailable" should {
    """return {"isAvailable": true} when searching ghorsey""" in {

      object controller extends ApplicationController with MckServiceFactory with MckRepositoryFactory {
        identityService.isSlugAvailable("ghorsey") returns true
      } // create my controller trait w/ my mocked service factory

      val result = controller.slugAvailable("ghorsey")(FakeRequest())
      
      status(result) mustEqual OK
      contentType(result) must beSome("application/json")
      contentAsString(result) mustEqual """{"isAvailable":true}"""
      
      got {
        one(controller.identityService).isSlugAvailable("ghorsey")
      }
    }
    
    """return {"isAvailable": false} when searching geoff""" in {
      object controller extends ApplicationController with MckServiceFactory with MckRepositoryFactory {
        identityService.isSlugAvailable("geoff") returns false
      }  // create my controller trait w/ my mocked service factory
      
      val result = controller.slugAvailable("geoff")(FakeRequest())
      
      status(result) mustEqual OK
      contentType(result) must beSome("application/json")
      contentAsString(result) mustEqual """{"isAvailable":false}"""
      
      got {
        one(controller.identityService).isSlugAvailable("geoff")
      }
    }
  }
}