Run unit tests with spring boot, "no runnable methods" error - spring

I'm running unit tests with Spring boot, but I get a weird no runnable error. The tests pass by the way, but after all test have ended successfully, I get this weird error out of nowhere:
java.lang.Exception: No runnable methods
at org.junit.runners.BlockJUnit4ClassRunner.validateInstanceMethods(BlockJUnit4ClassRunner.java:191)
at org.junit.runners.BlockJUnit4ClassRunner.collectInitializationErrors(BlockJUnit4ClassRunner.java:128)
at org.junit.runners.ParentRunner.validate(ParentRunner.java:416)
at org.junit.runners.ParentRunner.<init>(ParentRunner.java:84)
at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:65)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.<init>(SpringJUnit4ClassRunner.java:137)
at org.springframework.test.context.junit4.SpringRunner.<init>(SpringRunner.java:49)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
How do I fix this? Why is Spring boot looking for a runnable in my tests?
Here is an example of my code
package ca.bell.uc.hello.world
import org.junit.Assert
import org.junit.jupiter.api.Test
import org.junit.runner.RunWith
import org.springframework.test.context.junit4.SpringRunner
#RunWith(SpringRunner::class)
internal class example {
#Test
fun f() {
Assert.assertTrue(true)
}
}
And here is a screenshot of the error:
Thanks
P.S. This is Kotlin

You are trying to use JUnit 5 #Test annotation because of import:
import org.junit.jupiter.api.Test
In the console log we can see that JUnit 4 is used.
If you want to work with JUnit 4 you should use the import:
import org.junit.Test

Indeed it's the mix of JUnit4 and JUnit5. I had the same thing with tests with #Ignore or #Disabled that would not want to get ignored based on which #Test it was.
You can avoid this issue with spring by updating your spring dependencies and remove the Junit4 dependencies from your build.gradle.kts so that you won't mix them (tested with Gradle v6, check the groovy syntax if you use build.gradle).
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(module = "junit")
exclude(module = "junit-vintage-engine")
exclude(module = "mockito-core")
}
testImplementation("org.junit.jupiter:junit-jupiter:5.4.2")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
Once you have that you don't need to use #RunWith(SpringRunner::class) you can see the difference in your import, they should all contains "jupiter" and your class could look like this:
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
internal class ExampleTest {
#Test
fun test() {
Assertions.assertTrue(true)
}
}

Related

Operation timed out error when running spring-boot:run

I am trying to run through the Spring Quickstart Guide.
I have the DemoApplication.kt file updated to
package com.example.demo
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
#SpringBootApplication
#RestController
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
#GetMapping("/hello")
fun hello(#RequestParam(value = "name", defaultValue = "World") name: String?): String? {
return String.format("Hello %s!", name)
}
When I run ./mvnw spring-boot:run it times out with Exception in thread "main" java.net.ConnectException: Operation timed out.
Stack Trace
at java.base/sun.nio.ch.Net.connect(Net.java:579)
at java.base/sun.nio.ch.Net.connect(Net.java:568)
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:588)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
at java.base/java.net.Socket.connect(Socket.java:639)
at java.base/sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:304)
at java.base/sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:174)
at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:183)
at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:532)
at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:637)
at java.base/sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:266)
at java.base/sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:380)
at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:193)
at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1245)
at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1131)
at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:179)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1668)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1592)
at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:224)
at org.apache.maven.wrapper.DefaultDownloader.downloadInternal(DefaultDownloader.java:106)
at org.apache.maven.wrapper.DefaultDownloader.download(DefaultDownloader.java:93)
at org.apache.maven.wrapper.Installer.createDist(Installer.java:86)
at org.apache.maven.wrapper.WrapperExecutor.execute(WrapperExecutor.java:155)
at org.apache.maven.wrapper.MavenWrapperMain.main(MavenWrapperMain.java:72)
Any idea what I need to do differently? Thanks.
The Spring App you wrote wouldn't normally make an outbound connection that could fail - that was what was confusing about the original question.
Now that you have provided the stack trace it is clearer what is going on.
What's happening is that you are compiling and running the app using Maven and Maven needs some more artefacts for your application so it is automatically downloading them for you org.apache.maven.wrapper.DefaultDownloader.download(....
So if you have to use a proxy to reach the Internet, then you need to give these details to Maven. Try this guide: https://maven.apache.org/guides/mini/guide-proxies.html

Spring Boot Test fails to load application context with JUnit 5 for a custom integration test source set

