Specs2 with Scaldi - wrong implicit injector being invoked - scala

I'm trying to run a test with scaldi and specs2. In the test I need to override a StringManipulator function that uses an injected ProxyManipulator. The ProxyManipulator takes a string and returns its upper case in a Future. The replacement manipulator in the test returns a Future("Test Message").
Here is the StringManipulator class where the injection occurs:
class StringManipulator {
def manip (str : String) (implicit inj: Injector) : String = {
val prox = inject[ProxyManipulator]
Await.result(prox.manipulate(str), 1 second)
}
}
I'm using a package.object that contains the implicit injector:
import modules.MyModule
package object controllers {
implicit val appModule = new MyModule
}
And here is the specs2 test with the new binding:
#RunWith(classOf[JUnitRunner])
class StringManipScaldiSpec extends Specification {
class TestModule extends Module {
bind [ProxyManipulator] to new ProxyManipulator {
override def manipulate(name: String) = Future("Test Message")
}
}
"Application" should {
"do something" in {
val myTestModule = new TestModule
val str = "my string"
val stringMan = new StringManipulator() //(myTestModule)
stringMan.manip(str)(myTestModule) === "Test Message"
}
}
}
The problem is that when the test runs the class StringManipulator is still using the original Proxy Manipulator instead of the one passed in the TestModule. Any ideas?

Related

Why does it matter what order I mix in TestSuiteMixIn traits in scalatest suites?

I created the following fixtures:
trait DatabaseFixture extends TestSuiteMixin { this: TestSuite =>
// Just setting up a test database
val cpds = new ComboPooledDataSource
val url : URL = getClass.getResource( "c3p0.properties" )
val db = Database.forDataSource(cpds, Some(50))
val users = Schema.users
val instances = Schema.instances
Await.result(db.run( DBIO.seq(
users.schema.create,
) ), 3 seconds )
abstract override def withFixture(test: NoArgTest): Outcome = {
try super.withFixture(test)
finally cpds.close()
}
}
trait UserControllerFixture extends DatabaseFixture with ScalatraSuite { this: TestSuite =>
addServlet( new UserController(db), "/user/*" )
abstract override def withFixture(test: NoArgTest): Outcome = {
super.withFixture( test )
}
}
Here is the first way I mixed them in to a test suite:
class UserControllerTestSuite extends DatabaseFixture with ScalatraSuite with FlatSpecLike with Matchers {
"POST to /user/add" should "return 201 for created" in {
post( "/instance/add" ) {
status shouldBe 201
}
}
}
This failed to compile with the following error: method withFixture in trait TestSuite of type (test: UserControllerTestSuite.this.NoArgTest)org.scalatest.Outcome has weaker access privileges; it should be public
However, when I mixed the fixtures in after the other scalatest traits, it compiled fine:
class UserControllerTestSuite extends ScalatraSuite with FlatSpecLike with Matchers with DatabaseFixture {
"POST to /user/add" should "return 201 for created" in {
post( "/instance/add" ) {
status shouldBe 201
}
}
}
What's going on here? What does it mean that withFixture() has "weaker access privileges"?
Mixins in Scala are scanned right to left. This is why DatabaseFixture is called before other traits in case where your code works.
So before when there was some other trait (TestSuite) before DatabaseFixture with withFixture method , it tried to override it "weaker access privilege", which means exactly what it says. You cannot override public method with private for example. It has to be the same priority or higher (public > protected in your case.)

Invoking an injected Scala class from an Akka actor

I have the following class in Play for Scala that injects another class:
class MainEtl2 #Inject() (ic: injectedClass) {
def run (option: String) = {
ic.method1()
// ... some code
}
}
I need to call the method run in an Akka Actor. This is my attempt, assuming that Guice will inject injectedClass when MainEtl2 is invoked:
class MainEtl extends Actor {
#Inject val me2 : MainEtl2
def receive = {
case option: String => {
val x = me2.run(option)
// ... more code
}
}
}
MainEtl class does not compile with the followint error:
class MainEtl needs to be abstract, since value me2 is not defined
How to make this work?
I would propose such solution based on Play documentation. Define your actor in such way:
class MainEtl #Inject() (me2: MainEtl2) extends Actor {
def receive = {
case option: String => {
val x = me2.run(option)
}
}
}
Define play module with Akka support and bind named actor:
class AkkaBindings extends AbstractModule with AkkaGuiceSupport {
bindActor[MainEtl]("mainEtl")
}
Register this module in application.conf by adding
play.modules.enabled += "some.package.AkkaBindings"
Now you can inject your actor by name reference:
class Scheduler #Inject()(#Named("mainEtl") mainEtl: ActorRef) {
//some code
val scheduler = QuartzSchedulerExtension(system)
scheduler.schedule("dailyproc", mainEtl, "abc", None)
}
I would try inject MainEtl2 similar to how CountingService is injected in this example:
class MainEtl #Inject() (me2: MainEtl2) extends Actor {
def receive = {
case option: String => {
val x = me2.run(option)
// ... more code
}
}
}
Though you are specifying #Inject annotation, you still need an initial value which guice will override while injecting dependencies, so try this,
class MainEtl extends Actor {
#Inject val me2 : MainEtl2 = null //initial value.
def receive = {
case option: String => {
val x = me2.run(option)
// ... more code
}
}
}

My Play Application's Constructor takes an argument, how do I give a mocked argument at Spec Test?

