Autoconfigure ReactiveCrudRepos in integration tests - spring-boot

I'm having some difficulty in writing some Integration tests. I have something like so
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Service.class)
public class ServiceTest {
#Rule
public PostgreSQLContainer postgres = new PostgreSQLContainer();
#Autowired
Service Service;
#Before
public void setUp() {
PostgresqlConnectionFactory connectionFactory = new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder()
.host(postgres.getHost())
.port(postgres.getFirstMappedPort()) // optional, defaults to 5432
.username(postgres.getUsername())
.password(postgres.getPassword())
.database(postgres.getDatabaseName()) // optional
.build());
Resource resource = new ClassPathResource("sql.sql");
Mono<PostgresqlConnection> mono = connectionFactory.create();
mono.map(connection -> connection
.createStatement(Helpers.asString(resource))
.execute()).block();
}
#Test
public void test() {
Request Request = new Request();
request.setName("name");
Mono<Item> itemMono = Service.createNewHub(hubRequest);
Item item = itemMono.block();
Assert.assertEquals(1L, 1L);
}
}
And my Service.class looks like the below
#Service
public class Service {
private Repository repository;
public Service(Repository repository) {
this.repository = repository;
}
public Flux<Item> getAllItems() {
return repository.findAll();
}
}
And my repo
#Repository
public interface Repository extends ReactiveCrudRepository<Item, Integer> {
}
My error is the following
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.Repository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
While all of the code I have written in the application is able to be injected fine, when it comes to the ReactiveCrudRepos, I am not having any luck on getting their instantiated object. What do I need to do to have the implementations created and injected?

As long as you use #SpringBootTest(classes = Service.class), the other beans are not loaded into the application context. The annotation element class of the annotation is described as:
The component classes to use for loading an ApplicationContext.
Remove the element and use the #SpringBootApplication as is:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Service.class)
public class ServiceTest { /* code */ }
Remember, using this way is testing an application context completely different from what will be run on the production environment. It should be a last resort.
Moreover, avoid naming the interface Repository when there exists the Spring annotation #Repository itself. I would personally prefer:
#Repository
public interface ItemRepository extends ReactiveCrudRepository<Item, Integer> {
}

Related

Geting java.lang.IllegalStateException: Duplicate mock definition while using #MockBean in test case

