JUnit5 SpringBootTest NullPointerException when testing Service - junit5

I am following a tutorial for unit testing a Service, and I am attempting to use JUnit5 instead of JUnit4 (which is what the tutorial uses). The test works fine when I follow the tutorial exactly with JUnit4, but I get a NullPointerException with JUnit5.
The repository:
public interface CourseRepo extends CrudRepository<Course, Long> {}
The service:
#Service
public class CourseService {
private final CourseRepo courseRepo;
public CourseService(CourseRepo courseRepo) {
this.courseRepo = courseRepo;
}
public Course save(Course course) {
Course newCourse = courseRepo.save(course);
return newCourse;
}
}
The test:
package com.elovell.blackboard.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import com.elovell.blackboard.domain.Course;
import com.elovell.blackboard.repository.CourseRepo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
#ExtendWith(MockitoExtension.class)
#SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class CourseServiceTest {
#Mock
public CourseRepo courseRepo;
#InjectMocks
public CourseService courseService;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testSaveCourse() {
Course mockCourse = new Course();
mockCourse.setPk1(1L);
mockCourse.setCourseId("ENGL101");
when(courseRepo.save(any(Course.class))).thenReturn(mockCourse);
// save(null) should still return mockCourse
Course newCourse = courseService.save(null);
assertEquals("ENGL101", newCourse.getCourseId());
}
}
Here is the first few lines of the Exception:
java.lang.NullPointerException
at com.elovell.blackboard.service.CourseServiceTest.testSaveCourse(CourseServiceTest.java:44)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
The test and dependencies portion of my build file:
test {
useJUnitPlatform {
includeEngines 'junit-jupiter'
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.1.0")
testCompile 'org.mockito:mockito-junit-jupiter:2.23.4'
}

This looks like you trying to mix an integration test with unit tests.
When you use the #SpringBootTest annotation then SpringExtension for Junit5 starts an application context before running your test, and to make mocks in tests you need to use #MockBean annotation instead of #Mock & #InjectMocks. This is a different scope of tests.
As I see, you need just a unit test and you can remove the SpringBootTest annotation.
By the way, I think you got NPE because of you any(Course.class) in when(courseRepo....
Try to use isNull() or any() (without specification concrete class).

Related

What is the reason for an UninitializedPropertyAccessException in #SpringBootTest with MockK and Kotlin?

I am trying to use MockK 1.10.2 with Kotlin 1.4.10 and #SpringBootTest (Spring Boot 2.2.2.RELEASE) and I can't get it run because of a
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2020-11-05 15:00:37.878 WARN --- [ main] i.m.p.j.t.InliningClassTransformer : Failed to transform class java/lang/Object
java.lang.IllegalArgumentException: Unsupported class file major version 59
at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:195)
at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:176)
at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:162)
at net.bytebuddy.utility.OpenedClassReader.of(OpenedClassReader.java:86)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:3394)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1933)
at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:217)
at net.bytebuddy.dynamic.scaffold.inline.AbstractInliningDynamicTypeBuilder.make(AbstractInliningDynamicTypeBuilder.java:120)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3404)
at io.mockk.proxy.jvm.transformation.InliningClassTransformer.transform(InliningClassTransformer.kt:77)
at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:246)
at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563)
at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167)
at io.mockk.proxy.jvm.transformation.JvmInlineInstrumentation.retransform(JvmInlineInstrumentation.kt:28)
at io.mockk.proxy.common.transformation.RetransformInlineInstrumnetation$execute$1.invoke(RetransformInlineInstrumnetation.kt:19)
at io.mockk.proxy.common.transformation.RetransformInlineInstrumnetation$execute$1.invoke(RetransformInlineInstrumnetation.kt:6)
at io.mockk.proxy.common.transformation.ClassTransformationSpecMap.applyTransformation(ClassTransformationSpecMap.kt:41)
at io.mockk.proxy.common.transformation.RetransformInlineInstrumnetation.execute(RetransformInlineInstrumnetation.kt:16)
at io.mockk.proxy.jvm.ProxyMaker.inline(ProxyMaker.kt:88)
at io.mockk.proxy.jvm.ProxyMaker.proxy(ProxyMaker.kt:30)
kotlin.UninitializedPropertyAccessException: lateinit property taService has not been initialized
exception. (The same happens if I use tmpService. It doesn't matter if the imported service is in the same or a different package as the test class.)
As you can see from the code of my test class I already played around a lot but without any success so far.
package com.xxx.emr.tm
import com.xxx.data.emr.model.PatientAssignment
import com.xxx.data.tm.TMPService
import com.xxx.data.tm.model.MyType
import com.xxx.data.tm.model.MyTypeCode
import com.xxx.data.tm.model.UserAction
import com.xxx.emr.service.model.AuthenticatedUser
import io.mockk.every
import io.mockk.impl.annotations.InjectMockKs
import io.mockk.impl.annotations.SpyK
import io.mockk.junit5.MockKExtension
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.verify
import org.junit.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
#ExtendWith(SpringExtension::class, MockKExtension::class)
#SpringBootTest // (classes = [com.xxx.emr.service.MyServiceSpringBoot::class])
// #RunWith(SpringJUnit4ClassRunner::class)
// #ActiveProfiles("test")
// #EnableAutoConfiguration
// #AutoConfigureMockMvc
class MyTest {
// (
// val tmpService: TMPService,
// val taService: TaService
// )
// #InjectMockKs
// #Autowired
// #SpyK
#MockK
private lateinit var tmpService: TMPService
#InjectMockKs
#Autowired
private lateinit var taService: TaService
#Test
fun assignAToB() {
MockKAnnotations.init(this, relaxUnitFun = true)
val vId = "xxx"
val authUser: AuthenticatedUser = AuthenticatedUser.mockedUser()
val userAction = UserAction(
userId = "user_id",
actionCode = ActionType.GET_IT,
sessionId = "sessionId"
)
val xxx = MyType(
MyTypeCode.ABC.value,
MyTypeCode.ABC.name,
)
val actionResult = UserActionResult(hashMapOf("success" to true))
// every { tmpService.getXxx(any()) } returns xxx
verify( exactly = 1 ) {
tmpService.doSomething(any(), any(), any())
}
verify( exactly = 1 ) {
taService.dowhatYouWant(„vId", authUser, userAction)
}
}
Autowiring does not work...so what is my fault? How can I make the test run at all?
I think you should initialize your mocks first, by adding this in your test file for example:
#org.springframework.test.context.event.annotation.BeforeTestMethod
fun initMocks() {
org.mockito.MockitoAnnotations.initMocks(this)
}
I created a post on another thread about how to setup unit testing, comparable with your requirements:
https://stackoverflow.com/a/64669499/7919904

How to Implement ITestListener in Cucumber+Selenium+Java

I've created a project where am trying to run Cucumber+Selenium+Java tests using TestNG plugin.
My Runner class is :
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
#CucumberOptions(features = "src/test/resources", monochrome = true, glue = "com.ipkgs.stepdefinitions", plugin = {
"html:target/cucumber-html-report", "json:target/cucumber-json-report.json" }, dryRun = false)
public class CucumberTests extends AbstractTestNGCucumberTests {
#DataProvider(parallel = true)
public Object[][] scenarios() {
return super.scenarios();
}
}
I am creating a listener class where I want to get the result details as well as the scenario details like at which scenario failed at which step.
I am extending ItestListener but it is only giving me the pass/fail result without scenario details.
Is there any way to achieve this?
You shouldn't use TestNG listeners for that. While Cucumber "knows" about TestNG, TestNG in turn does not know anything about Cucumber. You need to use hooks mechanism for watching your scenario execution status.
Example from documentation:
#After
public void doSomethingAfter(Scenario scenario){
// Do something after after scenario
}
#AfterStep
public void doSomethingAfterStep(Scenario scenario){
// Do something after after step
}

How can I configure a JAX-RS application in Kotlin with resources having other dependencies injected

I use JAX-RS to build a REST API. To bootstrap all resources I have a overridden an "Application":
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationScoped
#ApplicationPath("/")
open class ApiConfig : Application() {
override fun getSingletons(): MutableSet<Any> {
println("----- init jaxrs -----")
return mutableSetOf(EchoResource())
}
}
As you can see I register the EchoResource() with brackets. It does not work when I use EchoResource::class.
My Problem is, that I want to inject some service into EchoResource:
import dev.elysion.mail.smtp.MailService
import javax.enterprise.context.RequestScoped
import javax.inject.Inject
import javax.ws.rs.GET
import javax.ws.rs.Path
#Path("/echo")
#RequestScoped
class EchoResource #Inject constructor(private val mailService: MailService) {
#GET
fun getHello(): String {
return "Hello World"
}
}
When I add the constructor I get an error in API Config saying that I do not pass a parameter for MailService.
In Java I would register the resource with EchoResource.class which does not care about any parameters.
How can I achieve the same with Kotlin?
It works if getClasses() is used instead of getSingletons():
import javax.enterprise.context.ApplicationScoped
import javax.ws.rs.ApplicationPath
import javax.ws.rs.core.Application
#ApplicationScoped
#ApplicationPath("/")
open class ApiConfig : Application() {
override fun getClasses(): MutableSet<Class<*>> {
return mutableSetOf(EchoResource::class.java)
}
}
Furthermore, all resources and all of their methods need to be open (not final) in order to be proxyable for CDI.

