How do I mock the class which has Configuration(play.api) injected as arguments to its constructor?
class SomeScalaClass #Inject(config: Configuration){
val someValue = config.get[String]("someValueInConfig")
def abc:Int = {
..
..
}
}
I am using scalamock. I am trying to test a class which has "SomeScalaClass" injected as dependency to it:
class ClassToTest #Inject()(obj: SomeScalaClass){.....}
I am getting error when I do this in my test Fixture:
val someScalaClassMock = mock[SomeScalaClass]
val classToTestObj = new ClassToTest(someScalaClassMock)
Error is: not found: value someScalaClassMock
I am new to scala so not sure how to tackle this issue. Thanks in Advance
You can try making someValue val as lazy val or extracting the config value inside the method definition if required inside a single method.
Or you can use typesafe config which does not need any Injection.
import com.typesafe.config.ConfigFactory
val someValue= ConfigFactory.load().getString("someValueInConfig")
Related
I am trying to convert from Guice to Macwire as a dependency injection framework. It is going fine apart from this Silhouette Module where I am getting a compilation error. Error at bottom.
Working Module in Guice:
class SilhouetteModule #Inject()(environment: play.api.Environment, configuration:
Configuration) extends AbstractModule with ScalaModule {
override def configure() = {
val iamConfig = IAMConfiguration
.fromConfiguration(configuration)
.fold(throw _, identity)
val htPasswdFile = File.apply(configuration.get[String]("file"))
bind[IdentityService[User]].toInstance(SimpleIdentityService.fromConfig(iamConfig))
bind[Silhouette[BasicAuthEnv]].to[SilhouetteProvider[BasicAuthEnv]]
bind[RequestProvider].to[BasicAuthProvider].asEagerSingleton()
bind[PasswordHasherRegistry].toInstance(PasswordHasherRegistry(new BCryptPasswordHasher()))
bind[AuthenticatorService[DummyAuthenticator]].toInstance(new DummyAuthenticatorService)
bind[AuthInfoRepository].toInstance(HtpasswdAuthInfoRepository.fromFile(htPasswdFile))
bind[SecuredErrorHandler].to[RestHttpSecuredErrorHandler]
}
#Provides
def provideEnvironment(identityService: IdentityService[User], authenticatorService:
AuthenticatorService[DummyAuthenticator], eventBus: EventBus, requestProvider:
RequestProvider): Environment[BasicAuthEnv] =
Environment[BasicAuthEnv](
identityService,
authenticatorService,
Seq(requestProvider),
eventBus
)
}
}
Equivalent attempt in Macwire:
trait SilhouetteModule extends BuiltInComponents {
import com.softwaremill.macwire._
val iamConfig = IAMConfiguration
.fromConfiguration(configuration)
.fold(throw _, identity)
val htPasswdFile = File.apply(configuration.get[String]("file"))
lazy val identityService: IdentityService[User] =
SimpleIdentityService.fromConfig(iamConfig)
lazy val basicAuthEnv: Silhouette[BasicAuthEnv] = wire[SilhouetteProvider[BasicAuthEnv]]
lazy val requestProvider: RequestProvider = wire[BasicAuthProvider]
lazy val passwordHasherRegistry: PasswordHasherRegistry = PasswordHasherRegistry(
new BCryptPasswordHasher())
lazy val authenticatorService: AuthenticatorService[DummyAuthenticator] =
new DummyAuthenticatorService
lazy val authInfoRepo: AuthInfoRepository =
HtpasswdAuthInfoRepository.fromFile(htPasswdFile)
lazy val errorHandler: SecuredErrorHandler = wire[RestHttpSecuredErrorHandler]
lazy val env: Environment[BasicAuthEnv] = Environment[BasicAuthEnv](
identityService,
authenticatorService,
Seq(requestProvider),
eventBus
)
def eventBus: EventBus
}
The Macwire example does not compile: I get an error:
Cannot find a value of type: [com.mohiva.play.silhouette.api.actions.SecuredAction]
lazy val basicAuthEnv: Silhouette[BasicAuthEnv] = wire[SilhouetteProvider[BasicAuthEnv]]
Sorry its a lot of code but I thought a side-by-side comparison would be more helpful.
Any help would be great!
MacWire doesn't magically creates values - if it needs to construct a value, it looks what values are taken by the constructor and - if by looking at all values available in the scope it can unambiguously find all the parameters of the constructor, the macro creates code new Class(resolvedArg1, resolvedArg2, ...).
So
all of these values have to be in Scope - they can be constructed by MacWire, or be abstract members implemented by some mixin, but still you have to write them down explicitly
if you have 2 values of the same type in scope - MacWire cannot generate the code because how it would know which value to pick? (Well, if one of the values is in closer "closer" than the other it can, but if they are equally close it cannot be resolved)
So if you get the error:
Cannot find a value of type: [com.mohiva.play.silhouette.api.actions.SecuredAction]
it means that you haven't declared any value of type com.mohiva.play.silhouette.api.actions.SecuredAction in SilhouetteModule nor in BuiltInComponents.
If this is something that is provided by another trait you can add abstract declaration here
val securedAction: SecuredAction // abstract val
and implement it somewhere else (be careful to avoid circular dependencies!).
I am upgrading to play framework 2.5, I have objects that are very hard to turn them to classes in order to use dependency injection, so I used this method instead:
object test {
#Inject var config: Configuration = _
def portNumber = config.getInt("server.port")
}
However on runTime i got null pointer exception, the old code used to be like this :
object test {
def portNumber = Play.configuration.getInt("server.port")
}
but it is deperecated and I must change it with DI.
and another question on the fly is it possible to the same if I have got a trait instead of an object
Another way to do is
import com.typesafe.config.ConfigFactory
val restConfig = ConfigFactory.load("rest.conf") //your conf file
val pageSize = restConfig.getInt("pagesize") //the value you want from conf file
You could set the configuration in a Singleton, like:
#Singleton
class ConfigForTest #Inject()(config: Configuration) {
test.config = config
}
And set from here config in the test Object.
So your test object looks like this:
object test {
var config: Configuration = _
def portNumber = config.getInt("server.port")
}
Don't forget to initialise the Singleton in your Module:
class Module
extends AbstractModule {
#Override()
override def configure(): Unit = {
bind(classOf[ConfigForTest])
.asEagerSingleton()
...
Or as Shweta shows, do it without any Injection. As you have a Play app, this would be enough:
import com.typesafe.config.ConfigFactory
object test {
val portNumber = ConfigFactory.load().getInt("server.port")
}
This takes application.conf direct from the Classpath.
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()
using play 2.5 and guice i have managed to successfully inject applicationConfig into a singleton class and reference a config variable inside it,
trait TMongoFactory{
val SERVER: String
val PORT: Int
val DATABASE: String
val connection: MongoClient
val collection: MongoDB
}
#Singleton
class MongoFactory #Inject()(val configuration: Configuration) extends TMongoFactory{
val SERVER = "localhost"
val PORT = 27017
val DATABASE = configuration.underlying.getString("connectionString")
val connection = MongoClient(SERVER, PORT)
val collection = connection(DATABASE)
}
class MongoModule extends AbstractModule {
def configure() = {
bind(classOf[TMongoFactory]).to(classOf[MongoFactory])
}
}
I can then pass this singleton to a repository class like so
#Singleton
class MongoRemainingAllowanceRepository #Inject()(MongoFactory: TMongoFactory) extends RemainingAllowanceRepository{
val context = MongoFactory.collection("remainingAllowance")
def save(remainingAllowance: RemainingAllowance): Unit ={
context.save(RemainingAllowance.convertToMongoObject(remainingAllowance))
}
This all works fine and as expected, but the problem is i need to call this repository in the test suite so i dont want it to have to take any arguments (specifically injected ones).
So i tried to change it to use an injector inside the body like so
#Singleton
class MongoRemainingAllowanceRepository extends RemainingAllowanceRepository{
val injector = Guice.createInjector(new MongoModule)
val mongoFactory = injector.getInstance(classOf[TMongoFactory])
val context = mongoFactory.collection("remainingAllowance")
def save(remainingAllowance: RemainingAllowance): Unit ={
context.save(RemainingAllowance.convertToMongoObject(remainingAllowance))
}
This feels like it should work and it compiles fine, but then on test or run it throws an error
Could not find a suitable constructor in play.api.Configuration. Classes
must have either one (and only one) constructor annotated with #Inject
or a zero-argument constructor that is not private. at
play.api.Configuration.class(Configuration.scala:173) while locating
play.api.Configuration
Apologies for the long post but i feel i needed to include most of this.
Does anyone know why this happens on an injector? Do i need to bind the configuration manually also now im referencing the custom module?
Any help appreciated
Thanks
Jack
When you create your class you can pass in the configuration yourself. Say you need key apiKey and its value...
val sampleConfig = Map("apiKey" ->"abcd1234")
val mongoFactory = new MongoFactory(Configuration.from(sampleConfig))
I'm following the advice from com.googlegroups.google-guice http://markmail.org/message/ljnhl6rstverrxuj
Well, it's actually almost as referred to you in the link from the other answer:
http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di/
class MyClient {
#Inject val toBeInjected: AnotherClass = toBeInjected // !!
}
trait ServiceInjector {
ServiceInjector.inject( this )
}
object ServiceInjector {
private val injector = Guice.createInjector( Array[Module]( new YourModule ) )
def inject( obj: AnyRef ) = injector.injectMembers( obj )
}
Usage:
val client = new MyClient with ServiceInjector
or:
class InjectedMyClient extends MyClient with ServiceInjector
But I'm very new to Scala and trying to work out how I can use the following pattern for dependency injection, when the Guice Module itself needs references to instances passed in from elsewhere.
But since traits can't have constructors, and neither can the Companion Object it looks like I'm screwed?
package au.id.rleach.overmind.guice
import com.google.inject.{Provides, Guice, Binder, Module}
import org.slf4j.Logger
import org.spongepowered.api.service.ServiceManager
import org.spongepowered.api.world.TeleportHelper
import org.spongepowered.api.{GameRegistry, Game}
import org.spongepowered.api.plugin.PluginManager
import org.spongepowered.api.scoreboard.ScoreboardBuilder
import org.spongepowered.api.service.event.EventManager
class OModule(val game: Game, val logger: Logger, val pluginManager: PluginManager, val serviceManager: ServiceManager, val eventManager: EventManager, val gameRegistry: GameRegistry, val teleportHelper: TeleportHelper) extends Module {
override def configure(binder: Binder): Unit = {
binder.bind(classOf[Game]).toInstance(game)
binder.bind(classOf[Logger]).toInstance(logger)
binder.bind(classOf[PluginManager]).toInstance(pluginManager)
binder.bind(classOf[ServiceManager]).toInstance(serviceManager)
binder.bind(classOf[EventManager]).toInstance(eventManager)
binder.bind(classOf[GameRegistry]).toInstance(gameRegistry)
binder.bind(classOf[TeleportHelper]).toInstance(teleportHelper)
//bind(classOf[File]).annotatedWith(new ConfigDirAnnotation(true)).toInstance(Loader.instance.getConfigDir)
}
}
trait ServiceInjector {
ServiceInjector.inject(this)
}
object ServiceInjector {
private val injector = Guice.createInjector(
//####
new OModule()//compilation error.
//####
)
def inject(obj: AnyRef) = injector.injectMembers(obj)
}
I realize that the object is being initialized when it's first used, and that is after I have a copy of the instances to pass to OModule, but I can't seem to find a way to pass them in to the object.
Since I'm using Scala I am not a fan anymore of using DI frameworks since Scala has natively DI-like support already. This is called the Cake Pattern. There are plenty resources available on this, like this blogpost from Cake Solutions.
Both at ScalaDays 2014 and Devoxx 2014 Dick Wall also presented about a more lightweight DI solution which he called the Parfait Pattern. Both talks can be viewed on Parleys.com
If you really want to use a DI framework, Scaldi is a nice looking Scala framework utilising Scala features, but of course you can also keep on using Spring or Guice.
I'm not sure about this:
#Inject val toBeInjected: AnotherClass = toBeInjected
wouldn't work in my experience. It needs to be a var rather than val and the initial value null.
#Inject var toBeInjected: AnotherClass = null
I created a demo on GitHub which is the Play-Scala template with the index method changed as follows:
class Application extends Controller {
#Inject var ws: WSClient = null
def index = Action.async {
ws.url("http://google.com").get.map(r => Ok(r.body))
}
}
which worked well. That injected to a field, rather than as a constructor parameter. The same technique can be used with traits.