Spring test: ApplicationContext configuration classes (spring data mongodb) - spring

I only want to test mongo related code. This is my test code snippet:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {MongoConfig.class})
#SpringBootTest
public class ModelTest {
#Autowired
private MongoTemplate mongoTemplate;
As you can see I'm using #ContextConfiguration in order to only load Mongo related configuration:
#Configuration
public class MongoConfig {
#Bean
public CustomConversions customConversions(){
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(new ReferenceWriterConverter());
return new MongoCustomConversions(converters);
}
}
As you can see it's only intented to load custom converters are going to be used by mongoTemplate in order to serialize objects to mongodb database.
Also, src/test/resources/application.properties is:
spring.data.mongodb.host: localhost
spring.data.mongodb.port: 27017
The problem is that when I'm trying to run test it's getting me an Unsatisfied dependency expressed through field 'mongoTemplate':
UnsatisfiedDependencyException: Error creating bean with name 'net.gencat.transversal.repositori.digital.mongo.ModelTest': Unsatisfied dependency expressed through field 'mongoTemplate'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.data.mongodb.core.MongoTemplate' available
Related project 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-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Any ideas?

Seems issue you are trying to load custom mongo configuration #Bean client here or extend AbstractMongoConfiguration.
change your database name here instead of demo
#Configuration
public class MongoConfig extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
return "demo";
}
#Override
public MongoClient mongoClient() {
return new MongoClient("localhost", 27017);
}
#Bean
public CustomConversions customConversions(){
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(new ReferenceWriterConverter());
return new MongoCustomConversions(converters);
}
}

Related

Could not autowire. No beans of 'StateMachineFactory<States, Events>' type found

#Configuration
#EnableStateMachineFactory
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter
<States, Events> {
// configuring...
}
public enum Events {
CONFIRM_RESET,
CANCEL_RESET
// other events
}
public enum States {
INITIAL,
STARTING_ORDER
// other states
}
#Service
#Slf4j
public class OrderService {
#Autowired
private StateMachineFactory<States, Events> stateMachineFactory;
// Could not autowire. No beans of 'StateMachineFactory<States, Events>' type found.
}
#EnableStateMachineFactory annotation does not work. Could not autowire. No beans of StateMachineFactory<States, Events>' type found.
In the same time after using #EnableStateMachine I can autowire 1 statemachine.
oh, this is a version of boot starter problem, i change my version from 2.2.1 to 2.5.4 and the problem get away
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-starter</artifactId>
<version>3.0.1</version>
</dependency>
This solved my problem

No qualifying bean of type available in SpringBoot Application

When running my SpringBoot application, I am getting this error:
An exception occurred while running. null: InvocationTargetException: Error creating bean with name 'bookController': Unsatisfied dependency expressed through field 'bookRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.experimental001.Repositories.BookRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)} -> [Help 1]
I have read through many posts regarding this error but none of them seem to fix my problem.
I have tried adding the #Controller annotation but it doesn't seem to fix the bean problem.
Controller:
#RestController
#RequestMapping("/api/books")
public class BookController {
#Autowired
private BookRepository bookRepository;
#GetMapping
public Iterable findAll() {
return bookRepository.findAll();
}
#GetMapping("/title/{bookTitle}")
public List findByTitle(#PathVariable String bookTitle) {
return bookRepository.findByTitle(bookTitle);
}
#GetMapping("/{id}")
public Book findOne(#PathVariable Long id) {
return bookRepository.findById(id)
.orElseThrow(() -> new BookNotFoundException("Book not found", null));
}
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public Book create(#RequestBody Book book) {
return bookRepository.save(book);
}
#DeleteMapping("/{id}")
public void delete(#PathVariable Long id) {
bookRepository.findById(id).orElseThrow(() -> new BookNotFoundException("Book not found", null));
bookRepository.deleteById(id);
}
#PutMapping("/{id}")
public Book updateBook(#RequestBody Book book, #PathVariable Long id) {
if (book.getId() != id) {
throw new BookIdMismatchException("error");
}
bookRepository.findById(id).
orElseThrow(() -> new BookNotFoundException("Book not found", null));
return bookRepository.save(book);
}
}
Simple Controller:
#Controller
public class SimpleController {
#Value("${spring.application.name}")
String appName;
#GetMapping("/")
public String homePage(Model model) {
model.addAttribute("appName", appName);
return "home";
}
}
Repository:
#EnableJpaRepositories
#Repository
public interface BookRepository extends CrudRepository<Book, Long> {
public List<Book> findByTitle(String title);
}
Pom:
<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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</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-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Application File:
#SpringBootApplication
#ComponentScan(basePackages = BookRepository)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You might need to add the following to the main app class:
#ComponentScan(basePackages = "Package where the repository is located”)
Looks like your repository is in a different packer and spring context is not able to pick it up when starting up.
Please refer this doc: https://www.baeldung.com/spring-component-scanningfor

spring boot fails to start-- define a bean of type 'TopicRepository' in configuration

