Scala dependency injection simple pattern - scala

I hope you are having a great day!
By the requirement of the framework, I have to use inject Configuration class and in order to get configuration keys.
The problem is now I have to refactor my code but I can't figure out how can I do that.
Problem
For the sake of simplicity let's consider a Sender class and it's companion object.
class Sender(image: File, name: String) {
def send() = { Sender.s3Client.send(image, name) }
}
object Sender {
val accessKey = config.get[String]("accessKey")
val secretKey = config.get[String]("secretKey")
val s3Client: AmazonS3 = ... withCredentials ( accessKey, secretKey) ...
}
Here I config.get method should be an injected object.
Question
How can I inject Configuration class in this scenario?
I can't use like below because Some other method instantiates this class with image and name param
class Sender #Inject() (image: File, name: String, config: Configuration) { ... }
Thank you!

In Scala, you can have your own DI container even without using DI Framework.
trait ConfigurationModule {
def config: Configuration
}
trait S3Module {
def accessKey: String
def secretKey: String
lazy val s3Client: AmazonS3 = ... withCredentials ( accessKey, secretKey) ...
}
object YourOwnApplicationContext extends ConfigurationModule with S3Module with ... {
...
lazy config: Configuration = ConfigFactory.load()
lazy val accessKey = config.get[String]("accessKey")
lazy val secretKey = config.get[String]("secretKey")
}
Now all your dependencies are in YourOwnApplicationContext.
So you can do this:
class Sender(image: File, name: String) {
def send() = YourOwnApplicationContext.s3Client.send(image, name)
}
You may read these articles:
MacWire
Dependency Injection in Scala using MacWire

Related

Dependency mocking in scala

class Service1 #Inject()(service2: Service2) {
val url = service2.REDIS_URL
}
class TestService #Inject()(service1: Service1) {
def foo() => {}
}
I have the above 2 classes.
I need to test TestService.foo. Following is the code that I am trying but its not working.
class TestServiceTest extends org.scalatest.AsyncFunSuite with MockFactory {
val service1Mock = mock[Service1]
....
....
}
While initiating the test cases service2.REDIS_URL fails with null pointer error.
I am unable to find any answers in the scala mock documentation about how to properly mock services/singleton objects.
Update:
class Service2 #Inject()(){
val REDIS_URL = "some constant"
}
class Service1 #Inject()(service2: Service2){
val redisStr = service2.REDIS_URL
def getUrl = redisStr
}
class TestService #Inject()(service1: Service1){
def foo() = service1.getUrl
}
it should "test properly" in {
val mocks1 = mock[Service1]
}
This is not working
but if we change Service1 to
class Service1 #Inject()()(service2: Service2) {
def url = service2.REDIS_URL
}
it works.
But,
class Service1 #Inject()()(service2: Service2) {
def url = service2.REDIS_URL
config.useSingleServer()
.setAddress(REDIS_URL)
}
Again fails
This is due to service2 being null while the Mock is generated. This is very weird that the class is run while creating the Mock in ScalaTest and it finds service2 to be null causing NPE.
No, you cannot mock singleton objects in Scala. But I don't see any in your code. And you mock services just like any other class in Scala.
I am not sure I understand what your actual problem is, but I will try explain the best I can what I understood so far. As someone already said you have to tell your mock what calls to mock, otherwise of course it has no choice but to return null to whatever tries dereferencing it.
By mixing in MockFactory this means you are using the mock method of ScalaMock. A known limitation of ScalaMock is that it does not support mocking of val fields. This is because mocks are generated using macros as anonymous subclasses of the class to mock. But the Scala compiler does not allow overriding of val fields in subclasses, because val fields are immutable.
So there is no way you can mock service1.url, as long as url remains a val. A quick fix is converting the url into a def, so you can then mock the call to the method url and that should solve your null pointer issue. Here's that idea in action:
class Service1 #Inject() (service2: Service2) {
def url: String = service2.REDIS_URL
}
class TestService #Inject() (service1: Service1) {
def foo(): String = "this is " + service1.url
}
// ...
import org.scalamock.scalatest.MockFactory
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class ProgramTest extends AnyWordSpec with Matchers with MockFactory {
"mock Service1 url " in {
val service1Mock = mock[Service1]
val mytestService = new TestService(service1Mock)
(service1Mock.url _).expects().returns("somethingelse")
val res = mytestService.foo()
res shouldBe "this is somethingelse" // true
}
}
This works. No nulls here. If for some reason, you don't want to change the url into a def, a better alternative is to switch to mockito-scala because that can mock fields as well. You don't need ScalaMock for this.
If I understood correctly and your mock of Service1 is still failing with ScalaMock even after changing url to def for some unknown reason, then that's one more reason to switch to mockito-scala. I could not reproduce your null pointer using it. First import this:
libraryDependencies += "org.mockito" %% "mockito-scala" % "1.17.7" % Test
I tested TestService.foo as follows:
class Service1 #Inject() (service2: Service2) {
val url: String = service2.REDIS_URL
}
class TestService #Inject() (service1: Service1) {
def foo(): String = "this is " + service1.url
}
// ...
import org.mockito.MockitoSugar
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class ProgramTest extends AnyWordSpec with MockitoSugar with Matchers {
"mock Service1 url " in {
val service1Mock = mock[Service1]
val mytestService = new TestService(service1Mock)
when(service1Mock.url).thenReturn("somethingelse")
val res = mytestService.foo()
res shouldBe "this is somethingelse" // true
}
}
And it worked as expected.