I'd like to start with some integration tests for my spring boot backend service. Therefore I added a gradle plugin that helps me adding the integrationTest source set.
// build.gradle.kts
plugins {
id("org.springframework.boot") version "2.4.2"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
id("org.jlleitschuh.gradle.ktlint") version "9.4.1"
id("org.unbroken-dome.test-sets") version "3.0.1" // <<
kotlin("jvm") version "1.4.21"
kotlin("plugin.spring") version "1.4.21"
}
...
dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
...
testSets {
create("integrationTest")
}
...
Then I started building my first test spec:
// TeamApiIntegrationTest.kt
package com.myapp.integrationtest.api
import com.myapp.userservice.repository.TeamRepository
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit.jupiter.SpringExtension
#SpringBootTest
#ContextConfiguration(classes = [TeamRepository::class])
#ExtendWith(SpringExtension::class)
class TeamApiIntegrationTest #Autowired constructor(
private val teamRepository: TeamRepository
) {
#Test
fun `should fetch a list of two teams where the current auth user is part of`() {
assertThat(teamRepository).isNotNull
}
}
The exception I receive is this one:
Failed to resolve parameter [com.myapp.userservice.repository.TeamRepository teamRepository] in constructor [public com.myapp.integrationtest.api.TeamApiIntegrationTest(com.myapp.userservice.repository.TeamRepository)]: No qualifying bean of type 'com.myapp.userservice.repository.TeamRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [com.myapp.userservice.repository.TeamRepository teamRepository] in constructor [public com.myapp.integrationtest.api.TeamApiIntegrationTest(com.myapp.userservice.repository.TeamRepository)]: No qualifying bean of type 'com.myapp.userservice.repository.TeamRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
[...]
I understand the problem but don't know why it occurs since I am explictly "adding" TeamRepository to the ContextConfiguration.
EDIT:
// TeamRepository.kt
import org.springframework.data.repository.PagingAndSortingRepository
import org.springframework.stereotype.Repository
#Repository()
interface TeamRepository : PagingAndSortingRepository<Team, String> {
fun findByName(name: String): List<Team?>
fun findByUsers(user: User): List<Team?>
}
You need to remove the #SpringBootTest.
#SpringBootTest will look for actual configuration with Junit 5.
I have faced the same issue earlier in java
You can find the details here

Spring Cucumber ActiveProfiles annotation not working with CucumberContext