I was following this JavaBrains tutorials of Spring Boot.
My project structure is as follows:
CourseApiApp.java:
#SpringBootApplication
#ComponentScan(basePackages = {
"com.bloodynacho.rishab.topics"
})
#EntityScan("com.bloodynacho.rishab.topics")
public class CourseApiApp {
public static void main(String[] args) {
SpringApplication.run(CourseApiApp.class, args);
}
}
TopicController.java:
#RestController
public class TopicController {
#Autowired
private TopicService topicService;
#RequestMapping(
value = "/topics"
)
public List<Topic> getAllTopcs() {
return topicService.getAllTopics();
}
}
TopicService.java:
#Service
public class TopicService {
#Autowired
private TopicRepository topicRepository;
public List<Topic> getAllTopics() {
List<Topic> topics = new ArrayList<>();
this.topicRepository
.findAll()
.forEach(topics::add);
return topics;
}
}
Topic.java:
#Entity
public class Topic {
#Id
private String id;
private String name;
private String description;
}
TopicRepository.java:
#Repository
public interface TopicRepository extends CrudRepository<Topic, String>{
}
pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</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>
</dependencies>
I was using the lombok #Getter, #Getter and #AllArgsConstructor in Topic.java but I removed it after reading one of the answers here.
I read this1, this2, this3
Still, I get
***************************
APPLICATION FAILED TO START
***************************
Description:
Field topicRepository in com.bloodynacho.rishab.topics.TopicService required a bean of type 'com.bloodynacho.rishab.topics.TopicRepository' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.bloodynacho.rishab.topics.TopicRepository' in your configuration.
Process finished with exit code 1
EDIT: I read this explaining how even without actually implementing the interface the #Autowired works. I understand the solution, but I don't understand how to solve my issue. Clearly, there is some problem with the way Spring Data is set up and configured (as mentioned in the answer)
Because if your other packages hierarchies are below your main application with the #SpringBootApplication annotation, you’re covered by implicit components scan.
Therefore, one simple solution can be done by following 2 steps:
Rename the package of main class to be com.bloodynacho.rishab.
(That is what I suggest that the complete package name of main app. is supposed to be root of other packages.)
Remove #ComponentScan and #EntityScan annotation.
(Although #ComponentScan is different from #EntityScan, it can be also removed in my experience.)

Error creating bean with name 'gemfireCache': FactoryBean threw exception on object creation

I am trying to create an "employee" Region and put some data into it. But, I am getting Exception below:
[warn 2018/12/27 17:15:46.518 IST tid=0x1] Exception
encountered during context initialization - cancelling refresh
attempt: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'gemfireConfiguration': Injection of
resource dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'gemfireCache': FactoryBean threw exception on
object creation; nested exception is java.lang.NoClassDefFoundError:
it/unimi/dsi/fastutil/ints/Int2ObjectOpenHashMap
[warn 2018/12/27 17:15:46.519 IST tid=0x1] Invocation of
destroy method failed on bean with name 'gemfireCache':
org.apache.geode.cache.CacheClosedException: A cache has not yet been
created.
[error 2018/12/27 17:15:46.522 IST tid=0x1] Caught exception
while allowing TestExecutionListener
[org.springframework.test.context.web.ServletTestExecutionListener#c667f46]
to prepare test instance
[com.gemfire.demo.Gemfire1ApplicationTests#48bfb884]
Domain class
#Region("employee")
public class Employee {
#Id
public String name;
public double salary;
...
}
Repository class
#Repository
public interface EmployeeRepository extends CrudRepository<Employee, String> {
Employee findByName(String name);
}
Configuration class
#Configuration
#ComponentScan
#EnableGemfireRepositories(basePackages = "com.gemfire.demo")
public class GemfireConfiguration {
#Autowired
EmployeeRepository employeeRepository;
#Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", "SpringDataGemFireApplication");
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", "config");
return gemfireProperties;
}
#Bean
#Autowired
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
#Bean(name="employee")
#Autowired
LocalRegionFactoryBean<String, Employee> getEmployee(final GemFireCache cache) {
LocalRegionFactoryBean<String, Employee> employeeRegion = new LocalRegionFactoryBean<String, Employee>();
employeeRegion.setCache(cache);
employeeRegion.setClose(false);
employeeRegion.setName("employee");
employeeRegion.setPersistent(false);
employeeRegion.setDataPolicy(DataPolicy.PRELOADED);
return employeeRegion;
}
}
POM.XML
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</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>org.springframework.data</groupId>
<artifactId>spring-data-gemfire</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.9.0</version>
</dependency>
Adding additional tips with your above GemFire/Spring JavaConfig configuration class above.
Given you are using Spring Data Kay (implied by your use of the Spring Boot 2.0.x parent POM, i.e. org.springframework.boot:spring-boot-dependencies; see here), then you could be using Spring Data GemFire's (relatively) new and convenient Annotation-based configuration model.
By doing so, your GemfireConfiguration class above would become...
#PeerCacheApplication
#EnableGemfireRepositories(basePackages = "com.gemfire.demo")
class GemfireConfiguration {
#Bean(name="employee")
LocalRegionFactoryBean<String, Employee> getEmployee(GemFireCache cache) {
LocalRegionFactoryBean<String, Employee> employeeRegion =
new LocalRegionFactoryBean<String, Employee>();
employeeRegion.setCache(cache);
employeeRegion.setClose(false);
employeeRegion.setDataPolicy(DataPolicy.PRELOADED);
return employeeRegion;
}
}
A few things to keep in mind:
#PeerCacheApplication is meta-annotated with #Configuration so you do not need the explicit Spring #Configuration annotation on the configuration class.
#PeerCacheApplication allows you to adjust the GemFire log-level (along with other logging configuration) using the logLevel annotation attribute. Similarly, you can set the log-level using the corresponding property, spring.data.gemfire.cache.log-level in a Spring Boot application.properties file (see here). There are many other attributes and corresponding properties (e.g. name) you can use to adjust and customize other configuration.
While String-based package names are supported on #EnableGemfireRepositories and similar annotations, we generally prefer and recommend users to use the type-safe variant basePacakgeClasses. You only need to refer to a single type from each top-level package where your application Repositories are kept.
The explicit #Autowired annotation is not needed on your bean definitions. You do not need to explicit inject the EmployeeRepository in the configuration class to have it initialized; just inject it into the #Service class where it will be used.
For convenience, the name ("employee") of the Region bean definition on your LOCAL "employee" Region, will also be used as the name of the Region, so employeeRegion.setName("employee") is unnecessary.
You should not combine LocalRegionFactoryBean.setPersistent(:boolean) with LocalRegionFactoryBean.setDataPolicy(:DataPolicy) since the DataPolicy is going to take precedence.
While #ComponentScan is perfectly acceptable and even convenient in development, I generally do not prefer nor recommend users to use component-scanning. It is usually always better to be explicit.
As stated in the comments, you chould remove <relativePath/> from your parent definition in your application Maven POM file.
Final note, as of this post, Spring Boot 2.0.8.RELEASE is the latest release.
As for your classpath issues, if you are using Maven correctly, then Maven should take care of pulling in the correct transitive dependencies.
You can refer to the many examples I have in this repo for further clarification.
Hope this helps!
As mentioned in comments, the error shows some dependencies (java.lang.NoClassDefFoundError: it/unimi/dsi/fastutil/ints/Int2ObjectOpenHashMap) are missing. Please add corresponding dependencies in your pom.xml

Springboot Junit test NoClassDefFoundError EntityManagerFactoryBuilderImpl

I created a springboot (2) webflux project as follow :
JPA Entity
#Entity
#Table(name = "users")
public class User implements Serializable
{
...
}
Spring repository
public interface UserRepository extends CrudRepository<User, Long>
{
}
Service
#Service
public class UserService
{
#Autowired
private UserRepository userRepo;
...
}
Webflux Handler
#Component
public class UserHandler
{
#Autowired
private UserService userService;
public Mono<ServerResponse> getUser(ServerRequest request)
{
...
}
}
RouteConfiguration
#Configuration
public class RouteConfiguration
{
#Bean
public static RouterFunction<ServerResponse> userRoutes(UserHandler userHandler)
{
return RouterFunctions.route(RequestPredicates.GET("/user"), userHandler:: getUser);
}
WebApp
#SpringBootApplication
public class WebApplication
{
public static void main(String[] args)
{
SpringApplication.run(WebApplication.class);
}
}
POM
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<!-- Provided -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- Runtime -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Test -->
<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>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Everything run fine, I can start my server and use it. I would like now to code some tests. Here is what I did :
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = WebApplication.class)
public class UserHandlerTest
{
#Autowired
private ApplicationContext context;
#MockBean
private UserService userService;
private WebTestClient testClient;
#Before
public void setUp()
{
testClient = WebTestClient.bindToApplicationContext(context).build();
}
#Test
public void testUser()
{
...
}
}
What ever I tried, I got an error with hibernate dependencies during "mvn clean install" process :
[ERROR] testUser(...UserHandlerTest) Time elapsed: 0 s <<< ERROR!
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl
I know JPA works in blocking way but I want to avoid to use NoSQL DB for this project. Did I miss something ? Thank you a lot for help !
Probably, you are missing details which we need to provide for datasource under src/main/resources. you can check https://github.com/hantsy/spring-reactive-sample/blob/master/boot-data-mongo/src/main/resources/application.yml. this might help you.
To test my Spring Webflux controllers, I finally use the WebFluxTest annotation. It works as expected :
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {RouteConfiguration.class, UserHandler.class})
#WebFluxTest
public class UserHandlerTest
{
#Autowired
private ApplicationContext context;
#MockBean(name="userService")
private UserService userService;
private WebTestClient testClient;
#Before
public void setUp()
{
testClient = WebTestClient.bindToApplicationContext(context).build();
}
...
As I do not use RestController annotation but functional endpoints I had to use ContextConfiguration and manually instantiate the WebTestClient.

Resources