I have the following code...
class MyClassImpl extends MyClass {
#Autowired private val myService: MyService = null
...
override def deleteItem(id: String): Boolean = {
return myService.delete(id)
}
}
So I want to make a simple unit test to make sure it is calling the service.
#RunWith(classOf[MockitoJUnitRunner])
class MyClassImplTest(){
#Spy
#InjectMocks
var myClass: MyClassImpl = _
#Mock
var myService: myService = _
#Test
def testRun(){
doReturn(true).when(myService).delete(anyString())
myClass.deleteItem("");
verify(myService, times(1)).delete(anyString())
}
}
But when I run the test inside MyClassImplTest myService has a mocked object. However, inside MyClassImpl it is still null, so I get an NPE.
Can someone see what I am missing? Is this an idiosyncrasy of Scala?
Related
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.
I want to use DI google guice which works perfectly fine in Java but doesnt work in case of scala. Here is my code:
Module:
class ConfigModule extends AbstractModule{
override def configure(): Unit = {
}
#Provides
#Named("config")
def getConfig(): Config = {
new Config
}
}
Config
class Config {
val config1: String = "Sample"
}
Service
class Service(#Named("config") config:Config) {
def read(): Unit = {
println("Reading")
}
}
Main Class
object SampleJob {
def main(args: Array[String]): Unit = {
val injector = Guice.createInjector(new ConfigModule)
val service = injector.getInstance(classOf[Service])
service.read()
}
}
Error:
1) Could not find a suitable constructor in com.job.Service. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
at com.job.Service.class(Service.scala:7)
while locating com.job.Service
Where am I mistaken?
UPDATE:
class Service(#Inject #Named("config") config:Config) {
def read(): Unit = {
println("Reading")
}
}
This also returns the same error
Could not find a suitable constructor in com.job.Service. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
at com.job.Service.class(Service.scala:8)
while locating com.job.Service
The error tells you what happened:
Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private
So you need to add #Inject annotation... on your constructor like in tutorial.
Instead of
class Service(#Inject #Named("config") config:Config) {
def read(): Unit = {
println("Reading")
}
}
it should be
class Service #Inject() (#Named("config") config:Config) {
def read(): Unit = {
println("Reading")
}
}
I have an integration test where I start an embedded MongoDB as a companion object. I want to reuse this piece of code and I am not sure if inheritance is the way to go (if possible).
It is a Spring Boot application:
This is my test:
#RunWith(SpringRunner::class)
#SpringBootTest
class RequestRepositoryTest {
#Autowired lateinit var requestRepository: RequestRepository
companion object {
private val starter = MongodStarter.getDefaultInstance()
private var _mongod: MongodProcess? = null
private var _mongo: MongoClient? = null
#BeforeClass
#JvmStatic fun beforeTest(){
val port = 27017
val _mongodExe = starter.prepare(MongodConfigBuilder()
.version(Version.Main.DEVELOPMENT)
.net(Net("localhost", port, Network.localhostIsIPv6()))
.build())
_mongod = _mongodExe.start()
_mongo = MongoClient("localhost", port)
}
#AfterClass
#JvmStatic fun afterTest(){
_mongod?.stop()
}
}
#Test
fun store() {
val id = requestRepository.store(Request(requestId = "123"))
assertNotNull(id)
}
}
My repository class:
#Repository
class RequestRepository #Autowired constructor(val datastore: Datastore)
{
fun store(request : Request) : String =
datastore.save(request).id.toString()
}
So my question is which is the 'correct' way to go about this in Kotlin.
Update edit: As an external object the test now looks a lot cleaner and the JUnit external resource is completely reusable across test classes:
Thanks #Lovis
#RunWith(SpringRunner::class)
#SpringBootTest
class RequestRepositoryTest {
companion object {
#ClassRule
#JvmField
val mongoServer = MongoServer
}
#Autowired lateinit var requestRepository: RequestRepository
#Test
fun store() {
val id = requestRepository.store(Request(requestId = "123"))
assertNotNull( id )
assertTrue { ObjectId.isValid(id) }
}
}
You should be able to achieve what you want using jUnit's #ClassRule and ExternalResource. No Kotlin magic needed :-)
Define an object in a separate File:
object MongoServer : ExternalResource() {
#Throws(Throwable::class)
override fun before() {
// before class
}
override fun after() {
// after class
}
}
Then use it within each test:
companion object {
#ClassRule
#JvmField
val mongoServer = MongoServer
}
The ClassRule annotation does the trick here, the companion object is necessary to make it static and the #JvmField annotation is necessary to make the field public. Those are restrictions by jUnit's rule system.
I am writing Play 2.5 application using Scala. I have following piece of code:
#ImplementedBy(classOf[BarRepositoryImpl])
trait BarRepository {
def bar = //some actions
}
class BarRepositoryImpl extends BarRepository
case class Foo( /*some fields*/) {
#Inject private var barRepository: BarRepository = null
def foo1 = {
val a = barRepository.bar //here barRepository is always null
// some actions with 'a' and returning some result which depends on 'a'
}
}
I also have a controller where I inject BarRepository as well, but through constructor and there everything works well while in the class Foo on the line val a = barRepository.bar I get a NullPointerException. Could someone help to figure out what's the problem? Is it forbidden to use injection in case class?
If you don't want to pollute your case class signature with Guice injected annotation and fields then simply add an implicit dependency on the method that needs it instead:
case class Foo( /*some fields*/) {
def bar1(someField: Int)(implicit barRepository: BarRepository) = {
// some code that interacts with barRepository
}
}
The calling class will have to have the BarRepository as an implicitly injected parameter. E.g. a Play controller like:
#Singleton
class HomeController #Inject()(cc: ControllerComponents)
(implicit barRepository: BarRepository)
extends AbstractController(cc) {
def index() = Action { implicit request =>
val foo = Foo("field")
val bar = foo.bar1
// ...
}
}
I would have assumed that you inject the object in your class signature?
case class Foo #Inject()(barRepository:BarRepository, /* your fields */){
/** some stuff **/
}
In JUnit you can use #ClassRule to annotate an static field.
How can I do this in Kotlin?
I tried:
object companion {
#ClassRule #JvmStatic
val managedMongoDb = ...
}
and
object companion {
#ClassRule #JvmField
val managedMongoDb = ...
}
but none of the last works because rule isn't executed.
I double checked that exactly same rule works fine without static context:
#Rule #JvmField
val managedMongoDb = ...
You are not using companion objects correctly. You are declaring an object (single instance of a class) called companion instead of creating a companion object inside of a class. And therefore the static fields are not created correctly.
class TestClass {
companion object { ... }
}
Is very different than:
class TestClass {
object companion { ... } // this is an object declaration, not a companion object
}
Although both are valid code.
Here is a correct working example of using #ClassRule, tested in Kotlin 1.0.0:
class TestWithRule {
companion object {
#ClassRule #JvmField
val resource: ExternalResource = object : ExternalResource() {
override fun before() {
println("ClassRule Before")
}
override fun after() {
println("ClassRule After")
}
}
}
#Test fun testSomething() {
println("Testing...")
}
}
This outputs:
ClassRule Before
Testing...
ClassRule After