How to inject correctly the play.api.Configuration inside an object in play framework2.5?

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.

ScalaTest : inject implicit variable

I am from Java background and I am trying to write UnitTests using Scala.
My class is as follows :
import com.softwaremill.sttp.{HttpURLConnectionBackend, Uri, sttp}
class MyClient(endpoint: String, principal: String) {
private implicit val serialization = org.json4s.native.Serialization
private implicit val backend = HttpURLConnectionBackend()
def getDataSet(id: String) : Option[DataSet] = {
//sttp.get(url).send <-- will use 'bakend'
}
}
here the implicit variable 'backend' is used to plugin the HTTP Client implementation.
In the UnitTest I am supposed to plugin the SttpBackendStub .
implicit val testingBackend = SttpBackendStub.synchronous
.whenRequestMatches(_.uri.path.startsWith("/dataSet"))
.thenRespond(dataSetJson)
val client = new MyClient("http://dummy", "dummy")
However when I initiate MyClient instance, it will still use HttpURLConnectionBackend instead of SttpBackendStub
Is there workaround to ingest 'testingBackend' into the MyClient during testing ?
The use of implicit here is making you think the problem is more complicated than it is. You're instantiating HttpURLConnectionBackend directly in MyClient so that's the "backend" you're going to get. If you want to use a different one, you'll have to pass it in to MyClient. You can give it a default value for production use but pass in a mock when you instantiate it in your test.
class MyClient(endpoint: String, principal: String,
implicit val backend: BackendInterface = HttpURLConnectionBackend) {
private implicit val serialization = org.json4s.native.Serialization
def getDataSet(id: String) : Option[DataSet] = {
//sttp.get(url).send <-- will use 'bakend'
}
}
And in your test:
val testingBackend = SttpBackendStub.synchronous
.whenRequestMatches(_.uri.path.startsWith("/dataSet"))
.thenRespond(dataSetJson)
val client = new MyClient("http://dummy", "dummy", testingBackend)

Guice: Could not find a suitable constructor in com.twitter.inject.Injector

