I have created the below class under test folder to unit test the cassandra connection in my spring boot application
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest({ "spring.data.cassandra.port=9042",
"spring.data.cassandra.keyspace-name=keyspacename","spring.data.cassandra.concontact-points=url",
"spring.config.import=vaultpath"})
#EnableAutoConfiguration
#ContextConfiguration
#ComponentScan
#TestExecutionListeners({ CassandraUnitDependencyInjectionTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class })
#CassandraUnit
public class ApplicationTests {
#Autowired
TestRepository testRepository;
#Test
public void contextLoads(){
System.out.println(test.findBytestId("***"));
}
}
Below are the dependencies added in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>org.cassandraunit</groupId>
<artifactId>cassandra-unit-spring</artifactId>
<version>2.1.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Getting the below error on trying to run the unit test not sure what other config needs to be added in test path
Expecting URI in variable: [cassandra.config]. Please prefix the file with file:/// for local files or file:/// for remote files. Aborting. If you are executing this from an external tool, it needs to set Config.setClientMode(true) to avoid loading configuration.
at org.cassandraunit.utils.EmbeddedCassandraServerHelper.copy(EmbeddedCassandraServerHelper.java:278)
at org.cassandraunit.utils.EmbeddedCassandraServerHelper.startEmbeddedCassandra(EmbeddedCassandraServerHelper.java:82)
at org.cassandraunit.utils.EmbeddedCassandraServerHelper.startEmbeddedCassandra(EmbeddedCassandraServerHelper.java:64)
Fatal configuration error; unable to start. See log for stacktrace.
Related
I'm making custom JDBC driver and I need to test it (with Spring-boot). I added a dependency (I use Maven) this driver JAR to another project and it's work fine in the main program. But when I'm trying to make integration tests (in unit tests the driver registered in DriverManager by itself) driver doesn't load so Spring-boot can't create bean with name 'dataSource'.
I also tried to write unit tests to use the driver directly, but the driver cannot load the properties file that is contained in the resources (file path is src/main/resources/driver.properties). In this case the driver registered in DriverManager by itself.
How can I make it so that:
The driver can see the file from resources during unit tests? FIXED
The driver start register itself during integration tests with Spring?
Note: JDBC use the service provider mechanism to load drivers, so I also have the java.sql.Driver file in resources.(src/main/resources/META-INF/services/java.sql.Driver).
Link for the JDBC driver code. Readme have also link to project, where I'm trying to test driver
Code of test:
#SpringBootTest
public class ApplicationSmokeTest {
#Test
public void contextLoad(){
assertTrue(true);
}
}
In main program driver is loaded and spring work with them fine.
Dependencies:
<dependencies>
<dependency>
<groupId>org.mock.jdbc</groupId>
<artifactId>HttpDriver</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
I'm working on migrating my project from Java 8 to Java 11. So I used, Spring 5.1.0, Java 11, Eclipse 4.16 and Tomcat 9. I'm able to build the source successfully. But when it comes to Tests, they are getting failed.
Here is what I've in pom.xml for tests.
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.27.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
And my test cases runs absolutely fine with the above dependencies in Java 8. But when I migrate the code to Java 11, I'm getting the below exception.
ERROR: org.springframework.test.context.TestContextManager - Caught exception while allowing
TestExecutionListener
[org.springframework.test.context.support.DependencyInjectionTestExecutionListener#54e063d] to
prepare test instance [com.test.SomeTest2#4293943]
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:122) ~[Spring-test-5.1.0.RELEASE.JAR:5.1.0.RELEASE]
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DefaultTestCDependencyInjectionTestExecutionListenerontext.java:122) ~[Spring-test-5.1.0.RELEASE.JAR:5.1.0.RELEASE]
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DefaDefaultTestCDependencyInjectionTestExecutionListenerontextultTestContext.java:122) ~[Spring-test-5.1.0.RELEASE.JAR:5.1.0.RELEASE]
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:312) ~[Spring-test-5.1.0.RELEASE.JAR:5.1.0.RELEASE]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211) ~[Spring-test-5.1.0.RELEASE.JAR:5.1.0.RELEASE]
Sample Test Class Structure I've
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:test-context.xml"})
public class SomeTestClass {
...
}
Which is getting failed because of the mentioned exception. But, I did some research and found a workaround i.e
to change from:
#ContextConfiguration(locations = {"classpath:test-context.xml"})
to this:
#ContextConfiguration(locations = {"classpath*:/spring/test-context.xml"})
And it works. But the problem is that I'm not allowed to edit the source code. How to achieve it?
If the directory that contains the "spring" directory is what is in your classpath, and not the "spring" directory itself, then this is not a "workaround", but a "fix". If you're not allowed to change anything, then you can't fix anything either.
According to your fix, it seems that test-context.xml is now located at classpath*:/spring/test-context.xml. Therefore, you can try adding the spring folder where test-context.xml is into the class path as well, and then it should work. No code changes needed for this solution. You can read how to do it at How do I add a directory to the eclipse classpath?
Im having a Springboot project where I have found a way to create and run simple Junit testcase which looks into a repository and fetches some data attribute for a given entity. The result of the Junit run is pass so no problem in regards to that.
But the thing is here, that I have seen a lot of examples out there where tutorials are showing Springboot projects where they can simply run Junit tests with only #Runwith or #SpringBootTest
for their specific test classes.
In my case I have to add 3 annotations, #SpringBootTest, #RunWith as well as #ContextConfiguation(with parameters) until Im able to run the testcase.
So my question is how will I be able to run it as minimalistic as possible, (some exercises I have seen have only one annotation for their springboot test class)
My Springboot test class looks like this:
Screenshot of my Junit class
and my Directory structure looks like this:
Screenshot of my Project directory structure
My application.properties looks like this:
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.show-sql=true
spring.datasource.url=jdbc:postgresql://localhost:5432/erfan
spring.datasource.username=erfan
spring.datasource.password=
#Some additional properties is trying to be set by Spring framework so this must be set
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
#spring.datasource.initialization-mode=always
#spring.datasource.initialize=true
#spring.datasource.schema=classpath:/schema.sql
#spring.datasource.continue-on-error=true
#HikariCP is a ConnectionPool manager, related to DB stuff
#Below is the property key you need to set to * as value to expose all kind of monitoring related information
#about your web application
#management.endpoints.web.exposure.include=*
And my pom.xml file:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<groupId>com.sample</groupId>
<artifactId>postgres</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>postgres</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
So am I missing like something in my application.properties file? Something that I should include to be able to remove "boilerplate" annotation in my test class?
Depends on what you're trying to do. Basically spring has custom annotations that configures the spring context to include only relevant beans. This is the so called test slices.
But there are a few "rules" I always try to follow:
Avoid #SpringBootTest unless you're doing integration testing, or manually setting which classes to use #SpringBootTest(classes = {MyService1.class, MyService2.class}
If you're testing spring jpa, you can use the #DataJpaTest annotation, example here
If you're testing controllers you can use the #WebMvcTest, example here
If you're testing other services, you can always use #ContextConfiguration to configure the spring context accordingly.
So for example, for your test I would write it in one of two ways:
#RunWith(SpringRunner.class)
#DataJpaTest
#Import(AnotherConfig.class)
class MyTest {
// test stuff here
}
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {AnotherConfig.class})
// NOTE, if you have a JpaConfig class where you #EnableJpaRepositories
// you can instead add this config class to the #ContextConfiguration classes
#EnableJpaRepositories
class MyTest {
// test stuff here
}
Basically, don't worry about how many annotations you have on top of your test, but worry about which beans/services are being autowired. For example the #SpringBootTest is a single annotation, but autowires all the beans in the spring context.
I strongly recommend not using a bunch of spring annotations on unit tests. Unit tests should only test one piece of code and not relate with externals or other layers, so Mockito should be sufficient.
Example:
#RunWith(MockitoJUnitRunner.class)
public class FooTest {
#InjectMocks
private FooService service;
#Mock
private FooRepository repository;
#Test
public void whenHappyPath_shouldReturnTrue(){
doReturn(Optional.empty()).when(repository).findById(anyLong());
assertTrue(service.isFoo(1L));
}
}
You are preventing your unit test to reach the repository layer, so you don't need to create a context with embedded DB or any other thing.
If you are using for integration tests then it is different and you will need different strategies. For that, I'd recommend use embedded DB on tests (which is made by default if you have h2 dependency):
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
And also use a integration or test spring profile:
#ActiveProfile("test") // or integration, you choose
public class FooIntegrationTest{
...
}
or force other configuration file to point to another configuration
#TestPropertySource(properties = { "spring.config.location=classpath:application-test.yml" })
application-test.properties
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=sa
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
Erfan, it completely depends on your test scenario.
First Scenario : Complete test (Integration Test)
If you want to test the whole of your app, like testing the Service layer, Repository layer, and the Controller layer, you need a real spring-context, so you must use all #SpringBootTest and #RunWith and ... to initialize spring context to test whole layers.
(This called integration-test)
Unit Test vs Integration Test: What's the Difference
how-to-use-java-integration-testing
Second Scenario: Unit test
If you want just to test a piece of your code, just like you want to test just service layer and other layers (like repository) does not important in your scenario, in this situation you must use some new framework like Mockito, to mock the pieces that you don't want to test them, in these scenarios you don't need the **spring-context initialization ** so you don't need to use #SpringBootTest or other annotations.
Mockito Sample
So based on your scenario you can use those annotations.
I strongly recommend you to read the below link for further information about best practices for testing in java.
Modern Best Practices for Testing in Java
I have spring test-case as shown below when I run it is not starting the Netty server and provides following exception.
Caused by: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:155)
Below is my test case:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SpringWebFluxDemoApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class CustomPriceControllerTest {
#Autowired
private ApplicationContext context;
private WebTestClient testClient;
#Before
public void init() {
testClient = WebTestClient.bindToApplicationContext(context).configureClient().baseUrl("http://localhost:8080").responseTimeout(Duration.ofSeconds(30)).build();
}
#Test
public void broadcastVoltageConsumption() {
this.testClient.get().uri("/api/getCustomPrice")
.accept(MediaType.APPLICATION_STREAM_JSON)
.exchange()
.expectBodyList(Custom.class)
.consumeWith((customResults) -> {
List<Custom> list = CustomResults.getResponseBody();
Assert.assertTrue(list != null && list.size() > 0);
});
}
}
My pom.xml has excluded the dependency for tomcat to enable Netty. My Spring boot class works perfectly fine. It boots Netty server.
Update - pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Had to add javax.servlet-api because I was facing issues with javax.servlet-api missing.
Update - 2
Removing the javax.servlet dependency from pom.xml solves the issue.
While when I try to run my main application it starts the Netty server normally. What is missing in this configuration? Can anyone help on this?
You wanted the webserver to be powered by Netty, but the exception you are getting is Servlet Specific
Since Netty is not servlet based technology, it seems you that somewhere (gradle/maven/#Configuration) you are mixing them,
so , just remove all references to Servlet dependencies and re try
If any dependency needs a servlet, it will force Jetty to come up and netty will never start in that case.
Whenever you see webflux is starting Jetty and not netty, you should run following command and then search who is including Jetty as dependency then you will find the answer.
./gradlew dependencies
In your case, you are including servlet dependency directly which is causing that issue.
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>test</scope>
</dependency>
I had the same issue. I just remove this dependency (which have servlet dependency):
testCompile('org.springframework.restdocs:spring-restdocs-mockmvc')
I've got a Maven project which uses Java-configured Spring (#Configuration etc.). Properties which are referenced by #Value are stored in different places, e.g. Tomcat's context.xml.
For testing I've created a .properties file to provide some values for the components and services. In my JUnit test (which uses a spring test context) this .properties file is added via #PropertySource. The problem is that the values will not be loaded from the file, instead the value identifier is set as value, e.g. ${someFlag:false} (so I get ClassCastExceptions for any other than String). Also the default value will not be set, so I think, the values won't be processed at all.
I'm sure Spring finds this file because when I change the value of #PropertySource I get some FileNotFoundException. Nevertheless I've tried different variants to point to this file an all have worked (tested by renaming which produced FileNotFoundException):
classpath:/test.properties (my preferred notation)
/test.properties
file:src/test/resources/test.properties
I'm also sure that Spring itself works, because when I remove the #Value, the class under test is injected via #Autowired in my test as expected.
Down below you'll find the problem scenario stripped down as much as possible. For versions and dependencies please see the pom.xml at the bottom.
MyService.java
package my.package.service;
// Imports
#Service
public class MyService {
#Value("${someFlag:false}")
private Boolean someFlag;
public boolean hasFlag() {
return BooleanUtils.isTrue(someFlag);
}
}
MyConfiguration.java
#Configuration
#ComponentScan(basePackages = {"my.package.service"})
public class MyConfiguration {
}
MyServiceComponentTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {MyTestConfiguration.class})
public class MyServiceComponentTest {
#Autowired
private MyService service;
#Test
public void hasFlagReturnsTrue() {
assertThat(service.hasFlag(), is(true));
}
}
MyTestConfiguration.java
#Configuration
#Import({MyConfiguration.class})
#PropertySource("classpath:/test.properties")
public class MyTestConfiguration {
}
src/test/resources/test.properties
someFlag=true
pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.2.3.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
The issue here is you need a PropertySourcesPlaceholderConfigurer also which is actually responsible for resolving the ${..} fields, just add another bean which creates this bean:
#Bean
public static PropertySourcesPlaceholderConfigurer propertiesResolver() {
return new PropertySourcesPlaceholderConfigurer();
}
With Spring 4, it's now possible to use TestPropertySource:
#TestPropertySource(value="classpath:/config/test.properties")
In order to load specific properties for a junit test
In addition to Biju Kunjummen answer:
If you use #ConfigurationProperties to inject properties into bean setters, then ConfigurationPropertiesBindingPostProcessor need to be created (instead of PropertySourcesPlaceholderConfigurer):
#Configuration
static class PropertyConfig {
#Bean
public static ConfigurationPropertiesBindingPostProcessor propertiesProcessor() {
return new ConfigurationPropertiesBindingPostProcessor();
}
}