#Autowired member not initialized in a Junit 5 test written in Kotlin

I can't get an #Autowired member to be initialized in a JUnit 5 test. Here is the test:
import org.amshove.kluent.`should be equal to`
import org.junit.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
#SpringBootTest
class SnackQueryResolverTest {
#TestConfiguration
class SnackQueryResolverTestConfig {
#Bean
fun snackQueryResolverFactory() = SnackQueryResolver()
}
#Autowired
private lateinit var snackQueryResolver: SnackQueryResolver
#Test
fun `snacks`() {
val snacks = snackQueryResolver.snacks()
snacks.size `should be equal to` 5
}
}
When the test is run I receive the error:
kotlin.UninitializedPropertyAccessException: lateinit property snackQueryResolver has not been initialized
If I eliminate the #Autowired and instantiate the bean during construction, the test runs fine:
#SpringBootTest
class SnackQueryResolverTest {
private val snackQueryResolver: SnackQueryResolver = SnackQueryResolver()
#Test
fun `snacks`() {
val snacks = snackQueryResolver.snacks()
snacks.size `should be equal to` 5
}
}
What am I missing?
Turns out a small error was causing the issue - careless with IDE code completion. For #Test, I was importing:
import org.junit.Test
instead, this needs to be used for JUnit 5 tests:
import org.junit.jupiter.api.Test