I'm working on a project where we have a component which consists:
core
connector to external system 1
connector to external system 2
The connectors are mutually exlusive (if connector1 is active, connector2 is always inactive and vice versa). The core and a single connector are autowired on startup of the ApplicationContext. Which connector is instantiated is based on a value in the spring application properties.
We're writing integration tests using spring-cucumber (v6.2.2). For each external system, I want to run a set of cucumber tests. I've created 2 testsets using annotations on the cucumber scenario's which allows me to seperate the tests for connector1 and connector2.
The problem I'm having is that I need both testsets to run with a different spring profile, so I can use a different configuration. I can't find how to do this.
Current implementation (with a single profile):
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>6.2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>6.2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>6.2.2</version>
<scope>test</scope>
</dependency>
CucumberConnector1IT.java
package omitted.for.company.rules;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(
features = { "classpath:feature/" },
glue = { "omitted.for.company.rules.cucumber.step" },
plugin = { "pretty", "json:target/cucumber-report/cucumber.json",
"html:target/cucumber-report/cucumber.html" },
tags = "#Connector1 and not #ignore" // tags make sure only applicable tests are run
)
public class CucumberConnector1IT {
}
CucumberConnector2IT.java
package omitted.for.company.rules;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(
features = { "classpath:feature/" },
glue = { "omitted.for.company.rules.cucumber.step" },
plugin = { "pretty", "json:target/cucumber-report/cucumber.json",
"html:target/cucumber-report/cucumber.html" },
tags = "#Connector2 and not #ignore" // tags make sure only applicable tests are run
)
public class CucumberConnector2IT {
}
StepInitializer.java
package omitted.for.company.rules.steps;
import io.cucumber.java.Before;
import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
#SpringBootTest
#ActiveProfiles("test") // I can only get this to work if the #ActiveProfiles and #CucumberContextConfiguration annotations are on the same class
#CucumberContextConfiguration
public class StepInitializer {
// mock some beans to use in cucumber test
#MockBean
private EventProducer eventProducer;
#MockBean
private RestInterface restInterface;
#Before
public void setup() {
}
}
So far everything works. But what I need now is to put the #ActiveProfiles() annotation on a different class than the #CucumberContextConfiguration. If I can do this then I can annotate the correct step classes with the required profiles.
Problem is that I don't understand the spring annotations well enough to know which ones I can move and which ones I cannot. I found this example of exactly what I'm trying to do (spring-cucumber-profiles repo, notice the location of the #ActiveProfiles annotation here) . Unfortunately, it uses an older version of cucumber-spring (v5.6.0). That version doesn't yet have the #CucumberContextConfiguration annotation and does some magic with the spring context according to the documentation (Release notes of cucumber-spring). I tried to checkout the example repo and upgrade it to v6.2.2 but couldn't get it working with the new version.
If anyone spots what I'm doing wrong in my own examples, or has the possibility to get the example repo working with version 6.2.2 of cucumber-spring that would be much appreciated.
Thanks in advance! :)
I've solved the issue by separating the packages a bit, and creating separate StepInitializer classes for both testsets.
Current setup:
Test runner:
package omitted.for.company.rules;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(
features = { "classpath:feature/" },
extraGlue = { "omitted.for.company.rules.cucumber.step.common" }, // used extraGlue instead of glue
plugin = { "pretty", "json:target/cucumber-report/cucumber.json",
"html:target/cucumber-report/cucumber.html" },
tags = "#Connector1 and not #ignore" // tags make sure only applicable tests are run
)
public class CucumberConnector1IT {
}
Context Configuration:
package omitted.for.company.rules.steps;
import io.cucumber.java.Before;
import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
#SpringBootTest
#ActiveProfiles({ "test", "test-connector1" })
#CucumberContextConfiguration
public class Connector1StepInitializer {
// mock some beans to use in cucumber test
#MockBean
private EventProducer eventProducer;
#MockBean
private RestInterface restInterface;
#Autowired
private ApplicationContext applicationContext;
#Autowired
private Environment environment;
#Before
public void setup() {
assertThat(applicationContext).isNotNull();
assertThat(environment.getActiveProfiles()).containsOnly("test","test-connector1");
}
}
Both connectors/test runners have their own runner class and their own ContextConfiguration class.
It's very important that the classes containing the #CucumberContextConfiguration annotation are not in the shared glue package (as provided in the extraGlue property in the #CucumberOptions annotation).
Package structure looks like this:
├───common
│ └───step // Contains shared steps. This path should be in the 'extraGlue' field of the runner classes
├───connector1
│ │ CucumberConnector1IT.java // Runner 1
│ └───step
│ Connector1Steps.java // Specific steps
│ Connector1StepInitializer.java // has #ActiveProfiles and #CucumberContextConfiguration annotations, use to mock beans
└───connector2
│ CucumberConnector1IT.java // Runner 2
└───step
Connector2Steps.java // Specific steps
Connector2StepInitializer.java // has #ActiveProfiles and #CucumberContextConfiguration annotations, use to mock beans
This way I can still use different spring profiles :).

Gradle Error in contextLoads under test

i am trying to build a web using Spring and Angular Js tegether w/ Gradle. my problem is When i run my project or build gradle this error comes out.
"contextLoads FAILED"
"What went Wrong:"
.> There were failing tests.: /build/reports/tests/test/index.html
Caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException
ContextLoad Leads me to this Class:
package com.linkedin.learning.linkedinlearningfullstackappangularspringboot;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class LinkedInLearningFullStackAppAngularSpringBootApplicationTests {
#Test
public void contextLoads() {
}
}
heres the screenshot to make it clearer:
enter image description here

Kotlin - running an integration test, error `fun main is already defined`

I have written an integration test for a Web application but I don't know how to run this test. Maven build fails with the following error when I execute mvn test
[INFO] Compiling Kotlin sources from [src/main/kotlin]
[INFO] Classpath: <...>
[INFO] Classes directory is <...>\target\classes
[INFO] Module name is demo
[ERROR] <...>\src\main\kotlin\demo\Application.kt: (9, 1) 'public fun main(args: kotlin.Array<kotlin.String>): kotlin.Unit' is already defined in demo
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
Here is my main class (Application.kt)
package demo
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
#SpringBootApplication
open class Application
fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
Here is a part of my test class
package demo
import org.junit.runner.RunWith
import org.springframework.boot.test.SpringApplicationConfiguration
import org.springframework.boot.test.WebIntegrationTest
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import demo.Application
import demo.model.City
import demo.repository.CityRepository
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebIntegrationTest({"server.port=8080"})
class CityControllerTest {
#Autowired
private var cityRepository: CityRepository?
private val restTemplate: RestTemplate = TestRestTemplate()
// ...
}
There is no such error when I execute mvn clean test. So I suppose Kotlin is frightened of previously compiled classes. Though I am very unsure...
What is the right way to run an integration test?
It is a bug (https://youtrack.jetbrains.com/issue/KT-10051). Already fixed and the fix will be delivered very soon

Resources