ScalaTest : inject implicit variable - scala

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)

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.

Scala : How to use Global Config case class across application

I am new to scala, just started with my scala first application.
I have defined my config file under the resources folder, application.conf
projectname{
"application" {
"host":127.0.0.1
"port":8080
}
}
I have wrote one config parser file to parse from config file to case class
case class AppConfig (config: Config) {
val host = config.getString("projectname.application.host")
val port = config.getInt("projectname.application.port")
}
In my grpc server file, i have declared config as
val config = AppConfig(ConfigFactory.load("application.conf"))
I want to use this config variable across application, rather than loading application.conf file everytime.
I want to have one bootstrap function which will parse this config one time, making it available across application
You can do this automatically with PureConfig.
Add Pure Config to you build.sbt with:
libraryDependencies += "com.github.pureconfig" %% "pureconfig" % "0.11.0"
and reload the sbt shell and update your dependencies.
Now, let's say you have the following resource.conf file:
host: example.com
port: 80
user: admin
password: admin_password
You can define a case class called AppConfig:
case class AppConfig(
host: String,
port: Int,
user: String,
password: String
)
And create an instance of it, populated with the application config, using the loadConfig method:
import pureconfig.generic.auto._
val errorsOrConfig: Either[ConfigReaderFailures, AppConfig] = pureconfig.loadConfig[AppConfig]
This returns Either an error or your AppConfig, depending on the values in the config itself.
For example, if the value of port above will be eighty, instead of 80, you will get a detailed error, saying that the second config line (with the port: eighty) contained a string, but the only valid expected type is a number:
ConfigReaderFailures(
ConvertFailure(
reason = WrongType(
foundType = STRING,
expectedTypes = Set(NUMBER)
),
location = Some(
ConfigValueLocation(
new URL("file:~/repos/example-project/target/scala-2.12/classes/application.conf"),
lineNumber = 2
)
),
path = "port"
)
)
You can use loadConfigOrThrow if you want to get AppConfig instead of an Either.
After you load this config once at the start of your application (as close as possible to your main function), you can use dependency injection to pass it along to all the other classes, simply by passing the AppConfig in the constructor.
If you would like to wire up your Logic class (and other services) with the config case class using MacWire, as Krzysztof suggested in one of his options, you can see my answer here.
The plain example (without MacWire), looks like this:
package com.example
import com.example.config.AppConfig
object HelloWorld extends App {
val config: AppConfig = pureconfig.loadConfigOrThrow[AppConfig]
val logic = new Logic(config)
}
class Logic(config: AppConfig) {
// do something with config
}
Where the AppConfig is defined in AppConfig.scala
package com.example.config
case class AppConfig(
host: String,
port: Int,
user: String,
password: String
)
As a bonus, when you use this config variable in your IDE, you will get code completion.
Moreover, your config may be built from the supported types, such as String, Boolean, Int, etc, but also from other case classes that are build from the supported types (this is since a case class represents a value object, that contains data), as well as lists and options of supported types.
This allows you to "class up" a complicated config file and get code completion. For instance, in application.conf:
name: hotels_best_dishes
host: "https://example.com"
port: 80
hotels: [
"Club Hotel Lutraky Greece",
"Four Seasons",
"Ritz",
"Waldorf Astoria"
]
min-duration: 2 days
currency-by-location {
us = usd
england = gbp
il = nis
}
accepted-currency: [usd, gbp, nis]
application-id: 00112233-4455-6677-8899-aabbccddeeff
ssh-directory: /home/whoever/.ssh
developer: {
name: alice,
age: 20
}
Then define a config case class in your code:
import java.net.URL
import java.util.UUID
import scala.concurrent.duration.FiniteDuration
import pureconfig.generic.EnumCoproductHint
import pureconfig.generic.auto._
case class Person(name: String, age: Int)
sealed trait Currency
case object Usd extends Currency
case object Gbp extends Currency
case object Nis extends Currency
object Currency {
implicit val currencyHint: EnumCoproductHint[Currency] = new EnumCoproductHint[Currency]
}
case class Config(
name: String,
host: URL,
port: Int,
hotels: List[String],
minDuration: FiniteDuration,
currencyByLocation: Map[String, Currency],
acceptedCurrency: List[Currency],
applicationId: UUID,
sshDirectory: java.nio.file.Path,
developer: Person
)
And load it with:
val config: Config = pureconfig.loadConfigOrThrow[Config]
There are some possibilities to handle your problem:
Use runtime dependency injection framework like guice. You can use extension for scala.
Use implicits to handle it. You just need to create an object, which will hold your implicit config:
object Implicits {
implicit val config = AppConfig(ConfigFactory.load("application.conf"))
}
And then you can just add implicit config: Config to your arguments list when you need it:
def process(n: Int)(implicit val config: Config) = ??? //as method parameter
case class Processor(n: Int)(implicit val config: AppConfig) //or as class field
And use it like:
import Implicits._
process(5) //config passed implicitly here
Processor(10) //and here
A great advantage of it is you can pass config manually for tests:
process(5)(config)
The downside of this approach is, that having a lot of implicit resolution in your app, will make compilation slow, but it shouldn't be a problem if your app is not humongous.
Make config a field of your classes (it is called constructor injection).
class Foo(config: Config).
Then you can wire-up your dependencies manually, like:
val config: AppConfig = AppConfig()
val foo = Foo(config) //you need to pass config manually to constructors in your object graph
or you can use a framework which can automate it for you, like macwire:
val config = wire[AppConfig]
val foo = wire[Foo]
You can use a pattern called cake-pattern. It works fine for small-sized applications, but the bigger your app is, the clunkier this approach gets.
What is NOT a good approach is using global singleton like this:
object ConfigHolder {
val Config: AppConfig = ???
}
And then use it like:
def process(n: Int) = {
val host = ConfigHolder.Config.host // anti-pattern
}
It is bad because it makes mocking your config for tests very difficult and the whole testing process becomes clunky.
In my opinion, if your app is not very big, you should use implicits.
If you want to learn more on this topic, check this great guide.
You should define the fields as parameters of you case class.
final case class AppConfig(host: String, port: Int)
Then you overload the apply method of your companion object
object AppConfig {
def apply(config: Config): AppConfig = {
val host = config.getString("projectname.application.host")
val port = config.getInt("projectname.application.port")
AppConfig(host, port)
}
}
However the simplest way to process configuration with case classes is to use pureconfig.
I want to use this config variable across application, rather than loading application.conf file everytime.
Just put it in an object, like
object MyConfig {
lazy val config = AppConfig(ConfigFactory.load("application.conf"))
}
I want to have one bootstrap function which will parse this config one time, making it available across application
As soon as you call MyConfig.config it is loaded just once - as object is a Singleton. So no special bootstrap is needed.
The pattern you're trying to achieve is called Dependency Injection. From Martin Fowler's post on this topic
The basic idea of the Dependency Injection is to have a separate object, an assembler, that populates a field in the lister class with an appropriate implementation for the finder interface.
Register this configuration instance in a Dependency Injection tool like Guice.
class AppModule(conf: AppConfiguration) extends AbstractModule {
override def configure(): Unit = {
bind(classOf[AppConfiguration]).toInstance(conf)
}
}
....
// somewhere in the code
import com.google.inject.Inject
class FooClass #Inject() (config: AppConfiguration)