I have one service class that I want to mock but while running the test I am Getting Caused by: java.lang.IllegalStateException: Duplicate mock definition [MockDefinition#482ba4b1 name = '', typeToMock = com.service.ThirdPartyService, extraInterfaces = set[[empty]], answer = RETURNS_DEFAULTS, serializable = false, reset = AFTER]
I have tried to create mock service using #MockBean at class level, field level, and used #Qualifier as well to resolve the issue
#Service
public class ThirdPartyService{
.......................
public String decrypt(String encryptedText) {
//third party SDK I am using
return Service.decrypt.apply(encryptedText);
}
.........
..............
}
#ComponentScan("com")
#PropertySource({"classpath:/api.properties", "classpath:/common.properties"})
#SpringBootConfiguration
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#Transactional
public class TestControllerTest extends IntegrationTest {
#MockBean
ThirdPartyService thirdPartyService;
#Before
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
#Test
public void test() throws Exception {
when(ts.decrypt("encryptedText")).thenReturn("decryptedText")
Request req = Request.builder().name("name123").build();
//written performPost method in some other class
ResultActions action = performPost("/test", req);
action.andExpect(status().isOk());
}
}
public class IntegrationTest {
protected final Gson mapper = new Gson();
private MockMvc mvc;
#Autowired
private WebApplicationContext context;
public ObjectMapper objectMapper = new ObjectMapper();
#Before
public void setup() {
this.mvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
}}
When I am calling Thirdparty service decrypt method then it should return me decryptedText as a string. But getting duplicate mock definition error
I had the same issue.
Cause of this were test configuration file which was put somewhere else and it contained the mocked bean.
I have solved this by using #Autowired instead of #MockBean as this will result in autowiring the already mocked bean.
In my case the problem appeared after another dependency update and the reason was in the #SpringBootTest annotation referencing the same class twice:
#SpringBootTest(classes = {MyApplication.class, ApiControllerIT.class})
class ApiControllerIT extends IntegrationTestConfigurer {
// ...
}
#SpringBootTest(classes = {MyApplication.class, TestRestTemplateConfiguration.class})
public class IntegrationTestConfigurer {
// ...
}
I fixed it by removing #SpringBootTest annotation from the child class (ApiControllerIT).
In my case it was incorrect test class name that doesn't end with 'Test'.
If you have nested test classes try this:
#NestedTestConfiguration(OVERRIDE)
From Spring release notes: https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-5.x#upgrading-to-version-53

Spring boot: #ConfigurationProperties not satisfied on test

I'm getting this exception on my test excution:
UnsatisfiedDependencyException: Error creating bean with name 'net.gencat.transversal.espaidoc.mongo.GridFSTest': Unsatisfied dependency expressed through field 'resourceProperties'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'net.gencat.transversal.espaidoc.ResourcesConfigProperties' available: expected at least 1 bean which qualifies as autowire candidate.
So, I think message is so clear enough: ResourcesConfigProperties is not satisfied.
My test:
RunWith(SpringRunner.class)
#SpringBootTest()
public class GridFSTest {
#Autowired
private GridFsTemplate gridFsTemplate;
#Autowired
private ResourcesConfigProperties resourceProperties;
public URL getHugeResource() {
try {
return Paths
.get(this.resourceProperties.getHuge())
.toUri()
.toURL();
} catch (MalformedURLException e) {
return null;
}
}
#Test
public void storeHugeFile() throws IOException {
URL resource = this.getHugeResource();
this.gridFsTemplate.store(
resource.openStream(),
resource.getPath(),
"mime"
);
}
}
and ResourcesConfigProperties is:
#ConfigurationProperties(prefix = "files")
public class ResourcesConfigProperties {
private String huge;
/**
* #return the huge
*/
public String getHuge() {
return huge;
}
/**
* #param huge the huge to set
*/
public void setHuge(String huge) {
this.huge = huge;
}
}
into my src/test/resources I have my application.properties file:
files.huge: /home/jcabre/Downloads/1GB.zip
Any ideas?
EDIT
Main Spring boot application:
#SpringBootApplication(
//scanBasePackages = { "cat.gencat.ctti.canigo.arch.web.rs" },
exclude = JmxAutoConfiguration.class
)
#EnableConfigurationProperties({
ApiProperties.class,
FileStoreProperties.class
})
#Import(RedisConfiguration.class)
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
TL;DR:
It is happening, because the #ConfigurationProperties are not managed by the application context you build in tests, although they will be load when the application launches, because you have #EnableConfigurationProperties on your app main class.
#EnableConfigurationProperties on main class only affect the application context you have when you bootRun, but not that in a #SpringBootTest. The application context you build in tests could be, under many circumstances, distinct with that of bootRun, just like in your case.
You can add #Component to make the context be aware of it, both in gradle bootRun and in gradle test. It's the easiest way, but not 100% the suggested way.
More proper way with details if you have time
Instead, you can add #EnableConfigurationProperties({Config1.class, Config2.class}) in a #SpringBootTest, to inject only some of the configuration properties class into the context, to avoid injection overhead.
It would be like:
//#RunWith(SpringRunner.class) // for JUnit4 in Spring
#ExtendWith(SpringExtension.class) // for JUnit5 in Spring.
#SpringBootTest
#EnableConfigurationProperties({
ResourcesConfigProperties.class,
})
#Data
public class ConfigsTest {
#Autowired
private ResourcesConfigProperties resourceConfigProperties;
...
}
Better yet, you can use #SpringBootTest(classes={}): classes within {} are those you want the application context of #SpringBootTest to manage(creation, initialization, loading properties from yaml files, and so on). Then you don't have to load all the context, but only part of it.
You can group all classes of #ConfigurationProperties in one class of #Configuration, and put it in the classes={} of #SpringBootTest, instead of repeating this list of #ConfigurationProperties everywhere. Something like:
//#RunWith(SpringRunner.class) // for JUnit4 in Spring
#ExtendWith(SpringExtension.class) // for JUnit5 in Spring.
#SpringBootTest(classes = {
TestConfiguration.class
})
#Data
public class ConfigsTest {
#Autowired
private ResourcesConfigProperties resourceConfigProperties;
...
}
TestConfiguration.java:
#EnableConfigurationProperties({
ResourcesConfigProperties.class,
})
#Configuration
public class TestConfiguration {
}
You need to add ResourcesConfigProperties to your EnableConfigurationProperties annotation in the main spring boot class, this will load and create a bean out of the ResourceConfigProperties for you
You could also add #Component to your ResourceConfigProperties if you do not want to add it to the EnableConfigurationProperties annotation.
When using the SpringBootTest or any slice test it will use whatever is annotated on, or beans defined within the main SpringBootApplication within the test context.
You also need to annotate ResourcesConfigProperties class with #Configuration as below, otherwise it will not create a bean of this class in the spring container.
#Configuration
#ConfigurationProperties(prefix = "files")
public class ResourcesConfigProperties {
private String huge;
/**
* #return the huge
*/
public String getHuge() {
return huge;
}
/**
* #param huge the huge to set
*/
public void setHuge(String huge) {
this.huge = huge;
}
}