Junit 5 Assertions.fail() can not infer type in Kotlin

When I attempt to use JUnit 5 Assertions.fail in my Kotlin tests I get a compilation failure because parameter V can not be inferred:
import org.junit.jupiter.api.Assertions.fail
internal class MyTests {
#Test
fun simpleTest() {
fail("Does not compile")
}
}
Of course a simple solution to this problem is:
import org.junit.jupiter.api.Assertions.fail
internal class MyTests {
#Test
fun simpleTest() {
val result: Any = fail("Compiles")
}
}
However I do not wish to have to create an unused value in my code. Is there a way to define the type without having to declare a val? Also why does this happen in Kotlin? Java has no such problem with generics:
import org.junit.jupiter.api.Assertions.fail;
class MyJavaTests {
#Test
public void simpleTest() {
fail("Compiles);
}
}
EDIT: I discovered immediately after posting the question that the solution is to parameterize the call:
import org.junit.jupiter.api.Assertions.fail
internal class MyTests {
#Test
fun simpleTest() {
fail<Any>("Does not compile")
}
}
However still willing to accept an answer that can explain why I need to do this in kotlin.
Please see this issue: https://github.com/junit-team/junit5/issues/1209
It seems that this is already fixed in the junit-jupiter-api Assertions.kt file as a top-level function in org.junit.jupiter.api package.
Import the org.junit.jupiter.api.fail and not the org.junit.jupiter.api.Assertions.fail