Scala dependency injection simple pattern

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

How to bind Class with Google Guice using a FQDN String

I'm using Play 2.5 with the Guice dependency injection.
Normal bind works like:
bind(classOf[SomeClass]).to(classOf[DefaultClass])
I now only need to be able to bind a class where the className has been specified in the Configuration.
I tried something like:
val className = config.getString("someClass.className")
val x: Class[_] = Class.forName(className)
bind(classOf[SomeClass]).to(classOf[x])
But then the types are wrong.
It needs to be done via Guice, since the SomeClass has an argument that needs to be injected, otherwise I would have used something like
val className = config.getString("someClass.className")
val x = Class.forName(className).newInstance().asInstanceOf[SomeClass]
bind(classOf[SomeClass]).toInstance(x)
Does anybody have an idea on how to bind it via guice?
I think you are looking for something like this...
lazy val injector = (new GuiceApplicationBuilder).injector()
def inject[T : ClassTag]: T = {
injector.instanceOf[T]
}
This is the simplest version, but doesn't handle arguments. You would create a class and then call this as inject[SomeDep].
I've not found a good way of injecting on-the-fly, the only way of really doing it via #inject at the top most invoked class. We only use inject for unit tests at present.
Thanks for the reply, but I finally got it to work using Scala's Reflection.
object Reflection {
import scala.reflect.api
import reflect.runtime.universe._
import reflect.ClassTag
def classTagToClass[T: reflect.ClassTag]: Class[T] = {
def ctag = implicitly[reflect.ClassTag[T]]
ctag.runtimeClass.asInstanceOf[Class[T]]
}
def typeToClassTag[T: TypeTag]: ClassTag[T] = {
ClassTag[T]( typeTag[T].mirror.runtimeClass( typeTag[T].tpe ) )
}
def stringToTypeTag[A](name: String): TypeTag[A] = {
val c = Class.forName(name) // obtain java.lang.Class object from a string
val mirror = runtimeMirror(c.getClassLoader) // obtain runtime mirror
val sym = mirror.staticClass(name) // obtain class symbol for `c`
val tpe = sym.selfType // obtain type object for `c`
// create a type tag which contains above type object
TypeTag(mirror, new api.TypeCreator {
def apply[U <: api.Universe with Singleton](m: api.Mirror[U]): U#Type =
if (m eq mirror) {
tpe.asInstanceOf[U#Type]
}
else {
throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.")
}
})
}
}
using the above object you can bind a class using it FQDN in the following way:
configuration.getString("config.className")
.map(className =>
bind(classOf[AbstractClass]).to(classTagToClass(typeToClassTag(stringToTypeTag[AbstractClass](className))))
).getOrElse(bind(classOf[AbstractClass]).to(classOf[AbstractClassImpl]))

Scala Play Guice using injector to manually inject a singleton class that has configuration injected into its constructor

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))