Spring Boot Camel Testing

I need to test Camel routes in a Spring Boot Application.
I've the Spring boot main class with all the necessary beans declared in it.
I am using the CamelSpringJUnit4ClassRunner.class.
Added my Spring boot main class in #ContextConfiguration as it contains all the configurations. I don't have a separate configuration class.
I 've autowired CamelContext in my Test class:
#Autowired
CamelContext camelContext;
But the test fails with the error:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'org.apache.camel.CamelContext' available: expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Try to use the CamelSpringBootRunner.class as the runner and add the #SpringBootTest annotation to the test class.
Example from the Camel repository
UPDATE (based on your comment)
If you change your bootstrapper class to SpringBootTestContextBootstrapper then it should work:
#BootstrapWith(SpringBootTestContextBootstrapper.class)
The equivalent configuration as you have but in this case you don't need to add the ContextConfiguration and the BootstrapWith annotation:
#RunWith(CamelSpringBootRunner.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#MockEndpoints("log:*")
#DisableJmx(false)
#SpringBootTest(classes = MyClass.class)
just enable #EnableAutoConfiguration it will work
With Camel 3.1 Spring Boot 2.2.5 and JUnit5, while also setting test application properties:
#CamelSpringBootTest
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestPropertySource(properties = "spring.cloud.consul.enabled=false")
public class CamelRouteTest {
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private CamelContext camelContext;
#EndpointInject("mock:bean:userService")
private MockEndpoint mockUserService;
private User user;
#BeforeEach
public void setUp() throws Exception {
AdviceWithRouteBuilder.adviceWith(camelContext, "getUsersRoute", a -> {
a.mockEndpointsAndSkip("bean:userService*");
});
user = new User();
user.setId(1);
user.setName("Jane");
mockUserService.returnReplyBody(constant(new User[] {user}));
}
#Test
public void callsRestWithMock() {
ResponseEntity<User[]> response = restTemplate.getForEntity("/rest/users", User[].class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
User[] s = response.getBody();
assertThat(s).contains(user);
}
#Test
public void callsDirectRouteWithMock() throws Exception {
User[] users = DefaultFluentProducerTemplate.on(camelContext)
.to("direct:getusers")
.request(User[].class);
assertThat(users).contains(user);
}
#Test
public void camelStarts() {
assertEquals(ServiceStatus.Started, camelContext.getStatus());
assertThat(camelContext.getRoutes()).hasSizeGreaterThan(0);
}
}
Assuming a RouteBuilder:
#Component
public class CamelRouter extends RouteBuilder {
#Value("${server.port}")
private int serverPort;
#Override
public void configure() throws Exception {
restConfiguration()
.contextPath("/rest")
.component("servlet")
.apiContextPath("/api-doc")
.port(serverPort)
.bindingMode(RestBindingMode.json)
.dataFormatProperty("prettyPrint", "true");
rest("/users")
.consumes("application/json")
.produces("application/json")
.get()
.outType(User[].class).to("direct:getusers");
from("direct:getusers").routeId("getUsersRoute")
.log("Get users")
.to("bean:userService?method=listUsers");
}
}
and application.yml:
camel:
component:
servlet:
mapping:
context-path: /rest/*
springboot:
name: MyCamel

No qualifying bean of type [java.lang.Class] found for dependency [java.lang.Class<org.springframework.data.repository.Repository<?, ?>>]

I have started working with Spring framework. Here I am working with Spring Data - Cassandra Repository modular application. I could able to test a spring-data-cassandra application individually, whereas when I try to use as a moudle in a project and scan the packages of components from other module like...
<context:component-scan base-package="example.dao,example.domain" />
I am getting an error
No qualifying bean of type [example.domain.EventRepository] found for dependency [example.domain.EventRepository]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
I can share you the code, if required.
The way I have done for another modules works fine.
I am not getting where is the problem.
Please find the code below for DAO CLASS.
#Service
#Transactional
public class EventDao {
#Autowired
private EventRepository eventRepository;
/*public EventDao(EventRepository eventRepository) {
this.eventRepository = eventRepository;
}*/
private final static Logger logger = LoggerFactory.getLogger(EventDao.class);
public Event saveMember(Event member) {
eventRepository.save(member);
return member;
}
}
My Repository interface.
package example.domain;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.Query;
public interface EventRepository extends CassandraRepository<Event> {
#Query("select * from event where type = ?0 and bucket=?1")
Iterable<Event> findByTypeAndBucket(String type, String bucket);
}
My Cassandra configuration class.
package example;
#Configuration
#PropertySource(value = { "classpath:cassandra.properties" })
#EnableCassandraRepositories(basePackages = { "example" })
public class CassandraConfiguration extends AbstractCassandraConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(CassandraConfiguration.class);
#Autowired
private Environment environment;
#Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
cluster.setContactPoints(environment.getProperty("cassandra.contactpoints"));
cluster.setPort(Integer.parseInt(environment.getProperty("cassandra.port")));
return cluster;
}
#Override
protected String getKeyspaceName() {
return environment.getProperty("cassandra.keyspace");
}
#Bean
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
return new BasicCassandraMappingContext();
}
}
UPDATE
I could able to create individual spring-data-cassandra and spring-data-neo4j modules seperately with other service modules and its working fine in both the modules.
But I have another module with Neo4j spring-data-neo4j module in the same project, when I try to run both(neo4j+cassandra) the modules under the same project its creating the problem.
still waiting for the help! I have tried my best!
Thanks!