If my play application has something like this:
class Foo() extends Bar {}
class Application #Inject (f: Foo) extends Controller {
def index = Action { OK("Hi, Foo App") }
}
How do I change my spec test to accept MockedFoo class?
#RunWith(classOf[JUnitRunner])
class MockedFoo() extends Bar {}
class ApplicationTest(implicit ee: ExecutionEnv) extends Specification {
"Sending a GET request to index " should {
"Respond with OK " in new WithApplication { //######## Inject MockedFoo
val response = route(app, FakeRequest(GET, "/")).get
status(response) mustEqual OK
}
}
}
Thanks for the help:
Copying from my own Gist: https://gist.github.com/rethab/01fde763d10f29273d43
First, create a helper class for convenience:
class WithFancyApp(lang: Lang = Lang.defaultLang,
overrideModules: Seq[GuiceableModule] = Seq()) extends
WithApplication(
app =
new GuiceApplicationBuilder()
.in(Environment(new File("."), getClass.getClassLoader, Mode.Test))
.loadConfig(env => Configuration.load(env))
.overrides(overrideModules:_*)
.bindings()
.build
) {
implicit def messages: Messages = Messages(lang, app.injector.instanceOf[MessagesApi])
}
Usage:
"use the overridden bindigs" in new WithFancyApp(
overrideModules = Seq(bind[MyInterface].to[MyImplementation])
) {
// test stuff with all regular bindings plus the ones from above
}

How to inject Configuration instance to scalatest?

I want to inject Configuration instance in one of my testing classes, I extend my test class with ConfiguredApp and injected the Configuration, it looks like this:
#DoNotDiscover()
class MyApiServiceSpec extends FreeSpec with ScalaFutures with ConfiguredApp {
implicit val formats = DefaultFormats
implicit val exec = global
lazy val configuration = app.injector.instanceOf[Configuration]
"Global test" - {
"testcase 1" in {
Server.withRouter() {
case GET(p"/get/data") => Action { request =>
Results.Ok()
}
} { implicit port =>
WsTestClient.withClient { implicit client =>
val service = new MyApiService {
override def config: Configuration = configuration
override val ws: WSClient = client
}
whenReady(service.getData()) { res =>
//i will test stuff here
}
}
}
}
}
}
(MyApiService is a trait)
Exception encountered when invoking run on a nested suite -
ConfiguredApp needs an Application value associated with key
"org.scalatestplus.play.app" in the config map. Did you forget to
annotate a nested suite with #DoNotDiscover?
java.lang.IllegalArgumentException: ConfiguredApp needs an Application
value associated with key "org.scalatestplus.play.app" in the config
map. Did you forget to annotate a nested suite with #DoNotDiscover?
someone have an idea why is that...?
thanks!333333
My answer is not answer to current question, but I want give some advice. If you want to write unit tests for controllers or some service, I would suggest to use a PlaySpec. In order to inject custom configuration for testing environment:
class MyControllerSpec extends PlaySpec with OneAppPerSuite {
val myConfigFile = new File("app/test/conf/application_test.conf")
val parsedConfig = ConfigFactory.parseFile(myConfigFile)
val configuration = ConfigFactory.load(parsedConfig)
implicit override lazy val app: Application = new GuiceApplicationBuilder()
.overrides(bind[Configuration].toInstance(Configuration(configuration)))
.build()
"MyController #index" should {
"should be open" in {
val result = route(app, FakeRequest(GET, controllers.routes.MyController.index().url)).get
status(result) mustBe OK
}
}
}
It seems that you tried to run this test alone. But with a ConfiguredAppyou must run this test with a Suite, like
class AcceptanceSpecSuite extends PlaySpec with GuiceOneAppPerSuite {
override def nestedSuites = Vector(new MyApiServiceSpec)
}
The injection looks ok.

Scala: Can I reproduce anonymous class creation with a factory method?

As far as I understand it, Scala creates an anonymous class if I create a class using the new keyword and follow the class name with a constructor:
class MyClass {
def doStuff() {
// ...
}
}
val mc = new MyClass {
doStuff()
}
The nice thing being that all the code in the constructor is in the scope of the new object.
Is there a way I can reproduce this syntax where the class is created by a factory method rather than the new keyword? i.e. make the following code work:
val mf = new MyFactory
val mc = mf.MyClass {
doStuff()
}
I can't find a way to do it but Scala has so much to it that this might be pretty easy!
Using an import as suggested by #Ricky below I can get:
val mf = MyFactory;
val mc = mf.MyClass
{
import mc._
doStuff()
}
(Where the blank line before the block is needed) but that code block is not a constructor.
You can do this, but you still have to keep the new keyword, and create the nested class as a path-dependent type:
class Bippy(x: Int) {
class Bop {
def getIt = x
}
}
val bip = new Bippy(7)
val bop = new bip.Bop
bop.getIt // yields 7
val bop2 = new bip.Bop{ override def getIt = 42 }
bop2.getIt // yields 42
I don't think it's possible. However, a common pattern is to add a parameter to factory methods which takes a function modifying the created object:
trait MyClass {
var name = ""
def doStuff():Unit
}
class Foo extends MyClass {
def doStuff() { println("FOO: " + name) }
}
trait MyClassFactory {
def make: MyClass
def apply( body: MyClass => Unit ) = {
val mc = make
body(mc)
mc
}
}
object FooFactory extends MyClassFactory {
def make = new Foo
}
You can then create and modify instance with a syntax close to your example:
val foo = FooFactory { f=>
f.name = "Joe"
f.doStuff
}
It sounds like you're just looking to mix in a trait. Instead of calling myFactoryMethod(classOf[Foo]] which ideally would do (if Scala permitted it):
new T {
override def toString = "My implementation here."
}
you can instead write
trait MyImplementation {
override def toString = "My implementation here."
}
new Foo with MyImplementation
However, if you are just looking to get the members of the new object accessible without qualification, remember you can import from any stable identifier:
val foo = new Bar
import foo._
println(baz) //where baz is a member of foo.