I am using Guice injections and Finatra with my service.
When trying to build a small test app I am getting this error:
Could not find a suitable constructor in com.twitter.inject.Injector. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
at com.twitter.inject.Injector.class(Injector.scala:9)
My Module with the injectors looks like this
object ServiceModule extends TwitterModule {
#Provides
#Singleton
def provideS2SAuthServiceConfig(): S2SAuthServiceConfig = {
val servicePath = DynamicProperty.getInstance("myorg.module.auth.servicePath").getString
val serviceUrl = DynamicProperty.getInstance("myorg.module.auth.serviceUrl").getString
val httpClient: Service[Request, Response] = Http.client.withTls(serviceUrl).newService(serviceUrl)
S2SAuthServiceConfig(httpClient, servicePath)
}
#Provides
#Singleton
def provideS2SAuthClient(injector: Injector): S2SAuthClient = {
val s2sAuthClientClass = DynamicProperty.getInstance("myorg.mymodule.s2s.s2sAuthClient").getString
val s2sAuthClientInstance = injector.instance(Class.forName(s2sAuthClientClass))
s2sAuthClientInstance.asInstanceOf[S2SAuthClient]
}
}
It works well when I inject these objects in the constructor of my classes, but I get the error when trying to get an object instance like this:
def main (args: Array[String]): Unit = {
val injector = new Injector(Guice.createInjector(ServiceModule))
val authClient = injector.instance[S2SAuthClientImpl](classOf[S2SAuthClientImpl])
val token = authClient.getToken("MyClientID", "MySecret", "MyScope")
println(token)
}
Any ideas why Guice is not able to find the constructor for the Twitter Injector class?

Spray REST API Application Structure - Suggestions Needed

I have the following multi-module project structure built using sbt:
myProject-api
myProject-core
myProject-core is organized as below:
It contains certain actors which acts as a facade to my services. For example., I have a UserActor that sits in front of a UserService. A NotificationActor that sits in front of a NotificationService and so on.
I have another trait that exposes there actors to anybody that is interested:
trait MyProjectCoreActors {
def myAppCfg = MyProjConfig.appCfg
def userActor = myAppCfg.userActor
def notifyActor = myAppCfg.notifyActor
}
object MyProjectCoreActors {
... some initialization routing that initializes the MyProjConfig
}
My UserActor is thus defined as:
class UserActor(service: UserService) extends Actor {
...
...
}
My UserService is as follows:
class UserService(dbConfig: DbConfig) {
...
...
}
I have another class called MyProjectConfig which I initialize using the application.conf file. In this file I have the connection details to the database and so on. The MyProjectConfig is initialized as below:
trait MyProjectConfig {
def actorSystem: ActorSystem
// the actors
def userActor: ActorRef
}
object MyProjectConfig {
def apply(appConfig: Config, system: ActorSystem): MyProjectConfig = {
new MyProjectConfig {
private val dbConfig = loadDBConfig(appConfig)
override val actorSystem = system
// each actor gets its own DBConfigInstance instance
override val userActor =
actorSystem.actorOf(
Props(new UserActor(UserService(dbConfig)))
)
}
}
}
I have now the Spray routing as defined below:
trait MyProjectService extends HttpService with MyProjectCoreActors {
def pingRoute = path("ping") {
get {
userActor ! "newUser"
complete("pong!")
}
}
def pongRoute = path("pong") {
get { complete("pong!?") }
}
def route = pingRoute ~ pongRoute
}
What is now missing is a way to call the MyProjectConfig.apply(....) method and pass in the Actor System and the underlying application.conf!
This was originally a Play based application, where I had a Lifecycle plug in which had access to the underlying Application from where I got the config and the actor system. How could I now get the same here with Spray?
I have a Boot class that looks like this:
object MyBootHttpService extends App {
implicit val actorSystem = ActorSystem("myproj-actor-system")
}
How could I pass this ActorSytem to MyProjectConfig.apply(....)? and from where could I get the application.conf?
I think you can do such things (DI) in your MyBootHttpService class.
For example
object MyBootHttpService extends App {
implicit val actorSystem = ActorSystem("myproj-actor-system")
private val config = ConfigFactory.load
private val myAppConfig = MyProjectConfig(config, actorSystem)
// Initialise classes that depend on config and actorsystem....
private val service = new MyProjectService with HttpServiceActor {
override implicit val actorRefFactory = actorSystem
}
// Bind our service
IO(Http) ? Bind(listener = service, interface = "0.0.0.0", port = config.getInt("port"))
}
Typesafe config library object ConfigFactory is generally used to load config files. ConfigFactory.load with no args will try and load application.conf from the classpath.