Multiple Spring Configuration files (one per Profile)

I'm a Spring rookie and trying to benefit from the advantages of the easy 'profile' handling of Spring. I already worked through this tutorial: https://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile and now I'd like to adapt that concept to an easy example.
I've got two profiles: dev and prod. I imagine a #Configuration class for each profile where I can instantiate different beans (implementing a common interface respectively) depending on the set profile.
My currently used classes look like this:
StatusController.java
#RestController
#RequestMapping("/status")
public class StatusController {
private final EnvironmentAwareBean environmentBean;
#Autowired
public StatusController(EnvironmentAwareBean environmentBean) {
this.environmentBean = environmentBean;
}
#RequestMapping(method = RequestMethod.GET)
Status getStatus() {
Status status = new Status();
status.setExtra("environmentBean=" + environmentBean.getString());
return status;
}
}
EnvironmentAwareBean.java
public interface EnvironmentAwareBean {
String getString();
}
EnvironmentAwareBean.java
#Service
public class DevBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "development";
}
}
EnvironmentAwareBean.java
#Service
public class ProdBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "production";
}
}
DevConfig.java
#Configuration
#Profile("dev")
public class DevConfig {
#Bean
public EnvironmentAwareBean getDevBean() {
return new DevBean();
}
}
ProdConfig.java
#Configuration
#Profile("prod")
public class ProdConfig {
#Bean
public EnvironmentAwareBean getProdBean() {
return new ProdBean();
}
}
Running the example throws this exception during startup (SPRING_PROFILES_DEFAULT is set to dev):
(...) UnsatisfiedDependencyException: (...) nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [EnvironmentAwareBean] is defined: expected single matching bean but found 3: prodBean,devBean,getDevBean
Is my approach far from a recommended configuration? In my opinion it would make more sense to annotate each Configuration with the #Profile annotation instead of doing it for each and every bean and possibly forgetting some variants when new classes are added later on.
Your implementations of EnvironmentAwareBean are all annotated with #Service.
This means they will all be picked up by component scanning and hence you get more than one matching bean. Do they need to be annotated with #Service?
Annotating each #Configuration with the #Profile annotation is fine. Another way as an educational exercise would be to not use #Profile and instead annotate the #Bean or Config classes with your own implementation of #Conditional.

Resources