I'm looking for a way to inject a dependency into a Test (in /tests/models/) that looks like following:
class FolderSpec(implicit inj: Injector) extends Specification with Injectable{
val folderDAO = inject [FolderDAO]
val user = User(Option(1), LoginInfo("key", "value"), None, None)
"Folder model" should {
"be addable to the database" in new WithFakeApplication {
folderDAO.createRootForUser(user)
val rootFolder = folderDAO.findUserFolderTree(user)
rootFolder must beSome[Folder].await
}
}
}
Where
abstract class WithFakeApplication extends WithApplication(FakeApplication(additionalConfiguration = inMemoryDatabase()))
/app/modules/WebModule:
class WebModule extends Module{
bind[FolderDAO] to new FolderDAO
}
/app/Global:
object Global extends GlobalSettings with ScaldiSupport with SecuredSettings with Logger {
def applicationModule = new WebModule :: new ControllerInjector
}
But at compilation time I have following stack trace:
[error] Could not create an instance of models.FolderSpec
[error] caused by java.lang.Exception: Could not instantiate class models.FolderSpec: argument type mismatch
[error] org.specs2.reflect.Classes$class.tryToCreateObjectEither(Classes.scala:93)
[error] org.specs2.reflect.Classes$.tryToCreateObjectEither(Classes.scala:207)
[error] org.specs2.specification.SpecificationStructure$$anonfun$createSpecificationEither$2.apply(BaseSpecification.scala:119)
[error] org.specs2.specification.SpecificationStructure$$anonfun$createSpecificationEither$2.apply(BaseSpecification.scala:119)
[error] scala.Option.getOrElse(Option.scala:120)
Sadly, I didn't find anything on the matter in Scaldi documentation.
Is there a way to inject things in tests?
Scaldi does not provide an integration with any testing framework, but you actually normally don't need it. What you can do in this case is to create a test Module that contains mocks and stubs (like in-memory databases) and then just provide a test Global to the FakeApplication. Here is an example of how you can do it:
"render the index page" in {
class TestModule extends Module {
bind [MessageService] to new MessageService {
def getGreetMessage(name: String) = "Test Message"
}
}
object TestGlobal extends GlobalSettings with ScaldiSupport {
// test module will override `MessageService`
def applicationModule = new TestModule :: new WebModule :: new UserModule
}
running(FakeApplication(withGlobal = Some(TestGlobal))) {
val home = route(FakeRequest(GET, "/")).get
status(home) must equalTo(OK)
contentType(home) must beSome.which(_ == "text/html")
println(contentAsString(home))
contentAsString(home) must contain ("Test Message")
}
}
You can find this code in the scaldi-play-example application.
Related
When using ScalaTest's org.scalatest.FlatSpecLike, I can create a Fixture and mix it into the test, as follows:
class Foo extends FlatSpecLike with Matchers {
trait Fixture {
val bar = 2
}
"This test method" should "not cause a compile error" in new Fixture {
2 shouldEqual bar
}
}
However, when I use org.scalatest.fixture.FlatSpecLike instead, I cannot do this:
class Baz extends fixture.FlatSpecLike with Matchers {
trait Fixture {
val bar = 2
}
"This test method" should "cause a compile error" in new Fixture { implicit session =>
...
}
}
Could someone suggest how I can mix in a trait into a test method with fixture.FlatSpecLike ?
UPDATE:
Here is the error from the compiler after I run ~test:compile in SBT:
in new Fixture { implicit session =>
[error] ^
[error] one error found
[error] (Test / compileIncremental) Compilation failed
Intellij shows something like Type mismatch, expected () => Any, actual: Foo.this.Fixture with Object { ... }
I want to inject dependency with Generic type using Guice. Find below example in scala which replicate the issue.
ProductModel.scala
trait BaseProduct
case class Product() extends BaseProduct
CartService.scala
class CartService[A <: BaseProduct] #Inject()(productService : ProductService[A]) {
def getCartItems = productService.getProduct
}
ProductService.scala
class ProductService[A]{
def getProduct = println("ProductService")
}
Main.scala
object Main extends App {
val injector = Guice.createInjector(new ShoppingModule)
val cartService = injector.getInstance(classOf[CartService[Product]])
cartService.getCartItems
}
class ShoppingModule extends AbstractModule with ScalaModule {
override def configure(): Unit = {
bind[BaseProduct].to(scalaguice.typeLiteral[Product])
}
}
while running this Main.scala app getting below error.
service.ProductService<A> cannot be used as a key; It is not fully specified.
I have tried binding using codingwell library. But it doesn't help to identify ProductService Type.
When you create instance of cartService at that time use typeLiteral to create instance like
val cartService = injector.getInstance(Key.get(scalaguice.typeLiteral[CartService[Product]])
if you create instance like above you don't need to create module.
Create injector using default Module (i.e. useful if you have any other bindings in default Module.scala at application level)
val appBuilder = new GuiceApplicationBuilder()
val injector = Guice.createInjector(appBuilder.applicationModule())
and if you don't have any module you can skip passing module as argument and create injector without passing any module as well like
val injector = Guice.createInjector()
I would expect this test to pass:
import org.scalamock.scalatest.MockFactory
import org.scalatest.{FlatSpec, Matchers}
class FruitImpl {
case class FruitName(name: String)
def getFruitName: Option[FruitName] = {
Some(FruitName("apple"))
}
}
class FruitSpec extends FlatSpec with Matchers with MockFactory {
val f = mock[FruitImpl]
(f.getFruitName _).expects().returning(None)
behavior of "getFruitName method"
it should "return None" in {
f.getFruitName should === (None)
}
}
But it fails with:
[error] my/path/QuestionTest.scala:13: overriding method getFruitName in class FruitImpl of type => Option[this.FruitName];
[error] method getFruitName has incompatible type
[error] val f = mock[FruitImpl]
[error] ^
This works, though:
import org.scalamock.scalatest.MockFactory
import org.scalatest.{FlatSpec, Matchers}
case class FruitName(name: String)
class FruitImpl {
def getFruitName: Option[FruitName] = {
Some(FruitName("apple"))
}
}
class FruitSpec extends FlatSpec with Matchers with MockFactory {
val f = mock[FruitImpl]
(f.getFruitName _).expects().returning(None)
behavior of "getFruitName method"
it should "return None" in {
f.getFruitName should === (None)
}
}
The only difference is that the case class FruitName is defined outside of the class FruitImpl. Why does one version of the code fails and the other doesn't? What should one do to fix the error in the first example?
Without looking at the ScalaMock code, I'd say that the mock is not a true derivation of FruitImpl in the OO sense. Its purpose is to allow method interception, so it only deals with the facade. It follows then that the mock actually has no definition of the path dependent type FruitName, and so cannot work with a method signature that depends on it.
This is precisely why it does work when the FruitName definition is moved out of FruitImpl. It now exists independently of the mock, who's method signatures depending on it then work as expected.
Suppose I had this interface and class:
abstract class SomeInterface{
def doSomething : Unit
}
class ClassBeingTested(interface : SomeInterface){
def doSomethingWithInterface : Unit = {
Unit
}
}
Note that the doSomethingWithInterface method does not actually do anything with the interface.
I create a test for it like this:
import org.specs2.mutable._
import org.specs2.mock._
import org.mockito.Matchers
import org.specs2.specification.Scope
trait TestEnvironment extends Scope with Mockito{
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
class ClassBeingTestedSpec extends Specification{
"The ClassBeingTested" should {
"#doSomethingWithInterface" in {
"calls the doSomething method of the given interface" in new TestEnvironment {
test.doSomethingWithInterface
there was one(interface).doSomething
}
}
}
}
This test passes. Why? Am I setting it up wrong?
When I get rid of the scope:
class ClassBeingTestedSpec extends Specification with Mockito{
"The ClassBeingTested" should {
"#doSomethingWithInterface" in {
"calls the doSomething method of the given interface" in {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
test.doSomethingWithInterface
there was one(interface).doSomething
}
}
}
}
The test fails as expected:
[info] x calls the doSomething method of the given interface
[error] The mock was not called as expected:
[error] Wanted but not invoked:
[error] someInterface.doSomething();
What is the difference between these two tests? Why does the first one pass when it should fail? Is this not an intended use of Scopes?
When you mix-in the Mockito trait to another trait you can create expectations like there was one(interface).doSomething. If such an expression fails it only returns a Result, it doesn't throw an Exception. It then gets lost in a Scope because it is just a "pure" value inside the body of a trait.
However if you mix-in the Mockito trait to a mutable.Specification then an exception will be thrown on a failure. This is because the mutable.Specification class specifies that there should be ThrownExpectations by mixing in that trait.
So if you want to create a trait extending both Scope you can either:
create the trait from inside the specification and not have it extend Mockito:
class MySpec extends mutable.Specification with Mockito {
trait TestEnvironment extends Scope {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
...
}
create trait and specification as you do, but mix-in org.specs2.execute.ThrownExpectations
trait TestEnvironment extends Scope with Mockito with ThrownExpectations {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
class MySpec extends mutable.Specification with Mockito {
...
}
I am trying folow the example from Mock Objects in Play[2.0] but unfortunately I am not having success.
I have a UsersController that uses a UserModel.
trait UserModel extends ModelCompanion[User, ObjectId] {
// ...
}
Next, the abstract controller
abstract class UsersController extends Controller {
val userModel: UserModel
def sayHello = Action(parse.json) { request =>
// return a play Action. Doesn't use userModel
}
// Other methods
}
In the routes file, I call method say Hello in this way:
POST /hello controllers.Users.sayHello
In test directory, I created a subclass of UsersController using a UserModel mock.
package controllers
import org.specs2.mock.Mockito
object UserControllersTest extends UsersController with Mockito {
val userModel = mock[models.UserModel]
}
Now, the main part. I created a Spec test following the Jacob Groundwater example in the page mentioned before. In pluing argument for FakeApplication, I included a calling to UserControllersTest.
package controllers
import org.specs2.mutable.Specification
import play.api.libs.json.Json
import play.api.test._
import play.api.test.Helpers._
class UsersSayHelloSpec extends Specification {
running(FakeApplication()) {
"Users.SayHello" should {
def sendJson(jsonMap: Map[String, String], shouldBeCorrect: Boolean) = {
running(new FakeApplication(
additionalPlugins = Seq("controllers.UserControllersTest"))) {
// Preapration
val jsonRequisition = Json.toJson(jsonMap)
val Some(result) = routeAndCall(FakeRequest(POST,
"/hello",
FakeHeaders(Map("Content-Type" -> Seq("application/json"))),
jsonRequisition))
// ...
}
}
"Not process a empty String" in {
sendJson(Map.empty[String, String], false)
}
// Other tests calling sendJson ...
}
}
}
However, when I run the test I got this error message:
[info] Users.SayHello should
[error] ! Not process a empty String
[error] PlayException: Cannot load plugin [Plugin [controllers.UserControllersTest] cannot been instantiated.] (Application.scala:171)
...
[error] play.api.Application.<init>(Application.scala:158)
[error] play.api.test.FakeApplication.<init>(Fakes.scala:141)
[error] controllers.UsersSayHelloSpec$$anonfun$1$$anonfun$apply$5.sendJson$1(UsersSayHelloSpec.scala:20)
[error] controllers.UsersSayHelloSpec$$anonfun$1$$anonfun$apply$5$$anonfun$apply$26.apply(UsersSayHelloSpec.scala:46)
[error] controllers.UsersSayHelloSpec$$anonfun$1$$anonfun$apply$5$$anonfun$apply$26.apply(UsersSayHelloSpec.scala:46)
Where UsersSayHelloSpec.scala:20 referes to line where I call running method.
So my question is: What am I doing wrong?
I'm not sure what exactly are you trying to do, but the answer for question 'What am I doing wrong?' is:
The parameter 'additionalPlugins' is for additional Play plugins, and 'controllers.UserControllersTest' is not a Play plugin. It's a Controller.
You can read about Play 2 plugins here: http://www.objectify.be/wordpress/?p=464
Have you tried these examples: http://www.playframework.org/documentation/2.0.4/ScalaFunctionalTest ?