i am trying to setup unit tests for some elements to be used within a spring(-boot) application, and i struggled with setup around ConfigurationProperties and EnableConfigurationProperties. the way i finally got it to work doesn't seem consistent with the examples that i have seen in that i have witnessed needing both ConfigurationProperties and EnableConfigurationProperties on my configuration class, which doesn't seem right, and i was hoping that someone might provide some guidance.
here is a simplified example:
JavaTestConfiguration.java
package com.kerz;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.validation.constraints.NotNull;
#Configuration
#ConfigurationProperties
#EnableConfigurationProperties
public class JavaTestConfiguration {
public void setFoo(String foo) {
this.foo = foo;
}
#NotNull
String foo;
#Bean
String foo() {
return foo;
}
}
JavaTestConfigurationTest.java
package com.kerz;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {JavaTestConfiguration.class})
#TestPropertySource("classpath:test.properties")
public class JavaTestConfigurationTest {
#Autowired
String foo;
#Test
public void shouldWork() throws Exception {
assertEquals("foo", "bar", foo);
}
}
test.properties
foo=bar
Your test is more integration test if you are starting Spring context. Therefore you should test also production spring configuration.
I would advise not to create testing configuration. Use one production configuration for testing.
You are also using #TestPropertySource annotation, which is used when you need to define test specific properties. If you can test with PROD configuration do not use it.
Related
I'm struggling with JUnit 5 when moving the #SpringBootApplication to a different package.
I have setup a new SpringBoot-project (2.2.1.RELEASE) with Maven and Eclipse (had to upgrade this from "Eclipse Photon" to support the SpringBoot-Release
My package layout looks like this:
/src/main/java
com.package.sample.appl1
StartSamples.java
com.package.sample.appl1.start
com.package.sample.appl1.dbaccess
com.package.sample.appl1.run
com.package.sample.appl1.utils
com.package.sample.appl2.run
com.package.sample.appl2.run
/src/test/java
com.package.sample.appl1.dbaccess
SimpleTest.java
The class holding the #SpringBootApplication is:
#ComponentScan({
"com.package.sample"
})
#SpringBootApplication
public class StartSamples {
public static void main(String[] args) {
System.out.println("Start");
try {
SpringApplication.run(StartSamples.class, args);
} catch (Exception e) {
LOGGER.error("", e);
System.exit(-1);
}
}
And the test is this:
import static org.junit.Assert.assertEquals;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Test the Query-statements and the DAO methods
*
* #author U005078
*
*/
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ComponentScan({
"com.package.sample"})
#EnableAutoConfiguration
public class SimpleTest {
#SuppressWarnings("unused")
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleTest.class);
#Test
#DisplayName("SimpleTest")
public void testTotalRows() {
With this configuration all is fine, "StartSamples" works as expected and aqlso the SimpleTest.
But when moving "StartSamples" to a different package (e.g. "com.package.sample.start" would make more sense to me - "StartSamples" is still ok but "SimpleTest" does not fail nor succeed - test seems not to become executed.
I see a message:
class path resource [com/package/sapmle/appl1/dbaccess/SimpleTest-context.xml] does not exist
class path resource [com/package/sapmle/appl1/dbaccess/SimpleTestContext.groovy] does not exist
.SimpleTest]: SimpleTest does not declare any static, non-private, non-final, nested classes annotated with #Configuration.
I also found:
Neither #ContextConfiguration nor #ContextHierarchy found for test class [com.package.sample.appl1.dbaccess.SimpleTest], using SpringBootContextLoader
So I defined the #ContextConfiguration to the "SimpleTest", then it worked. But I do not understand at all why the move of the #SpringBootApplication did change this behaviour.
With another try of setting up this project I ended up with "No tests found with test runner 'JUnit 5'" and could also not find any reason. I started over again with the current approach and get to here. And do do nat any clue what gives me the error - for either of the problems.
Any explanation witld be appreciated. I tried for lots of hours now to find something in the internet - but I only found recommendations like "try this", "try that" but no help in understanding.
So any help is appreciated.
Define your SpringBoot Main class like below
#SpringBootTest(classes = {StartSamples.class})
public class SimpleTest {
...
}
I have the below code.
Note that I have an interface MySuperCoolEntityRepositoryContract.
And I have a "concrete interface" MySuperCoolEntityJpaRepository that implements my above MySuperCoolEntityRepositoryContract interface and JpaRepository.
All of that works fine with #ComponentScan.
I am changing my code to "java config", aka a centralized location where I can code up my DI definitions. (Also known as CompositionRoot in some circles).
The issue is when I try to "code up" the concrete for the interface. (Skip down to later in this question.
package com.me.domain.jpaentities;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
#Entity(name = "MySuperCoolEntityTableName")
public class MySuperCoolEntity implements Serializable {
#Id
#Column(name = "CoolSurrogateKeyColumn")
private String coolSurrogateKey;
#Column(name = "CoolMagicValueColumn")
private String coolMagicValue;
public String getCoolSurrogateKey() {
return this.coolSurrogateKey;
}
public void setCoolSurrogateKey(String coolSurrogateKey) {
this.coolSurrogateKey = coolSurrogateKey;
}
public String getCoolMagicValue() {
return this.coolMagicValue;
}
public void setCoolMagicValue(String coolMagicValue) {
this.coolMagicValue = coolMagicValue;
}
}
===============
package com.me.dal.repositories.interfaces;
import com.me.domain.jpaentities.MySuperCoolEntity;
import java.util.Collection;
public interface MySuperCoolEntityRepositoryContract {
Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);
}
=========================
package com.me.dal.repositories;
import com.me.dal.repositories.interfaces.MySuperCoolEntityRepositoryContract;
import com.me.domain.jpaentities.MySuperCoolEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Collection;
#Repository
public interface MySuperCoolEntityJpaRepository extends MySuperCoolEntityRepositoryContract, JpaRepository<MySuperCoolEntity,String> {
Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);
}
Now this issue.
package com.me.myapplication.configuration;
import com.me.dal.repositories.MySuperCoolEntityJpaRepository;
import com.me.dal.repositories.interfaces.MySuperCoolEntityRepositoryContract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MyCompositionRoot {
#Bean
public MySuperCoolEntityRepositoryContract getAMySuperCoolEntityRepositoryContract()
{
return new MySuperCoolEntityJpaRepository(); /* << issue is here, this is an abstract class, aka, an interface with some methods defined */
}
}
Using the super cool JpaRepository "concrete interface" aka "really an abstract class but called an interface" aka "Interface Default Methods" ( see https://dzone.com/articles/interface-default-methods-java ) ........
The exact error is:
MySuperCoolEntityJpaRepository is abstract; cannot be instantiated
I do understand the error. MySuperCoolEntityJpaRepository is abstract. I get that.
But with this super cool "just extend JpaRepository and get all kinds of default functionality".....
How do I register a concrete JpaRepository with Spring DI (specifically with "code it up" java config ?
............
I tried making it a "class".
public class MySuperCoolEntityJpaRepository extends MySuperCoolEntityRepositoryContract, JpaRepository<MySuperCoolEntity,String>
but that wants me to define all those built in methods like "findAll",etc, etc.
Spring boot magically provides implementation for the methods defined in your interface. The #EnableJpaRepositories scans all packages below the package for interfaces extending JpaRepository and creates a Spring bean for it that is backed by an implementation of SimpleJpaRepository (spring data provides default imlpementations of CRUD repository through this class).
Your interface MySuperCoolEntityJpaRepository extends the interface MySuperCoolEntityRepositoryContract , but you only extend the JpaRepository on the interface MySuperCoolEntityJpaRepository which means spring will only provide the default implementations for methods in the interface MySuperCoolEntityJpaRepository . So try it like :
public interface MySuperCoolEntityRepositoryContract extends JpaRepository<MySuperCoolEntity,String>{
Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);
}
then extend this in your repository like :
#Repository
public interface MySuperCoolEntityJpaRepository extends MySuperCoolEntityRepositoryContract {
Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);
}
Related Post : how annotation #Repository in java spring work?
I figured out a workaround. I don't really like it, but I guess it works.
I also added MySuperCoolEntityBalServiceContract (you can get the idea from just the below), so you know why/how I need to have the getAMySuperCoolEntityRepositoryContract method in my CompositionRoot class below.
I'll leave this (not marked) as the answer in case someone else has a better way, or sees issue(s) with the below. I don't like the EntitiyManager work around, but it got things moving.
package com.me.myapplication.configuration;
import com.me.apicore.managers.MySuperCoolEntityBalService;
import com.me.apicore.managers.interfaces.MySuperCoolEntityBalServiceContract;
import com.me.dal.repositories.interfaces.MySuperCoolEntityRepositoryContract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import javax.inject.Inject;
import javax.persistence.EntityManager;
#Configuration
public class MyCompositionRoot {
#Inject
private EntityManager entManager; /* part of the work around trick */
#Bean
public MySuperCoolEntityBalServiceContract getAMySuperCoolEntityBalServiceContract() {
return new MySuperCoolEntityBalService(this.getAMySuperCoolEntityRepositoryContract());
}
#Bean
public MySuperCoolEntityRepositoryContract getAMySuperCoolEntityRepositoryContract() {
//return new MySuperCoolEntityJpaRepository(); /* does not work. :( */
RepositoryFactorySupport factory = new JpaRepositoryFactory(entManager);
MySuperCoolEntityRepositoryContract repository = factory.getRepository(MySuperCoolEntityRepositoryContract.class);
return repository;
}
}
And I tweaked this (note the addition of the RepositoryDefinition annotation)
package com.me.dal.repositories.interfaces;
import com.me.domain.jpaentities.MySuperCoolEntity;
import org.springframework.data.repository.RepositoryDefinition;
import java.util.Collection;
#RepositoryDefinition(domainClass = MySuperCoolEntity.class, idClass = String.class)
public interface MySuperCoolEntityRepositoryContract {
Collection<MySuperCoolEntity> findByCoolMagicValue(String coolMagicValue);
}
I'm trying to create a test class for some of my services but I haven't been able to mock my DAOs, which are annotated as seen here:
import org.springframework.stereotype.Repository;
#Repository
public class TestDAO {
}
This is my test class
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
class ArchivoTest {
#Mock
public TestDAO testDAO;
#Test
public void test() {
System.out.print(testDAO == null);
}
}
When I run the test with JUnit archivoDAO is always null, and I don't know what to do about it or if I'm missing something. I'm a complete noob at mockito, but a half an hour search through google solved nothing, so I guess it's something not that obvious. Thanks in advance
I'm using AspectJ and AOP in a Spring-boot project in order create an external library to log some activities.
Although I have configured this pointcut:
#Pointcut("call(void org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage(Message,Channel))")
private void getEventOnMessage(){}
the aspect
#Before(value="getEventOnMessage()")
public void getEventOnMessage(JoinPoint joinPoint){
System.out.println("VOILA'");
}
is not triggered.
Details:
package com.tim.sdp.timLogging.Aspects.handler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages="org.springframework.amqp.rabbit.listener.adapter")
public class AppConfig {
#Bean()
public AspectForOnMessage myAspect() {
return new AspectForOnMessage();
}
}
Aspect class implementation:
package com.tim.sdp.timLogging.Aspects.handler;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
#Component
#Aspect
public class AspectForOnMessage {
#Pointcut("call(void org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage(Message,Channel))")
private void getEventOnMessage(){}
#Before(value="getEventOnMessage()")
public void getEventOnMessage(JoinPoint joinPoint){
System.out.println("VOILA'");
}
}
Might you help me, please? It's the only event I can not capture.
In this forum you can find another person with the same problem:
Spring forum link
Thank you in advance.
Oh, a classical one!
As documented here, call() is not supported in proxy-based Spring AOP which you have configured in your application via #EnableAspectJAutoProxy. You need to switch from "AOP lite" to the full power of AspectJ as described there or stick with pointcuts really supported in Spring AOP.
Having some trouble injecting a dependency in one of my JUnit test classes.
I believe the TestApplication is not package scanning or is not being loaded.
Code below:
package com.mitto.repositories;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import com.github.springtestdbunit.DbUnitTestExecutionListener;
import com.github.springtestdbunit.annotation.DatabaseSetup;
import com.mitto.MittoApplicationTests;
import com.mitto.domain.User;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration( classes= { MittoApplicationTests.class } )
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class})
#DatabaseSetup("UserRepositoryTest.xml")
public class UserRepositoryTest {
#Autowired
UserRepository repository;
private static final long FACEBOOK_ID = 1234567;
#Test
public void getUserById() {
User user = repository.findOne(1L);
assertNotNull(user);
assertEquals( user.getFacebookId(), FACEBOOK_ID );
}
}
MittoApplicationTests.java
package com.mitto;
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 MittoApplicationTests {
#Test
public void contextLoads() {
}
}
UserRepository.java
package com.mitto.repositories;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.mitto.domain.User;
#Repository
public interface UserRepository extends PagingAndSortingRepository<User, Long>{
User findByFacebookId( long facebookId );
User findByAuthToken( String token );
}
I can't see anything wrong with this.
Sometimes, a working example is better than fixes.
Here is a working example:
First, in your configuration class
#SpringBootApplication
#ComponentScan(value = "com.mitto")
#EnableJpaRepositories(value = "com.mitto")
#EntityScan(basePackages = {"com.mitto.domain"}, basePackageClasses = {Jsr310JpaConverters.class})
public class MittoApplicationTests {
}
Second, in your test class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MittoApplicationTests.class) // replace the #ContextConfiguration with #SpringBootTest
// rest of of your annotations ...
public class UserRepositoryTest {
#Autowired
UserRepository repository;
// your test cases
}
A Spring Boot application is just a Spring ApplicationContext, so nothing very special has to be done to test it beyond what you would normally do with a vanilla Spring context. One thing to watch out for though is that the external properties, logging and other features of Spring Boot are only installed in the context by default if you use SpringApplication to create it.
Spring Boot provides a #SpringBootTest annotation which can be used as an alternative to the standard spring-test #ContextConfiguration annotation when you need Spring Boot features. The annotation works by creating the ApplicationContext used in your tests via SpringApplication.
Please read the documentation for more details:
SpringBootTest annotation
boot-features-testing