Spring Boot + Kotlin annotation error(s) - spring-boot

I have a Spring Boot 2.0.0.M2 (with WebFlux) application written in Kotlin.
I'm used to define/declare "annotations" for test cases in order to avoid some boilerplate configuration; something like:
import java.lang.annotation.ElementType
import java.lang.annotation.Inherited
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
...
#Inherited
#Target(ElementType.TYPE)
#AutoConfigureWebTestClient // TODO: FTW this really does?!
#Retention(RetentionPolicy.RUNTIME)
//#kotlin.annotation.Target(AnnotationTarget.TYPE)
//#kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
#ActiveProfiles(profiles = arrayOf("default", "test"))
#ContextConfiguration(classes = arrayOf(Application::class))
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
annotation class SpringWebFluxTest
...then in my tests I use it like:
#SpringWebFluxTest
#RunWith(SpringRunner::class)
class PersonWorkflowTest {
private lateinit var client: WebTestClient
...
The issue is that I can't achieve the same using the annotations provided by Kotlin:
#kotlin.annotation.Target(AnnotationTarget.TYPE)
#kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
I'm getting a this annotation is not applicable to target 'class' inside PersonWorkflowTest. If I use the Java ones everything is fine, but on the other hand I get these warnings ― which what I'm really trying to get rid of :)
...
:compileTestKotlin
w: /home/gorre/Workshop/kotlin-spring-boot-reactive/src/test/kotlin/io/shido/annotations/SpringWebFluxTest.kt: (18, 1): This annotation is deprecated in Kotlin. Use '#kotlin.annotation.Target' instead
w: /home/gorre/Workshop/kotlin-spring-boot-reactive/src/test/kotlin/io/shido/annotations/SpringWebFluxTest.kt: (20, 1): This annotation is deprecated in Kotlin. Use '#kotlin.annotation.Retention' instead

kotlin has it own #kotlin.annotation.Retention and #kotlin.annotation.Target annotations. take a while to look the kotlin annotation documentation please.
Edit
I have test it in springframework that no problem. Note that have a distinction in #Target between java and kotlin.
the kotlin kotlin.annotation.#Target(AnnotationTarget.CLASS) is translated to:
#java.lang.annotation.Target(ElementType.TYPE);
and the kotlin kotlin.annotation.#Target(AnnotationTarget.TYPE) is translated to:
#java.lang.annotation.Target;
which means AnnotationTarget.TYPE don't supported for java. it just used in kotlin. so your own annotation should be like this:
//spring-test annotations
#ActiveProfiles(profiles = arrayOf("default", "test"))
#ContextConfiguration(classes = arrayOf(Application::class))
//spring-boot annotations
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureWebTestClient
//kotlin annotations
#Target(AnnotationTarget.CLASS)
#Retention(AnnotationRetention.RUNTIME)// can remove it default is RUNTIME
annotation class SpringWebFluxTest;

Related

JUnit tests: How to "inherit" Spring application context without extending base test class?

My tests of a Spring application are written using JUnit 4. To ease writing of JUnit tests and reduce repetitive code, I use a base class (AbstractDaoTest) that defines a basic Spring application context using annotation #ContextConfiguration(classes = …):
#ContextConfiguration(classes = TestDbConf.class)
public abstract class AbstractDaoTest {
...
}
Subclasses extend the application context by defining extra annotation ContextConfiguration(…) with additional classes:
#ContextConfiguration(classes = SomeExtraBean.class)
public class SomeTestClass extends AbstractDaoTest {
...
}
JUnit 5 came with the modern approach "composition is better than inheritance".
I like the idea so I tried to avoid inheritance by defining a composed annotation:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = TestDbConf.class)
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
public #interface DaoTest { }
… and using the annotation on a test class:
#DaoTest
#ContextConfiguration(classes = ExtraBeanClass.class)
public class SomeTest {
...
}
But when I execute it, it seems that the #ContextConfiguration annotation causes the application context to be shadowed/overwritten, i.e. the context defined by annotation #DaoTest is not used.
What is the correct approach? Is it possible to achieve the goal using JUnit 5 extension?
I searched the internet and read several articles about JUnit 5 extensions and JUnit 5 in general but I couldn't find the answer in any of them.

#SpyBean not working with Pact and JUnit 5

I'm trying to use the #SpyBean to mock a method of a #Component and doesn't work. #MockBean works (followed the example). I've tried, read and researched many ways but couldn't make it work.
Here's the example:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment. DEFINED_PORT)
#ExtendWith(SpringExtension::class)
#Provider("MyMicroService")
#PactFolder("../../../pacts")
internal class ClientContracts {
#SpyBean
private lateinit var myService: MyService
#TestTemplate
#ExtendWith(PactVerificationInvocationContextProvider::class)
fun pactVerificationTestTemplate(context: PactVerificationContext) {
context.verifyInteraction()
}
#State("default", "NO_DATA")
fun toDefaultState() {
reset(processService)
}
}
(I super simplified the test function so it's easier to read, I'd be actually doing doReturn(...).when(...).blah())
I'm always getting the "not a mock" error, because the object is always the bean wrapped by Spring CGLIB:
org.mockito.exceptions.misusing.NotAMockException: Argument should be a mock, but is: class com.blah.MyServiceImpl$$EnhancerBySpringCGLIB$$9712a2a5
at com.nhaarman.mockitokotlin2.MockitoKt.reset(Mockito.kt:36)
...
I've tried:
with #SpringJUnitConfig
with a separate #TestConfiguration, but got resolved to same above bean
Using Mockito.initAnnotations(this) in a #BeforeEach
and more, I've tried with so many combinations that I can't remember...
Is there something that I'm missing? Or an option that I don't know?
Above issue is not related to the pact or pact JVM library
The issue is not about spring
Spring - I use spring with mockito and it works, the simple example is:
import com.nhaarman.mockito_kotlin.doReturn
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.SpyBean
import org.springframework.test.context.junit.jupiter.SpringExtension
#ExtendWith(value = [SpringExtension::class])
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = [Application::class]
)
internal class processorIntegrationTest : IntegrationTest() {
#SpyBean
// #MockBean
private lateinit var processor: Processor;
#Test
internal fun abcd() {
doReturn("something").`when`(processor).get()
val get = processor.get()
assertThat(get).isEqualTo("something")
}
}
Mockito - mockito_kotlin or mockito extension works with SpyBean
Issue is about mockito + CGLIB
CGLIB - from your logs feels like class com.blah.MyServiceImpl$$EnhancerBySpringCGLIB$$9712a2a5 there is a wrapper on top of your service implementation which is SpyBean.
Which means CGLIB wrapper is not and the error is for that.
Try removing CGLIB wrapper and it will work

