In Scala, can I use Guice to inject Scala objects?
For example, can I inject into s in the following object?
object GuiceSpec {
#Inject
val s: String = null
def get() = s
}
Some research on Google revealed that you can accomplish this as follows (the code that follows is a ScalaTest unit test):
import org.junit.runner.RunWith
import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
import org.scalatest.junit.JUnitRunner
import com.google.inject.Inject
import com.google.inject.Module
import com.google.inject.Binder
import com.google.inject.Guice
import uk.me.lings.scalaguice.ScalaModule
#RunWith(classOf[JUnitRunner])
class GuiceSpec extends WordSpec with MustMatchers {
"Guice" must {
"inject into Scala objects" in {
val injector = Guice.createInjector(new ScalaModule() {
def configure() {
bind[String].toInstance("foo")
bind[GuiceSpec.type].toInstance(GuiceSpec)
}
})
injector.getInstance(classOf[String]) must equal("foo")
GuiceSpec.get must equal("foo")
}
}
}
object GuiceSpec {
#Inject
var s: String = null
def get() = s
}
This assumes you are using scala-guice and ScalaTest.
The above answer is correct, but if you don't want to use ScalaGuice Extensions, you can do the following:
val injector = Guice.createInjector(new ScalaModule() {
def configure() {
bind[String].toInstance("foo")
}
#Provides
def guiceSpecProvider: GuiceSpec.type = GuiceSpec
})
Related
I need to return a json response in camelCase with finatra but it is in snake_case by default. From what I found so far, I need to use ObjectMapper, but I can't understand where do I pass it once I create it. An example would be very helpful. Here is what I have:
import com.twitter.finagle.http.Request
import com.twitter.finatra.http.Controller
class myTargetingController extends Controller {
val endpoint = "http://....."
get(s"$endpoint/?") { request: Request =>
// what do I do with it?
// val objectMapper = ScalaObjectMapper.builder.camelCaseObjectMapper
response.ok.json(myObject)
}
}
==================================================================
import com.twitter.finagle.{Service, SimpleFilter}
import com.twitter.finagle.http.{ Request, Response}
import com.twitter.finatra.http.routing.HttpRouter
import com.twitter.finatra.http.{HttpServer}
import com.twitter.finatra.http.filters.CommonFilters
import com.twitter.util.Future
object MyServerApp extends MyServer
class MyServer extends HttpServer {
override protected def configureHttp(router: HttpRouter) {
router
.filter[CommonFilters]
.add[CorsFilter, MyController]
}
}
P.S. I am very-very new to Scala
Following up from the comments
Define a custom ObjectMapperModule
class CamelCaseModule extends ScalaObjectMapperModule {
override val propertyNamingStrategy: PropertyNamingStrategy =
new PropertyNamingStrategy.UpperCamelCaseStrategy
override def additionalMapperConfiguration(mapper: ObjectMapper): Unit = {
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true)
}
}
Override the default Jackson module for your server
override def jacksonModule = new CamelCaseModule
Make sure you have
"com.twitter" %% "finatra-jackson" % yourFinatraVersion % "test"
in your build.sbt
And that you import
import com.fasterxml.jackson.databind.{DeserializationFeature, Module, ObjectMapper, PropertyNamingStrategy}
import com.twitter.finatra.jackson.modules.ScalaObjectMapperModule
Tested it locally and it seems to work
Hope this helps
I wrote some traits to use it as a base for my functional tests
This file is for creating a DB in memory (H2 + Evolutions)
BlogApiDBTest.scala
package functional.common
import play.api.db.Databases
import play.api.db.evolutions.Evolutions
trait BlogApiDBTest {
implicit val testDatabase = Databases.inMemory(
name = "blog_db",
urlOptions = Map(
"MODE" -> "MYSQL"
),
config = Map(
"logStatements" -> true
)
)
org.h2.engine.Mode.getInstance("MYSQL").convertInsertNullToZero = false
Evolutions.applyEvolutions(testDatabase)
}
Here I am overriding some injected components for testing purposes
BlogApiComponentsTest.scala
package functional.common
import common.BlogApiComponents
import org.scalatestplus.play.components.WithApplicationComponents
import play.api.{BuiltInComponents, Configuration}
trait BlogApiComponentsTest extends WithApplicationComponents with BlogApiDBTest {
override def components: BuiltInComponents = new BlogApiComponents(context) {
override lazy val configuration: Configuration = context.initialConfiguration
override lazy val blogDatabase = testDatabase
}
}
This is the base class for my functional tests
BlogApiOneServerPerTestWithComponents.scala
package functional.common
import org.scalatestplus.play.PlaySpec
import org.scalatestplus.play.components.{OneServerPerTestWithComponents}
trait BlogApiOneServerPerTestWithComponents extends PlaySpec with OneServerPerTestWithComponents with BlogApiComponentsTest {
}
Finally the test I am trying to execute
PostControllerSpec.scala
package functional.controllers
import functional.common.BlogApiOneServerPerTestWithComponents
import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures}
import play.api.mvc.{Results}
import play.api.test.{FakeRequest, Helpers}
import play.api.test.Helpers.{GET, route}
class PostControllerSpec extends BlogApiOneServerPerTestWithComponents
with Results
with ScalaFutures
with IntegrationPatience {
"Server query should" should {
"provide an Application" in {
val Some(result) = route(app, FakeRequest(GET, "/posts"))
Helpers.contentAsString(result) must be("success!")
}
}
}
Then I get
blog-api/test/functional/controllers/PostControllerSpec.scala:18:31: Cannot write an instance of play.api.mvc.AnyContentAsEmpty.type to HTTP response. Try to define a Writeable[play.api.mvc.AnyContentAsEmpty.type]
Here is the code
Adding the following import should make it work:
import play.api.test.Helpers._
Looking at the signature of route
def route[T](app: Application, req: Request[T])(implicit w: Writeable[T]): Option[Future[Result]]
we see it expects an implicit w: Writeable[T]. The above import will provide it via Writables
I'm using a Silhouette v4.0 library with play framework 2.5.
And have been trying to write test code using play specs2.
But, I get the following error with my test class as below.
Error Message
[error] could not find implicit value for parameter env: com.mohiva.play.silhouette.api.Environment[utils.auth.DefaultEnv]
.withAuthenticator[DefaultEnv](identity.loginInfo)
^
Here's the test class
package controllers
import com.google.inject.AbstractModule
import org.joda.time.DateTime
import org.specs2.specification.Scope
import org.specs2.matcher._
import org.specs2.mock._
import play.api.test._
import play.api.libs.json._
import play.api.libs.json.Json
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.mailer.{ MailerClient, Email }
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.inject.bind
import com.mohiva.play.silhouette.test._
import com.mohiva.play.silhouette.api._
import com.mohiva.play.silhouette.api.repositories.AuthInfoRepository
import com.mohiva.play.silhouette.api.util._
import com.mohiva.play.silhouette.impl.providers._
import net.codingwell.scalaguice.ScalaModule
import utils.auth.DefaultEnv
class TestControllerSpec extends PlaySpecification with Mockito {
"case" in new Context {
new WithApplication(application) {
val request = FakeRequest(POST, "/api/test")
.withAuthenticator[DefaultEnv](identity.loginInfo) // <-
val result = route(app, request).get
status(result) must be equalTo OK
}
}
trait Context extends Scope {
val identity = User(
loginInfo = LoginInfo(..)
..
)
implicit val env = FakeEnvironment[DefaultEnv](Seq(identity.loginInfo -> identity))
class FakeModule extends AbstractModule with ScalaModule {
def configure() = {
bind[Environment[DefaultEnv]].toInstance(env)
}
}
lazy val application = new GuiceApplicationBuilder()
.overrides(new FakeModule)
.build
}
}
There are some other test classes similar to this class are properly able to compile and execute.
It's kind of implicit problem with scope..
Therefore, I tried to import all the same as another test class which's able to compile properly. But, still unable to compile.
Missing some import?
As the compiler states, you're missing an implicit value. Use the following, which is modeled after one of Silhouette's specs:
class TestControllerSpec extends PlaySpecification with Mockito {
"the POST request" should {
"return an OK response" in new Context {
new WithApplication(application) {
val identity = User(LoginInfo(...))
implicit val env = FakeEnvironment[DefaultEnv](Seq(identity.loginInfo -> identity))
val request = FakeRequest(POST, "/api/test")
.withAuthenticator(identity.loginInfo)
val result = route(app, request).get
status(result) must be equalTo OK
}
}
}
trait Context extends Scope {
...
}
}
I'm developing web app using Scala, Play Framework. And I need to test controller with the custom action. Please, take a look on the code:
package controllers.helpers
import play.api.mvc.{ActionBuilder, Request, Result}
import scala.concurrent.Future
class CustomAction extends ActionBuilder[Request] {
override def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]): Future[Result] = {
// do some stuff
block(request)
}
}
package controllers
import javax.inject.Singleton
import play.api.mvc.Controller
#Singleton
class SomeController #Inject() (customAction: CustomAction
) extends Controller {
def foo() = customAction(parse.json) { implicit request =>
// do some stuff and create result
}
}
And below you can find a code of the test class. I use Specs2 and I got org.mockito.exceptions.misusing.InvalidUseOfMatchersException on the line with customeActionMock.invokeBlock(any[Request[_]], any[(Request[_]) => Future[Result]]) returns mock[Future[Result]]
package controllers
import controllers.helpers.CustomAction
import org.specs2.mock.Mockito
import play.api.mvc.{Request, Result}
import play.api.test.{FakeHeaders, FakeRequest, PlaySpecification}
import scala.concurrent.Future
class SomeControllerSpec extends PlaySpecification with Mockito {
private val customeActionMock = mock[CustomAction]
customeActionMock.invokeBlock(any[Request[_]], any[(Request[_]) => Future[Result]]) returns mock[Future[Result]] //this doesn't work, exception is thrown there
"SomController" should {
"respond Ok on valid request" in {
val result = new UserController(customeActionMock).foo()(FakeRequest())
status(result) shouldEqual OK
}
}
}
I understand that I mock block parameter of the CustomAction incorrectly. Can someone help me to do it properly?
My project uses Play 2.5.x. I use scalatest. This is how I test controllers.
import org.scalatestplus.play.OneAppPerSuite
import org.scalatest._
import org.scalatest.time.{Millis, Seconds, Span}
import org.scalatest.concurrent.ScalaFutures
import scala.concurrent.Future
class SomeControllerSpec extends FlatSpec with Matchers with ScalaFutures with OneAppPerSuite {
private val customeActionMock = new CustomAction // create an instance of a class
implicit val defaultPatience = PatienceConfig(timeout = Span(5,Seconds), interval = Span(500, Millis))
it should "respond Ok on valid request" in {
val resultF : Future[Result] = new UserController(customeActionMock).foo()(FakeRequest())
whenReady(resultF) { resultR =>
resultR.header.status shouldBe 200
}
}
}
Don't mock the CustomAction :
class SomeControllerSpec extends PlaySpecification with Mockito {
private val customeActionMock = new CustomAction
"SomController" should {
"respond Ok on valid request" in {
val result = new UserController(customeActionMock).foo()(FakeRequest())
status(result) shouldEqual OK
}
}
}
using scala, slick 2.0 & eclipse I have an error I can't explain : "value ddl is not a member of scala.slick.lifted.TableQuery[SqliteSpec.this.Personnes]"
here is the code:
I declare a trait like this :
trait sqlite {
val db = Database.forURL("jdbc:sqlite:rdvs.txt", driver = "org.sqlite.JDBC")
class Personnes(tag: Tag) extends Table[Rdv](tag, "RDV") {
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def nom = column[String]("NOM", O.NotNull)
def prénom = column[String]("PRENOM")
def sexe = column[Int]("SEXE")
def télPortable = column[String]("TELPOR")
def télBureau = column[String]("TELBUR")
def télPrivé = column[String]("TELPRI")
def siteRDV = column[String]("SITE")
def typeRDV = column[String]("TYPE")
def libelléRDV = column[String]("LIBELLE")
def numRDV = column[String]("NUMRDV")
def étape = column[String]("ETAPE")
def dateRDV = column[Date]("DATE")
def heureRDVString = column[String]("HEURE")
def statut = column[String]("STATUT")
def orderId = column[String]("ORDERID")
def * = (id.?, nom, prénom, sexe, télPortable, télBureau, télPrivé,
siteRDV, typeRDV, libelléRDV, numRDV, étape, dateRDV, heureRDVString,
statut, orderId) <> (Rdv.tupled, Rdv.unapply _)
}
}
and here is the wrong code :
db.withDynSession{
val personnes=TableQuery[Personnes]
personnes.ddl.create
}
althought I followed this official tutorial : http://slick.typesafe.com/doc/2.0.0/schemas.html (section DDL)
Do you know what's wrong?
thanks.
Maybe this is useful for somebody: I had the same problem, but my mistake was importing different driver simple implicits. In my main model class had Postgres', but in my tests had H2's (in order to make in-memory integration testing). Switching to the same drivers solved the issue.
I would like to add the entire code, but the "edit" button is no more present under my question; here is the code:
package tests {
#RunWith(classOf[JUnitRunner])
class SqliteSpec extends Specification with sqlite {
"la base sqlite" should {
"create a new database file" in new Sqlite_avant_chaque_test {
todo
}
}
class Sqlite_avant_chaque_test extends Scope {
println("avant test")
def abc = {
db.withDynSession {
val personnes = TableQuery[Personnes]
personnes.ddl.create
}
}
}
}
}
ok, I only needed to log on; here is the working code :
import org.specs2.execute.AsResult
import org.specs2.runner.JUnitRunner
import scala.collection.GenTraversableOnce
import org.specs2.time.TimeConversions$longAsTime
import org.specs2.collection.BiMap
import org.specs2.control.Debug$Debuggable
import scala.collection.mutable.ListBuffer
import org.specs2.internal.scalaz.TreeLoc
import org.specs2.mutable.Specification
import scala.xml.NodeSeq
import scala.collection.immutable.List
import org.specs2.text.LinesContent
import org.specs2.specification.Fragments
import scala.math.Numeric
import java.net.URL
import org.specs2.matcher.MatchSuccess
import scala.runtime.Nothing$
import scala.reflect.ClassTag
import org.specs2.main.Arguments
import java.io.InputStream
import org.specs2.data.Sized
import org.junit.runner.RunWith
import org.specs2.specification.Scope
import java.sql.SQLInvalidAuthorizationSpecException
import models.sqlite
import scala.slick.lifted.TableQuery
//import scala.slick.driver.JdbcDriver.simple._
import scala.slick.driver.SQLiteDriver.simple._
import play.api.test.Helpers
import play.test.Helpers
package tests {
#RunWith(classOf[JUnitRunner])
class SqliteSpec extends Specification with sqlite {
sequential
"la base sqlite" should {
"create a new database file" in new Sqlite_avant_chaque_test {
todo
}
}
class Sqlite_avant_chaque_test extends Scope {
println("avant test")
//db.withDynSession {
db.withSession { implicit session: Session =>
val personnes = TableQuery[Personnes]
personnes.ddl.create
println("avant tests, après création base")
}
}
}
}