I have trait of wiremock like this:
trait WiremockSetup extends BeforeAndAfterAll { self: Suite =>
import WireMockConfiguration._
protected val wiremockServer = new WireMockServer(options().dynamicPort())
override protected def beforeAll(): Unit = {
super.beforeAll()
wiremockServer.start()
}
override protected def afterAll(): Unit = {
wiremockServer.stop()
super.afterAll()
}
}
And I mix the trait in my test class like this
class Foo extends FlatSpec with WiremockSetup{
"Test scneario" should "do something" in {
assert (1 == 1)
}
}
But I am seeing compilation problems like this:
An exception or error caused a run to abort.
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.github.tomakehurst.wiremock.jetty9.JettyHttpServerFactory.buildHttpServer(JettyHttpServerFactory.java:63)
at com.github.tomakehurst.wiremock.WireMockServer.<init>(WireMockServer.java:76)
Why is problem?
Make sure you have this dependency:
"com.github.tomakehurst" % "wiremock-jre8" % "2.22.0"
Related
I am using Google Guice as DI framework and I am writing Unit Tests for my classes which use Google Guice. I am also trying to do partial mocking.
Here is the code I wrote
class Test1 {
def test1() = "I do test1"
}
class Test2 {
def test2() = "I do test2"
}
class TestPartialMock #Inject()(t1: Test1, t2: Test2) {
def test3() = "I do test3"
def createList() : List[String] = List(t1.test1(), t2.test2(), test3())
}
My objective is to write a test case for the code above, but I ONLY want to mock test3
I wrote this test case
class TestModule extends AbstractModule with ScalaModule with MockitoSugar {
override def configure() = {
bind[Test1]
bind[Test2]
val x = mock[TestPartialMock]
when(x.test3()).thenReturn("I am mocked")
when(x.createList()).thenCallRealMethod()
bind(classOf[TestPartialMock]).toInstance(x)
}
}
class PartialMockTest extends FunSpec with Matchers {
describe("we are testing workhorse but mock test3") {
it("should return mock for test3") {
val module = new TestModule
val injector = Guice.createInjector(module)
val tpm = injector.getInstance(classOf[TestPartialMock])
val result = tpm.workHorse()
result should contain ("I do test2")
result should contain ("I do test1")
result should contain ("I am mocked")
result should not contain ("I do test3")
}
}
}
However the test fails with a null pointer exception on the dependencies (call to t1)
java.lang.NullPointerException
at TestPartialMock.createList(TestPartialMock.scala:9)
at PartialMockTest.$anonfun$new$2(PartialMockTest.scala:16)
at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
at org.scalatest.Transformer.apply(Transformer.scala:22)
at org.scalatest.Transformer.apply(Transformer.scala:20)
at org.scalatest.FunSpecLike$$anon$1.apply(FunSpecLike.scala:454)
So how can I have the injected dependencies along with the mocking for test3 method?
Here are my dependencies if you need to look at those
"net.codingwell" %% "scala-guice" % "4.1.0",
"org.scalatest" % "scalatest_2.12" % "3.0.3",
"org.scalamock" % "scalamock-scalatest-support_2.12" % "3.5.0",
"org.mockito" % "mockito-core" % "2.7.22"
The trick is that Mockito doesn't call the constructor of the (base) class when a mocked object is created. Thus the dependencies of the TestPartialMock are not initialized. The simplest way to work this around is to spy on a real object that you can create with whatever configuration you want
class TestModule extends AbstractModule with ScalaModule with MockitoSugar {
override def configure() = {
//bind[Test1]
//bind[Test2]
//val x = mock[TestPartialMock]
val realObject = new TestPartialMock(new Test1, new Test2)
val x = spy(realObject)
when(x.test3()).thenReturn("I am mocked")
when(x.createList()).thenCallRealMethod()
bind(classOf[TestPartialMock]).toInstance(x)
}
}
Play 2.4 app, using dependency injection for service classes.
I found that Specs2 chokes when a service class being tested has more than one injected dependency. It fails with "Can't find a constructor for class ..."
$ test-only services.ReportServiceSpec
[error] Can't find a constructor for class services.ReportService
[error] Error: Total 1, Failed 0, Errors 1, Passed 0
[error] Error during tests:
[error] services.ReportServiceSpec
[error] (test:testOnly) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 2 s, completed Dec 8, 2015 5:24:34 PM
Production code, stripped to bare minimum to reproduce this problem:
package services
import javax.inject.Inject
class ReportService #Inject()(userService: UserService, supportService: SupportService) {
// ...
}
class UserService {
// ...
}
class SupportService {
// ...
}
Test code:
package services
import javax.inject.Inject
import org.specs2.mutable.Specification
class ReportServiceSpec #Inject()(service: ReportService) extends Specification {
"ReportService" should {
"Work" in {
1 mustEqual 1
}
}
}
If I remove either UserService or SupportService dependency from ReportService, the test works. But obviously the dependencies are in the production code for a reason. Question is, how do I make this test work?
Edit: When trying to run the test inside IntelliJ IDEA, the same thing fails, but with different messages: "Test framework quit unexpectedly", "This looks like a specs2 exception..."; see full output with stacktrace. I opened a Specs2 issue as instructed in the output, though I have no idea if the problem is in Play or Specs2 or somewhere else.
My library dependencies below. (I tried specifying Specs2 version explicitly, but that didn't help. Looks like I need specs2 % Test as is, for Play's test classes like WithApplication to work.)
resolvers += "scalaz-bintray" at "https://dl.bintray.com/scalaz/releases"
libraryDependencies ++= Seq(
specs2 % Test,
jdbc,
evolutions,
filters,
"com.typesafe.play" %% "anorm" % "2.4.0",
"org.postgresql" % "postgresql" % "9.4-1205-jdbc42"
)
There is limited support for dependency injection in specs2, mostly for execution environments or command-line arguments.
There is nothing preventing you from just using a lazy val and your favourite injection framework:
class MySpec extends Specification with Inject {
lazy val reportService = inject[ReportService]
...
}
With Play and Guice, you could have a test helper such as this:
import play.api.inject.guice.GuiceApplicationBuilder
import scala.reflect.ClassTag
trait Inject {
lazy val injector = (new GuiceApplicationBuilder).injector()
def inject[T : ClassTag]: T = injector.instanceOf[T]
}
If you really need runtime dependency injection, then it's better to use Guice loading, I guess:
package services
import org.specs2.mutable.Specification
import scala.reflect.ClassTag
import com.google.inject.Guice
// Something you'd like to share between your tests
// or maybe not
object Inject {
lazy val injector = Guice.createInjector()
def apply[T <: AnyRef](implicit m: ClassTag[T]): T =
injector.getInstance(m.runtimeClass).asInstanceOf[T]
}
class ReportServiceSpec extends Specification {
lazy val reportService: ReportService = Inject[ReportService]
"ReportService" should {
"Work" in {
reportService.foo mustEqual 2
}
}
}
Alternatively you can implement Inject object as
import scala.reflect.ClassTag
import play.api.inject.guice.GuiceApplicationBuilder
object Inject {
lazy val injector = (new GuiceApplicationBuilder).injector()
def apply[T : ClassTag]: T = injector.instanceOf[T]
}
It depends whether you want to use Guice directly, or thru play wrappers.
Looks like you are out of luck ATM: The comment says
Try to create an instance of a given class by using whatever constructor is available and trying to instantiate the first parameter recursively if there is a parameter for that constructor.
val constructors = klass.getDeclaredConstructors.toList.filter(_.getParameterTypes.size <= 1).sortBy(_.getParameterTypes.size)
i.e. Specs2 doesn't provide own DI out-of-the box,
Or you can reimplement the functionality yourself, if Guice isn't working for you.
App code:
package services
import javax.inject.Inject
class ReportService #Inject()(userService: UserService, supportService: SupportService) {
val foo: Int = userService.foo + supportService.foo
}
class UserService {
val foo: Int = 1
}
class SupportService {
val foo: Int = 41
}
Test code
package services
import org.specs2.mutable.Specification
import scala.reflect.ClassTag
import java.lang.reflect.Constructor
class Trick {
val m: ClassTag[ReportService] = implicitly
val classLoader: ClassLoader = m.runtimeClass.getClassLoader
val trick: ReportService = Trick.createInstance[ReportService](m.runtimeClass, classLoader)
}
object Trick {
def createInstance[T <: AnyRef](klass: Class[_], loader: ClassLoader)(implicit m: ClassTag[T]): T = {
val constructors = klass.getDeclaredConstructors.toList.sortBy(_.getParameterTypes.size)
val constructor = constructors.head
createInstanceForConstructor(klass, constructor, loader)
}
private def createInstanceForConstructor[T <: AnyRef : ClassTag]
(c: Class[_], constructor: Constructor[_], loader: ClassLoader): T = {
constructor.setAccessible(true)
// This can be implemented generically, but I don't remember how to deal with variadic functions
// generically. IIRC even more reflection.
if (constructor.getParameterTypes.isEmpty)
constructor.newInstance().asInstanceOf[T]
else if (constructor.getParameterTypes.size == 1) {
// not implemented
null.asInstanceOf[T]
} else if (constructor.getParameterTypes.size == 2) {
val types = constructor.getParameterTypes.toSeq
val param1 = createInstance(types(0), loader)
val param2 = createInstance(types(1), loader)
constructor.newInstance(param1, param2).asInstanceOf[T]
} else {
// not implemented
null.asInstanceOf[T]
}
}
}
// NB: no need to #Inject here. The specs2 framework does it for us.
// It sees spec with parameter, and loads it for us.
class ReportServiceSpec (trick: Trick) extends Specification {
"ReportService" should {
"Work" in {
trick.trick.foo mustEqual 2
}
}
}
And that expectedly fails with
[info] ReportService should
[error] x Work
[error] '42' is not equal to '2' (FooSpec.scala:46)
If you don't need runtime dependency injection, then it's better to use cake pattern, and forget reflection all-together.
My colleague suggested a "low-tech" workaround. In the test, instantiate service classes with new:
class ReportServiceSpec extends Specification {
val service = new ReportService(new UserService, new SupportService)
// ...
}
This also works:
class ReportServiceSpec #Inject()(userService: UserService) extends Specification {
val service = new ReportService(userService, new SupportService)
// ...
}
Feel free to post more elegant solutions. I've yet to see a simple DI solution that works (with Guice, Play's default).
Does anyone else find it curious that Play's default test framework does not play well with Play's default DI mechanism?
Edit: In the end I went with an "Injector" test helper, almost the same as what Eric suggested:
Injector:
package testhelpers
import play.api.inject.guice.GuiceApplicationBuilder
import scala.reflect.ClassTag
/**
* Provides dependency injection for test classes.
*/
object Injector {
lazy val injector = (new GuiceApplicationBuilder).injector()
def inject[T: ClassTag]: T = injector.instanceOf[T]
}
Test:
class ReportServiceSpec extends Specification {
val service = Injector.inject[ReportService]
// ...
}
I implemented example explained in this link. But I rewrite it in scala. But I am getting java.lang.NoClassDefFoundError: org/eclipse/jetty/io/nio/AsyncConnection error.
Here is my RoomWebSocketHandler class:
import java.io.IOException
import javax.servlet.http.HttpServletRequest
import org.eclipse.jetty.websocket.WebSocket.Connection
import org.eclipse.jetty.websocket.{WebSocket, WebSocketHandler}
import scala.collection.mutable
class RoomWebSocketHandler extends WebSocketHandler {
private val webSockets = new mutable.ArrayBuffer[StateWebSocket]()
override def doWebSocketConnect(request: HttpServletRequest, protocol: String): WebSocket = {
new StateWebSocket()
}
private class StateWebSocket extends WebSocket.OnTextMessage {
var connection: Connection = _
def onOpen(connection: Connection) {
this.connection = connection
webSockets += this
}
def onMessage(data: String) {
try {
for (webSocket <- webSockets) {
webSocket.connection.sendMessage(data)
}
} catch {
case x: IOException => this.connection.close()
}
}
def onClose(closeCode: Int, message: String) {
webSockets -= this
}
}
}
and this is my Main class:
import java.net.InetSocketAddress
import grizzled.slf4j.Logger
import org.eclipse.jetty.server.{Handler, Server}
import org.eclipse.jetty.server.handler.{DefaultHandler, HandlerList, ResourceHandler}
import org.eclipse.jetty.servlet.ServletContextHandler
object Main {
var jettyServer: Option[Server] = None
def startServer(): Unit = {
val LOCAL_PORT = 4041
logger.debug("startServer begin")
jettyServer match {
case Some(s) =>
logger.info("Server is already running")
logger.debug("startServer end")
return
case None =>
logger.info("Server is not running")
}
val server = new Server(new InetSocketAddress("127.0.0.1", LOCAL_PORT))
server.setStopAtShutdown(true)
val handlers = new HandlerList()
val roomWebSocketHandler = new RoomWebSocketHandler();
roomWebSocketHandler.setHandler(new DefaultHandler());
handlers.setHandlers(Array[Handler](roomWebSocketHandler, new DefaultHandler()))
server.setHandler(handlers)
logger.debug("Starting jetty-server")
jettyServer = Some(server)
server.start()
logger.info("Server started on port: " + LOCAL_PORT)
logger.debug("startServer end")
}
def main(args: Array[String]): Unit = {
startServer()
}
}
and here is my dependencies:
dependencies {
compile 'org.slf4j:slf4j-api:1.7.12'
compile 'ch.qos.logback:logback-classic:1.1.3'
compile 'com.h2database:h2:1.4.188'
compile 'org.clapper:grizzled-slf4j_2.11:1.0.2'
compile 'org.eclipse.jetty:jetty-webapp:9.3.3.v20150827'
compile 'org.eclipse.jetty:jetty-websocket:8.1.17.v20150415'
compile 'org.eclipse.jetty:jetty-http:8.1.17.v20150415'
compile 'org.eclipse.jetty:jetty-io:8.1.17.v20150415'
compile 'org.eclipse.jetty:jetty-util:8.1.17.v20150415'
compile 'org.eclipse.jetty:jetty-continuation:8.1.17.v20150415'
compile 'org.eclipse.jetty:jetty-server:8.1.17.v20150415'
compile 'org.eclipse.jetty:jetty-jmx:8.1.17.v20150415'
compile 'commons-cli:commons-cli:1.3.1'
compile 'org.scala-lang:scala-library:2.11.7'
runtime 'javax.servlet:javax.servlet-api:3.1.0'
testCompile 'org.scalacheck:scalacheck_2.11:1.12.4'
}
Error:
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jetty/io/nio/AsyncConnection
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.eclipse.jetty.websocket.WebSocketHandler.<init>(WebSocketHandler.java:32)
at mypackage.RoomWebSocketHandler.<init>(RoomStateWebSocketHandler.scala:15)
at mypackage.Main$.startServer(Main.scala:48)
at mypackage.Main$.main(Main.scala:109)
at mypackage.Main.main(Main.scala)
Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.io.nio.AsyncConnection
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 29 more
I updated all my dependencies to Jetty 9.x and used examples shown in this link
I'm running into a problem with an actor that extends Stash and which works perfectly fine when instantiating it with actorOf in a simple ActorSystem. Now I would actually like to write some tests for my stashing actors before using them in my program. But I cannot figure out a way to use an TestActorRef with this actor in my test suite.
The code that works looks like this:
import akka.actor.{Stash, Actor, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object StashTest {
val config = ConfigFactory.parseString(
"""
|akka.actor.default-mailbox {
| mailbox-type = "akka.dispatch.UnboundedDequeBasedMailbox"
|}
""".stripMargin)
}
class StashTestActor extends Stash {
def receive: Actor.Receive = {
case "unstash" =>
unstashAll()
context become print
case msg => stash()
}
def print: Actor.Receive = {
case msg => println(s"Unstashed message: $msg")
}
}
val system = ActorSystem("stashSystem", StashTest.config)
val ref = system.actorOf(Props[StashTestActor])
ref ! "stash me"
ref ! "blah"
ref ! "unstash"
Which prints
Unstashed message: stash me
Unstashed message: blah
But if I try to write a WordSpec test for this actor, it leaves me with some nasty exceptions I can't figure out what they would like me to change in my code.
The test class looks like this
import akka.testkit.{TestActorRef, TestKit}
import akka.actor.{Stash, Actor, ActorSystem}
import org.scalatest.{WordSpecLike, MustMatchers}
import com.typesafe.config.ConfigFactory
class StashTestActor extends Stash {
def receive: Actor.Receive = {
case "unstash" =>
unstashAll()
context become print
case msg => stash()
}
def print: Actor.Receive = {
case msg => println(s"Unstashed message: $msg")
}
}
class StashTest extends TestKit(ActorSystem("testSystem", StashTest.config))
with WordSpecLike
with MustMatchers {
"A simple stashing actor" must {
val actorRef = TestActorRef[StashTestActor]
"stash messages" in {
actorRef ! "stash me!"
}
"unstash all messages" in {
actorRef ! "unstash"
}
}
}
object StashTest {
val config = ConfigFactory.parseString(
"""
|akka.actor.default-mailbox {
| mailbox-type = "akka.dispatch.UnboundedDequeBasedMailbox"
|}
""".stripMargin)
}
When running the test, I get following exceptions that are thrown during the instantiation of the TestActorRef.
[ERROR] [08/20/2013 14:19:40.765] [testSystem-akka.actor.default-dispatcher-3] [akka://testSystem/user/$$a] Could not instantiate Actor
Make sure Actor is NOT defined inside a class/trait,
if so put it outside the class/trait, f.e. in a companion object,
OR try to change: 'actorOf(Props[MyActor]' to 'actorOf(Props(new MyActor)'.
akka.actor.ActorInitializationException: exception during creation
at akka.actor.ActorInitializationException$.apply(Actor.scala:218)
at akka.actor.ActorCell.create(ActorCell.scala:578)
at akka.actor.ActorCell.invokeAll$1(ActorCell.scala:425)
at akka.actor.ActorCell.systemInvoke(ActorCell.scala:447)
at akka.dispatch.Mailbox.processAllSystemMessages(Mailbox.scala:262)
at akka.testkit.CallingThreadDispatcher.process$1(CallingThreadDispatcher.scala:244)
at akka.testkit.CallingThreadDispatcher.runQueue(CallingThreadDispatcher.scala:284)
at akka.testkit.CallingThreadDispatcher.register(CallingThreadDispatcher.scala:153)
at akka.dispatch.MessageDispatcher.attach(AbstractDispatcher.scala:133)
at akka.actor.dungeon.Dispatch$class.start(Dispatch.scala:84)
at akka.actor.ActorCell.start(ActorCell.scala:338)
at akka.testkit.TestActorRef.<init>(TestActorRef.scala:50)
at akka.testkit.TestActorRef$.apply(TestActorRef.scala:141)
at akka.testkit.TestActorRef$.apply(TestActorRef.scala:137)
at akka.testkit.TestActorRef$.apply(TestActorRef.scala:146)
at akka.testkit.TestActorRef$.apply(TestActorRef.scala:144)
at stashActorTest.StashTest$$anonfun$1.apply$mcV$sp(StashTestActor.scala:29)
at stashActorTest.StashTest$$anonfun$1.apply(StashTestActor.scala:28)
at stashActorTest.StashTest$$anonfun$1.apply(StashTestActor.scala:28)
at org.scalatest.SuperEngine.registerNestedBranch(Engine.scala:613)
at org.scalatest.WordSpecLike$class.org$scalatest$WordSpecLike$$registerBranch(WordSpecLike.scala:120)
at org.scalatest.WordSpecLike$$anon$2.apply(WordSpecLike.scala:851)
at org.scalatest.words.MustVerb$StringMustWrapperForVerb$class.must(MustVerb.scala:189)
at org.scalatest.matchers.MustMatchers$StringMustWrapper.must(MustMatchers.scala:6167)
at stashActorTest.StashTest.<init>(StashTestActor.scala:28)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at java.lang.Class.newInstance(Class.java:374)
at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:444)
at sbt.TestRunner.runTest$1(TestFramework.scala:84)
at sbt.TestRunner.run(TestFramework.scala:94)
at sbt.TestFramework$$anon$2$$anonfun$$init$$1$$anonfun$apply$8.apply(TestFramework.scala:224)
at sbt.TestFramework$$anon$2$$anonfun$$init$$1$$anonfun$apply$8.apply(TestFramework.scala:224)
at sbt.TestFramework$.sbt$TestFramework$$withContextLoader(TestFramework.scala:212)
at sbt.TestFramework$$anon$2$$anonfun$$init$$1.apply(TestFramework.scala:224)
at sbt.TestFramework$$anon$2$$anonfun$$init$$1.apply(TestFramework.scala:224)
at sbt.TestFunction.apply(TestFramework.scala:229)
at sbt.Tests$$anonfun$7.apply(Tests.scala:196)
at sbt.Tests$$anonfun$7.apply(Tests.scala:196)
at sbt.std.Transform$$anon$3$$anonfun$apply$2.apply(System.scala:45)
at sbt.std.Transform$$anon$3$$anonfun$apply$2.apply(System.scala:45)
at sbt.std.Transform$$anon$4.work(System.scala:64)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
at sbt.Execute.work(Execute.scala:244)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:160)
at sbt.CompletionService$$anon$2.call(CompletionService.scala:30)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
Caused by: akka.actor.ActorInitializationException: Could not instantiate Actor
Make sure Actor is NOT defined inside a class/trait,
if so put it outside the class/trait, f.e. in a companion object,
OR try to change: 'actorOf(Props[MyActor]' to 'actorOf(Props(new MyActor)'.
at akka.actor.ActorInitializationException$.apply(Actor.scala:218)
at akka.testkit.TestActorRef$$anonfun$apply$2$$anonfun$apply$1.applyOrElse(TestActorRef.scala:148)
at akka.testkit.TestActorRef$$anonfun$apply$2$$anonfun$apply$1.applyOrElse(TestActorRef.scala:147)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:33)
at scala.util.Failure$$anonfun$recover$1.apply(Try.scala:185)
at scala.util.Try$.apply(Try.scala:161)
at scala.util.Failure.recover(Try.scala:185)
at akka.testkit.TestActorRef$$anonfun$apply$2.apply(TestActorRef.scala:147)
at akka.testkit.TestActorRef$$anonfun$apply$2.apply(TestActorRef.scala:153)
at akka.actor.CreatorFunctionConsumer.produce(Props.scala:369)
at akka.actor.Props.newActor(Props.scala:323)
at akka.actor.ActorCell.newActor(ActorCell.scala:534)
at akka.actor.ActorCell.create(ActorCell.scala:560)
... 58 more
Caused by: java.lang.NullPointerException
at akka.actor.UnrestrictedStash$class.$init$(Stash.scala:82)
at stashActorTest.StashTestActor.<init>(StashTestActor.scala:9)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at akka.actor.ReflectiveDynamicAccess$$anonfun$createInstanceFor$2.apply(DynamicAccess.scala:78)
at scala.util.Try$.apply(Try.scala:161)
at akka.actor.ReflectiveDynamicAccess.createInstanceFor(DynamicAccess.scala:73)
... 64 more
I don't have any problems with using TestActorRefs with actors that don't extend Stash. So I don't know if it is a configuration error or something else I'm missing.
TestActorRef can't be used together with Stash. TestActorRef requires a CallingThreadMailbox and Stash requires DequeBasedMessageQueueSemantics. The documentation should include this limitation and the error messages should be improved.
Using the default akka dispatcher allowed me to use Stash and TestActorRef:
val myActor = TestActorRef[MyActor](Props(classOf[MyActor]).withDispatcher("akka.actor.default-dispatcher"))
Note that this means your tests will no longer use the default CallingThreadDispatcher and will lose the benefits highlighted in the akka docs
I was able to test actors with Stash like this:
val actor = TestActorRef(Props(new MyActorWithStash()).withDispatcher("deque"))
I've added these Scalac options in my SBT build:
scalacOptions ++= Seq("-unchecked", "-deprecation","-feature"),
But then I can't access my Play 2.1 web application anymore.
It is the only modified part of my build, and if I remove this, it works fine again.
It's weird because the application seems to be running (I see startup logs, scheduled jobs...), but trying to access it from the browser gives me Play error page:
Cannot init the Global object
No source available, here is the exception stack trace:
->java.lang.ExceptionInInitializerError:
Global$.<init>(Global.scala:17)
Global$.<clinit>(Global.scala)
sun.misc.Unsafe.ensureClassInitialized(Native Method)
sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor(UnsafeFieldAccessorFactory.java:43)
sun.reflect.ReflectionFactory.newFieldAccessor(ReflectionFactory.java:140)
java.lang.reflect.Field.acquireFieldAccessor(Field.java:949)
java.lang.reflect.Field.getFieldAccessor(Field.java:930)
java.lang.reflect.Field.get(Field.java:372)
play.api.WithDefaultGlobal$class.play$api$WithDefaultGlobal$$scalaGlobal(Application.scala:37)
play.api.DefaultApplication.play$api$WithDefaultGlobal$$scalaGlobal$lzycompute(Application.scala:383)
play.api.DefaultApplication.play$api$WithDefaultGlobal$$scalaGlobal(Application.scala:383)
play.api.WithDefaultGlobal$$anonfun$play$api$WithDefaultGlobal$$globalInstance$1$$anonfun$apply$5.apply(Application.scala:52)
play.api.WithDefaultGlobal$$anonfun$play$api$WithDefaultGlobal$$globalInstance$1$$anonfun$apply$5.apply(Application.scala:52)
scala.Option.getOrElse(Option.scala:120)
play.api.WithDefaultGlobal$$anonfun$play$api$WithDefaultGlobal$$globalInstance$1.apply(Application.scala:52)
play.api.WithDefaultGlobal$$anonfun$play$api$WithDefaultGlobal$$globalInstance$1.apply(Application.scala:51)
play.utils.Threads$.withContextClassLoader(Threads.scala:18)
play.api.WithDefaultGlobal$class.play$api$WithDefaultGlobal$$globalInstance(Application.scala:50)
play.api.DefaultApplication.play$api$WithDefaultGlobal$$globalInstance$lzycompute(Application.scala:383)
play.api.DefaultApplication.play$api$WithDefaultGlobal$$globalInstance(Application.scala:383)
play.api.WithDefaultGlobal$class.global(Application.scala:66)
play.api.DefaultApplication.global(Application.scala:383)
play.api.WithDefaultConfiguration$class.play$api$WithDefaultConfiguration$$fullConfiguration(Application.scala:80)
play.api.DefaultApplication.play$api$WithDefaultConfiguration$$fullConfiguration$lzycompute(Application.scala:383)
play.api.DefaultApplication.play$api$WithDefaultConfiguration$$fullConfiguration(Application.scala:383)
play.api.WithDefaultConfiguration$class.configuration(Application.scala:82)
play.api.DefaultApplication.configuration(Application.scala:383)
play.api.Application$class.$init$(Application.scala:268)
play.api.DefaultApplication.<init>(Application.scala:383)
play.core.ReloadableApplication$$anonfun$get$1$$anonfun$apply$1$$anonfun$1$$anon$1.<init>(ApplicationProvider.scala:128)
play.core.ReloadableApplication$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(ApplicationProvider.scala:128)
play.core.ReloadableApplication$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(ApplicationProvider.scala:114)
scala.Option.map(Option.scala:145)
play.core.ReloadableApplication$$anonfun$get$1$$anonfun$apply$1.apply(ApplicationProvider.scala:114)
play.core.ReloadableApplication$$anonfun$get$1$$anonfun$apply$1.apply(ApplicationProvider.scala:112)
scala.util.Either$RightProjection.flatMap(Either.scala:523)
play.core.ReloadableApplication$$anonfun$get$1.apply(ApplicationProvider.scala:112)
play.core.ReloadableApplication$$anonfun$get$1.apply(ApplicationProvider.scala:104)
scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
java.lang.Thread.run(Thread.java:722)
My Global.scala looks like this:
object Global extends GlobalSettings {
// TODO use cake pattern injection?
val searchService: SearchService = StampleApplication.get.searchService
val breadcrumbService: BreadcrumbService = StampleApplication.get.breadcrumbService
override def onStart(app: Application) {
Logger.info("Application has started")
startElasticSearchIndexationScheduling
startBreadcrumbUpdaterJob
}
override def onStop(app: Application) {
Logger.info("Application shutdown...")
}
// TODO externaliser delais des jobs dans la conf play?
def startElasticSearchIndexationScheduling = {
Akka.system.scheduler.schedule(30 seconds , 30 seconds) {
Logger.info("Indexation job triggered")
searchService.indexUnindexedStamples
searchService.indexUnindexedUsers
}
}
def startBreadcrumbUpdaterJob = {
Akka.system.scheduler.schedule(30 seconds, 30 seconds) {
Logger.info("Breadcrumb updated job triggered")
breadcrumbService.updateChildsBreadcrumbForFlaggedCategories
Logger.info("Breadcrumb updated (with recursion!) job triggered")
breadcrumbService.recompteAllFlaggedWithRecursion
}
}
}
The Global$.(Global.scala:17) seems to reference this line:
val searchService: SearchService = StampleApplication.get.searchService
The rest of the sources are:
object StampleApplication {
val get: CakeApplication = {
println("Building stample cake application")
// Inject properties here
new CakeConfiguration().buildApplication
}
}
trait CakeApplication
extends ServiceLayer
with RepositoryLayer
trait ServiceLayer
extends RepositoryLayer
with DefaultUserServiceComponent
with DefaultCategoryServiceComponent
with DefaultStampleServiceComponent
with DefaultAclServiceComponent
with DefaultFileServiceComponent
with DefaultApiCallServiceComponent
with DefaultBreadcrumbServiceComponent
with DefaultSearchServiceComponent
with DefaultRelationshipServiceComponent
with DefaultSharingServiceComponent
with DefaultAuthCodeServiceComponent
with DefaultNotificationServiceComponent
with DefaultEventServiceComponent
with DefaultCommentServiceComponent
trait RepositoryLayer
extends MongoUserRepositoryComponent
with MongoCategoryRepositoryComponent
with MongoCategorySharingRepositoryComponent
with MongoStampleRepositoryComponent
with MongoFileRepositoryComponent
with MongoApiCallRepositoryComponent
with MongoRelationshipRepositoryComponent
with MongoSharingRepositoryComponent
with MongoNotificationRepositoryComponent
with SalatDAOFactoryComponent
with CollectionProviderComponent
trait CollectionProviderComponent {
def collectionProvider:MongoCollectionProvider
}