Cucumber 4 with Spring Boot and JPA

I am using cucumber + cucumber-spring 4.1 for testing a SpringBoot 2.1 application. This has scenarios that require the database to be rolled back between each scenario, but for the life of me cannot get it to work.
I have tried:
A base class with several annotations
`
#Transactional
#RunWith(SpringRunner.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public abstract class SpringBootBaseIntegrationTest { .. } `
Stepdef classes like:
`
#ContextConfiguration(classes = {Application.class})
#Ignore
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
#Transactional
public class DatabaseSteps extends SpringBootBaseIntegrationTest implements En { ... }
`
Features nd scenarios are annotated with #txn and the 'cucumber.api.spring' glue is added.
My application has :
`
#EnableJms
#EnableJpaRepositories
#EnableTransactionManagement
#SpringBootApplication
#PropertySource(ignoreResourceNotFound = false, value = "classpath:application.properties")
class Application implements CommandLineRunner { ... }
`
and I use standard JPA repositories.
My application.properties is:
`
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
spring.datasource.initialization-mode=always
spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.generate_statistics=false
spring.jpa.properties.hibernate.show_sql=false
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
#spring.h2.console.enabled=true
`
but whatever I do, in any combination, the data still remains between tests.
Can anyone shed light or managed a working set up?
Cheers
I figured it out in the end. My Cucumber tests were entering the app via a number of different methods (JMS, Rest), and consequently the test did not own the transaction scope for data injected via these services.
Consequently, I am truncating the db tables in a before scenario hook for each test which seems to work fine

#ComponentScan in application class breaks #WebMvcTest and #SpringBootTest

I am creating tests with #WebMvcTest annotation and found that if I have a #ComponentScan annotation in the application class it will break the expected behavior of the tests.
According to the WebMvcTest javadoc:
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans)."
The problem is that with #ComponentScan it is instantiating beans annotated with #Service. If instead of #ComponentScan I specify the scan base packages in the #SpringBootApplication annotation everything works as expected.
Another problem happens when I specify the controller class in the #WebMvcTest annotation. When there is a #ComponentScan annotation in the application class it will load all controllers instead of loading only the specified one.
Is this a bug in Spring Boot?
I want to use #ComponentScan because of the excludeFilters attribute which is not available in the #SpringBootApplication annotation.
A workaround I have found is to create a separate class with #Configuration annotation and move the #ComponentScan there.
Found the reason for this odd behavior.
This is the declaration of the #SpringBootApplication annotation:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(excludeFilters = {
#Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public #interface SpringBootApplication {
As you can see the #ComponentScan annotation inside the #SpringBootApplication has the excludedFilters attribute specified.
When I added the #ComponentScan annotation directly in my application class I did not specify the default excludedFilters and that is why the behavior was different.

How to exclude classes that are added using Spring's AutoConfiguration when using #WebMvcTest?

I want to test my Controller using #WebMvcTest and mock the dependencies, but Spring Boot's AutoConfiguration loads my Couchbase (Spring Data) configuration automatically. Couchbase is not available on some platforms which run the test, so the Test class will throw an Exception. How can I exclude some classes from the AutoConfiguration mechanism?
I tried the excludeFilters option of #WebMvcTest and #ActiveProfile to load another Application Context, but that didn't work.
Test configuration:
#RunWith(SpringRunner.class)
#WebMvcTest(value = ExchangeJobImportController.class, excludeFilters = #ComponentScan.Filter(classes = CouchbaseConfiguration.class))
#ActiveProfiles("test")
public class ExchangeJobImportControllerTest {
...
}
Couchbase Configuration:
#Configuration
#EnableCouchbaseAuditing
#EnableConfigurationProperties({CouchbaseProperties.class})
#EnableCouchbaseRepositories(basePackages = "com....")
public class CouchbaseConfiguration extends AbstractCouchbaseConfiguration {
...
}
After some more struggling I found the solution: exclude the #SpringBootApplication annotated class in the #WebMvcTest annotation:
#RunWith(SpringRunner.class)
#WebMvcTest(value = ExchangeJobImportController.class, excludeAutoConfiguration = Application.class, secure = false)
#ActiveProfiles("localhost")
public class ExchangeJobImportControllerTest {
...
}
For the record, I am using Spring Boot 1.5.1.RELEASE
I know it's been some time but I suppose the reason why the #ComponentScan.Filter solution doesn't work is that it looks for an annotation type for the classes field by default.
So, setting the FilterType as ASSIGNABLE_TYPE should work in your case:
#ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = CouchbaseConfiguration.class
)

Resources