Spring Boot 5 multiple JUnit JPA test files wiring - spring

I have two tests files in project.
One is testing directly my persistence layer :
#RunWith(SpringRunner.class)
#DataJpaTest
public class UserTests {
#Autowired
private TestEntityManager entityManager;
#Autowired
private UserRepository repository;
#Test
public void whenFindByEmail_thenReturnUser() {
// given
User user = new User("email#email.com", "12345678", "Some Name");
entityManager.persist(user);
entityManager.flush();
// when
User found = repository.findByEmail(user.getEmail());
// then
assertThat(found.getName()).isEqualTo(user.getName());
assertThat(found.getEmail()).isEqualTo(user.getEmail());
assertThat(found.getPasswordHash()).isEqualTo(user.getPasswordHash());
}
}
The other one is testing a service using the persistence layer :
#RunWith(SpringRunner.class)
#DataJpaTest
public class UserServiceTests {
#Autowired
private UserService service;
#Test
public void testSuccessfullUserCreation() {
UserCreationResult res = service.createUser("anything#anything.com", "1234567890", "Test");
assertThat(res).isEqualTo(UserCreationResult.OK);
}
#Test
public void testWrongEmailUserCreation() {
UserCreationResult res = service.createUser("anything#anything", "1234567890", "Test");
assertThat(res).isEqualTo(UserCreationResult.INVALID_EMAIL);
}
#Test
public void testTooShortPasswordUserCreation() {
String shortPassword =
String.join("", Collections.nCopies(UserService.minPasswordLength - 1, "0"));
UserCreationResult res = service.createUser("anything#anything.com", shortPassword, "Test");
assertThat(res).isEqualTo(UserCreationResult.WRONG_PASSWORD_LENGTH);
}
#Test
public void testTooLongPasswordUserCreation() {
String longPassword =
String.join("", Collections.nCopies(UserService.maxPasswordLength + 1, "0"));
UserCreationResult res = service.createUser("anything#anything.com", longPassword, "Test");
assertThat(res).isEqualTo(UserCreationResult.WRONG_PASSWORD_LENGTH);
}
#Test
public void testMaxLengthPasswordUserCreation() {
String maxPassword =
String.join("", Collections.nCopies(UserService.maxPasswordLength, "0"));
UserCreationResult res = service.createUser("anything#anything.com", maxPassword, "Test");
assertThat(res).isEqualTo(UserCreationResult.OK);
}
#Test
public void testMinLengthPasswordUserCreation() {
String minPassword =
String.join("", Collections.nCopies(UserService.minPasswordLength, "0"));
UserCreationResult res = service.createUser("anything#anything.com", minPassword, "Test");
assertThat(res).isEqualTo(UserCreationResult.OK);
}
#Test
public void testReservedEmailUserCreation() {
String email = "email#email.com";
UserCreationResult res = service.createUser(email, "1234567890", "Test");
assertThat(res).isEqualTo(UserCreationResult.OK);
res = service.createUser(email, "1234567890", "Test");
assertThat(res).isEqualTo(UserCreationResult.RESERVED_EMAIL);
}
}
First, my service autowiring wasn't working (UnsatisfiedDependencyException) so I had to add :
#ComponentScan("my.service.package") annotation to the test class.
This made the tests of UserServiceTests work when they were run independently (using eclipse to run only this class).
But when running all the tests of my app (in eclipse or with a good old mvn clean test), I had the same error on the same test class.
I tried adding the same component scan annotation to the other test class (UserTests) then everything worked.
I removed the component scan annotation from UserServiceTests and it still works.
I obviously deduced that the order the tests are executed matters.
Here are my 3 questions, the real one being the last one :
In the first place why do I have to put this component scan annotation even if my class is properly annotated #Service (so should be detected as a bean) ?
How is it that the order of tests matters ?
How can I have multiple JPA tests files that would run with a proper dependency injection independently ?
Here is my service class :
#Service
public class UserService {
#Autowired
private UserRepository repository;
public static final int minPasswordLength = 8;
public static final int maxPasswordLength = 50;
public static enum UserCreationResult {
OK, INVALID_EMAIL, RESERVED_EMAIL, WRONG_PASSWORD_LENGTH, UNKNOWN_ERROR
}
#Transactional
public UserCreationResult createUser(String email, String password, String name) {
if (password.length() < minPasswordLength || password.length() > maxPasswordLength) {
return UserCreationResult.WRONG_PASSWORD_LENGTH;
}
if (!EmailValidator.getInstance().isValid(email)) {
return UserCreationResult.INVALID_EMAIL;
}
final User existingUser = repository.findByEmail(email);
if (existingUser != null) {
return UserCreationResult.RESERVED_EMAIL;
}
final User user = repository.save(new User(email, password, name));
return user == null ? UserCreationResult.UNKNOWN_ERROR : UserCreationResult.OK;
}
}
And there is my pom.xml :
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.somedomain</groupId>
<artifactId>ws-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>My App</name>
<description>My app's description</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

Thanks to this question and answer I was able to make my tests work with proper configuration.
In my UserServiceTests, the service was basically not autowired because that's the expected behavior of #DataJpaTest : it doesn't scan for regular beans.
So I used #SpringBootTest for this class and removed the component scanning in both test classes.
After that, some of my service tests were failing because with #SpringBootTest, the database is not reset after each test.
I added a simple repository cleaning after each service test and everything works fine.
This question still remains :
How is it that the order of tests matters ?
Here are my new test classes :
Service tests :
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserServiceTests {
#Autowired
private UserService service;
#Autowired
private UserRepository repository;
#After
public void cleanUsers() {
repository.deleteAll();
}
#Test
public void testSuccessfullUserCreation() {
UserCreationResult res = service.createUser("anything#anything.com", "1234567890", "Test");
assertThat(res).isEqualTo(UserCreationResult.OK);
}
#Test
public void testWrongEmailUserCreation() {
UserCreationResult res = service.createUser("anything#anything", "1234567890", "Test");
assertThat(res).isEqualTo(UserCreationResult.INVALID_EMAIL);
}
#Test
public void testTooShortPasswordUserCreation() {
String shortPassword =
String.join("", Collections.nCopies(UserService.minPasswordLength - 1, "0"));
UserCreationResult res = service.createUser("anything#anything.com", shortPassword, "Test");
assertThat(res).isEqualTo(UserCreationResult.WRONG_PASSWORD_LENGTH);
}
#Test
public void testTooLongPasswordUserCreation() {
String longPassword =
String.join("", Collections.nCopies(UserService.maxPasswordLength + 1, "0"));
UserCreationResult res = service.createUser("anything#anything.com", longPassword, "Test");
assertThat(res).isEqualTo(UserCreationResult.WRONG_PASSWORD_LENGTH);
}
#Test
public void testMaxLengthPasswordUserCreation() {
String maxPassword =
String.join("", Collections.nCopies(UserService.maxPasswordLength, "0"));
UserCreationResult res = service.createUser("anything#anything.com", maxPassword, "Test");
assertThat(res).isEqualTo(UserCreationResult.OK);
}
#Test
public void testMinLengthPasswordUserCreation() {
String minPassword =
String.join("", Collections.nCopies(UserService.minPasswordLength, "0"));
UserCreationResult res = service.createUser("anything#anything.com", minPassword, "Test");
assertThat(res).isEqualTo(UserCreationResult.OK);
}
#Test
public void testReservedEmailUserCreation() {
String email = "email#email.com";
UserCreationResult res = service.createUser(email, "1234567890", "Test");
assertThat(res).isEqualTo(UserCreationResult.OK);
res = service.createUser(email, "1234567890", "Test");
assertThat(res).isEqualTo(UserCreationResult.RESERVED_EMAIL);
}
}
JPA tests :
#RunWith(SpringRunner.class)
#DataJpaTest
public class UserTests {
#Autowired
private TestEntityManager entityManager;
#Autowired
private UserRepository repository;
#Test
public void whenFindByEmail_thenReturnUser() {
// given
User user = new User("user#user.com", "12345678", "Some Name");
entityManager.persist(user);
entityManager.flush();
// when
User found = repository.findByEmail(user.getEmail());
// then
assertThat(found.getName()).isEqualTo(user.getName());
assertThat(found.getEmail()).isEqualTo(user.getEmail());
assertThat(found.getPasswordHash()).isEqualTo(user.getPasswordHash());
}
}

Related

AspectJ (Spring) + how to add the embedding of an aspect in a private method and get the specified data

I have created an aspect that should embed logging on methods marked with an annotation and with the private modifier.
In addition, I would like to add information to the log that will be available at the time of execution of the method (for example, the object with which the method works and the name of the method and the class with which it is currently working).
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</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</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
UserController
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
#GetMapping
public List<User> getUsers() {
List<User> userList = getUsersInternal();
return userList;
}
#AuditAnnotation()
private List<User> getUsersInternal() {
List<User> allUsers = userService.getAllUsers();
return allUsers;
}
}
annotation
#Retention(RUNTIME)
#Target(METHOD)
#Documented
public #interface AuditAnnotation {
public String nameMethod() default "";
}
loggingService
public interface LoggingService {
void log(String message);
}
/**
* A dummy implementation of logging service,
* just to inject it in {#link com.aspectj.in.spring.boot.aop.aspect.auditlog.interceptor.LoggingInterceptorAspect}
* that's managed by AspectJ
*/
#Service
public class DefaultLoggingService implements LoggingService {
private static final Logger logger = LoggerFactory.getLogger("sample-spring-aspectj");
#Override
public void log(String message) {
logger.info(message);
}
}
aspect
#Aspect
#Component
public class LoggingInterceptorAspect {
#Autowired
private LoggingService loggingService;
#Pointcut("execution(private * *(..))")
public void privateMethod() {}
#Pointcut("#annotation(com.aspectj.in.spring.boot.aop.aspect.auditlog.annotation.AuditAnnotation)")
public void annotatedMethodCustom() {}
#Before("annotatedMethodCustom() && privateMethod()")
public void addCommandDetailsToMessage() throws Throwable {
ZonedDateTime dateTime = ZonedDateTime.now(ZoneOffset.UTC);
String message = String.format("User controller getUsers method called at %s", dateTime);
System.out.println("+++++++++++++++++++++++++");
loggingService.log(message);
}
}
configuratinAspect
#Configuration
public class LoggingInterceptorConfig {
#Bean
public LoggingInterceptorAspect getAutowireCapableLoggingInterceptor() {
return Aspects.aspectOf(LoggingInterceptorAspect.class);
}
}
test
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class AspectjInSpringBootApplicationTests {
#Autowired
protected TestRestTemplate testRestTemplate;
}
class UserControllerTest extends AspectjInSpringBootApplicationTests {
#Test
void getUsers() {
String url = "/v1/users";
ParameterizedTypeReference<List<User>> typeReference =
new ParameterizedTypeReference<>() {
};
ResponseEntity<List<User>> responseEntity =
testRestTemplate.exchange(url, HttpMethod.GET, null, typeReference);
HttpStatus statusCode = responseEntity.getStatusCode();
assertThat(statusCode, is(HttpStatus.OK));
List<User> employeeDtoList = responseEntity.getBody();
System.out.println(employeeDtoList);
}
}
But at the moment I have no errors .
so far, I see that the aspect is embedded,
but I want it to be detailed so that the aspect is universal and I would not have to explicitly specify in the message in which class it works.
Maybe someone has ideas on how to fix it.
I suggest you to explore the AspectJ documentation and the JoinPoint API, too. Here is a little example in stand-alone AspectJ without Spring. You can adjust it to your needs:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
Application application = new Application();
System.out.println(application.add(4, application.multiply(2, 3)));
application.divide(5, 0);
}
private int add(int i, int j) {
return i + j;
}
private int multiply(int i, int j) {
return i * j;
}
private double divide(int i, int j) {
return i / j;
}
}
package de.scrum_master.aspect;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class MyAspect {
#Before("execution(private * *(..))")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
System.out.println(" Signature: " + joinPoint.getSignature());
System.out.println(" Target: " + joinPoint.getTarget());
System.out.println(" Arguments: " + Arrays.deepToString(joinPoint.getArgs()));
}
#AfterReturning(pointcut = "execution(private * *(..))", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
System.out.println(" Result: " + result);
}
#AfterThrowing(pointcut = "execution(private * *(..))", throwing = "exception")
public void afterThrowingAdvice(JoinPoint joinPoint, Throwable exception) {
System.out.println(" Exception: " + exception);
}
}
Console log:
Picked up _JAVA_OPTIONS: -Djava.net.preferIPv4Stack=true
execution(int de.scrum_master.app.Application.multiply(int, int))
Signature: int de.scrum_master.app.Application.multiply(int, int)
Target: de.scrum_master.app.Application#7cdbc5d3
Arguments: [2, 3]
Result: 6
execution(int de.scrum_master.app.Application.add(int, int))
Signature: int de.scrum_master.app.Application.add(int, int)
Target: de.scrum_master.app.Application#7cdbc5d3
Arguments: [4, 6]
Result: 10
10
execution(double de.scrum_master.app.Application.divide(int, int))
Signature: double de.scrum_master.app.Application.divide(int, int)
Target: de.scrum_master.app.Application#7cdbc5d3
Arguments: [5, 0]
Exception: java.lang.ArithmeticException: / by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
at de.scrum_master.app.Application.divide(Application.java:19)
at de.scrum_master.app.Application.main(Application.java:7)
In addition to the context data I printed here, you can also get annotations and their properties, method parameter names (even though I think that is unnecessary and availability depends on compilation options) etc.

Error creating Bean with name EntityManagerFactory

Hello every body i'm developping an authentication service using spring security & mysql database and ia have this error when running my springboot app
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]
Here is my 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-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.0.7.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
and this is my SecurityConfig.java
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#EnableJpaRepositories(basePackageClasses = UsersRepository.class)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyUserDetailsService myUserDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService)
.passwordEncoder(getPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("**/admin/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin().permitAll();
}
private PasswordEncoder getPasswordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
#Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
};
}
}
this is my Users.java
#Entity
#Table(name = "user")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
#Column(name = "login")
private String login;
#Column(name = "password")
private String password;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles;
public Users() {
}
public Users(Users users){
this.id=users.getId();
this.login=users.getLogin();
this.password=users.getPassword();
this.roles=users.getRoles();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
this is the roles.java
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private int roleId;
#Column(name = "role")
private String role;
public Role() {
}
public int getRoleId() {
return roleId;
}
public void setRoleId(int roleId) {
this.roleId = roleId;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
this is my UsersRepository.java
#Repository
public interface UsersRepository extends JpaRepository<Users, Integer> {
Optional<Users> findByName(String username);
}
this is MyUserDetailsService.java
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UsersRepository usersRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Users> optionalUsers = usersRepository.findByName(username);
optionalUsers
.orElseThrow(() -> new UsernameNotFoundException("Utilisateur Introuvable!"));
return optionalUsers
.map(MyUserDetails::new).get();
}
}
this is MyUserDetails.java
public class MyUserDetails extends Users implements UserDetails {
public MyUserDetails(final Users users) {
super(users);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles()
.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRole()))
.collect(Collectors.toList());
}
#Override
public String getPassword() {
return super.getPassword();
}
#Override
public String getUsername() {
return super.getLogin();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
and finally my controller of ressources AuthenticationCOntroller.java
#RequestMapping("/api")
#RestController
public class AuthentificationController {
#GetMapping(value = "/login")
public String hello() {
return "Hello World";
}
#PreAuthorize("hasAnyRole('ADMIN')")
#GetMapping("/admin/login")
public String helloAdmin() {
return "Hello ADMIN";
}
#GetMapping("/login/alternate")
public String alternate() {
return "alternate";
}
}
this is application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password =
spring.datasource.testWhileIdle=true
spring.datasource.validationQuery=SELECT 1
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
I'm using and MYSQL database it is well connected and the tables are created with some values.
Thank you and waiting for you answers.
if you are using java version 9+, add following too and remove hibernate core dependency as its already included in spring boot jpa starter.
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
Remove the Hibernate dependency because spring-boot-starter-data-jpa already has it.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.0.7.Final</version>
</dependency>
Hope this works :)
There is a conflict in pom.xml dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
spring-boot-starter-jpa has hibernate-core dependency included.
It is possible to view the dependency graph of a project like so:
mvn dependency:tree
mvn dependency:list
These commands will show what version of Hibernate included in the compilation.
To fix the UnsatisfiedDependencyException of entityManagerFactory bean, simply delete hibernate-core dependency from the pom.xml
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.0.7.Final</version>
</dependency>

javax.servlet.ServletException: Circular view path [products]

I am new in Java Spring 4.0. Here is my project set up.
ProductController
#Controller
public class ProductController {
private ProductService productService;
#Autowired
public void setProductService(ProductService productService) {
this.productService = productService;
}
#RequestMapping("/products")
public String listProducts(Model model){
model.addAttribute("products",productService.listAllProducts());
return "products";
}
#RequestMapping("product/{id}")
public String getProduct(#PathVariable Integer id, Model model){
model.addAttribute("product", productService.getProductById(id));
return "product";
}
#RequestMapping("product/edit/{id}")
public String edit(#PathVariable Integer id, Model model){
model.addAttribute("product",productService.getProductById(id));
return "productform";
}
#RequestMapping("/product/new")
public String newProduct(Model model){
model.addAttribute("product",new Product());
return "productform";
}
#RequestMapping(value = "/product", method = RequestMethod.POST)
public String saveOrUpdateProduct(Product product){
Product saveProduct = productService.saveOrUpdateProduct(product);
return "redirect:/product/" + saveProduct.getId();
}
#RequestMapping("/product/delete/{id}")
public String delete(#PathVariable Integer id){
productService.deleteProduct(id);
return "redirect:/products";
}
}
ProductServiceImpl
#Service
public class ProductServiceImpl implements ProductService {
Map<Integer, Product> products;
public ProductServiceImpl(){
loadProducts();
}
#Override
public List<Product> listAllProducts() {
return new ArrayList<>(products.values());
}
#Override
public Product getProductById(Integer id) {
return products.get(id);
}
#Override
public Product saveOrUpdateProduct(Product product) {
if (product != null){
if (product.getId() == null){
product.setId(getNextKey());
}
products.put(product.getId(), product);
return product;
} else {
throw new RuntimeException("Product Can't be nill");
}
}
#Override
public void deleteProduct(Integer id) {
products.remove(id);
}
private Integer getNextKey(){
return Collections.max(products.keySet()) + 1;
}
private void loadProducts(){
products = new HashMap<>();
Product product1 = new Product();
product1.setId(1);
product1.setDescription("Product 1");
product1.setPrice(new BigDecimal("12.99"));
product1.setImageUrl("http://example.com/product1");
products.put(1,product1);
Product product2 = new Product();
product2.setId(2);
product2.setDescription("Product 2");
product2.setPrice(new BigDecimal("14.99"));
product2.setImageUrl("http://example.com/product2");
products.put(2, product2);
Product product3 = new Product();
product3.setId(3);
product3.setDescription("Product 3");
product3.setPrice(new BigDecimal("34.99"));
product3.setImageUrl("http://example.com/product3");
products.put(3, product3);
Product product4 = new Product();
product4.setId(4);
product4.setDescription("Product 4");
product4.setPrice(new BigDecimal("44.99"));
product4.setImageUrl("http://example.com/product4");
products.put(4, product4);
Product product5 = new Product();
product5.setId(5);
product5.setDescription("Product 5");
product5.setPrice(new BigDecimal("25.99"));
product5.setImageUrl("http://example.com/product5");
products.put(5, product5);
}
}
So, I am injecting a CRUD service to the controller and works good. I can post, delete, display and update data. Now I want to make mockito test. So.
ProductControllerTest
public class ProductControllerTest {
#Mock
private ProductService productService;
#InjectMocks
private ProductController productController;
private MockMvc mockMvc;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(productController).build();
}
#Test
public void testList() throws Exception{
List<Product> products = new ArrayList<>();
products.add(new Product());
products.add(new Product());
when(productService.listAllProducts()).thenReturn((List)products);
mockMvc.perform(get("/products"))
.andExpect(status().isOk())
.andExpect(view().name("products"))
.andExpect(model().attribute("products",hasSize(2)));
}
}
Unfortunately, the test doesn't pass.
javax.servlet.ServletException: Circular view path [products]: would
dispatch back to the current handler URL [/products] again. Check your
ViewResolver setup! (Hint: This may be the result of an unspecified
view, due to default view name generation.)
Why is this happening? I mean I can display the products in my browswer, but the unit test fails.
I am using spring boot and the thymeleaf template.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>theo.tziomakas</groupId>
<artifactId>springmvc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springmvc</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Please try to add #ResponseBody annotation on methods of ProductController with #RequestMapping as shown below.
#RequestMapping("/products")
#ResponseBody
public String listProducts(Model model){
model.addAttribute("products",productService.listAllProducts());
return "products";
}
Hope it would help.

Cucumber: no backend found when running from Spring Boot jar

I am creating a small testing framework that should utilize both Cucumber and the Spring Boot platform. The idea is to let the whole application be packaged as a single jar and run after the BDD features have been properly parametrized.
The framework starts in command line runner mode like this:
public class FwApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(FwApplication.class, args);
}
#Override
public void run(String... arg0) throws Exception {
JUnitCore.main(CucumberIntegration.class.getCanonicalName());
}
}
Then there is the CucumberIntegration class:
#RunWith(Cucumber.class)
#CucumberOptions(features = "config/features")
#ContextConfiguration(classes= AppConfiguration.class)
public class CucumberIntegration {
}
I have also some simple tests which run fine under my IDE, but when I try to package the application and run it over java -jar fw-0.0.1-SNAPSHOT.jar I get to see following:
There was 1 failure:
1) initializationError(com.fmr.bddfw.test.CucumberIntegration)
cucumber.runtime.CucumberException: No backends were found. Please make sure you have a backend module on your CLASSPATH.
at cucumber.runtime.Runtime.<init>(Runtime.java:81)
at cucumber.runtime.Runtime.<init>(Runtime.java:70)
(...)
All necessary jars are already in the one jar created by maven and it works fine under my IDE.
Any ideas what could help?
EDIT: Here my pom 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxx</groupId>
<artifactId>fw</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>fw</name>
<description>BDD Testing Framework</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<cucumber.version>1.2.5</cucumber.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>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-spring</artifactId>
<version>${cucumber.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Using the suggestion given by Marcus:
Step1: Create your custom MultiLoader class:
package cucumber.runtime.io;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
public class CustomMultiLoader implements ResourceLoader {
public static final String CLASSPATH_SCHEME = "classpath*:";
public static final String CLASSPATH_SCHEME_TO_REPLACE = "classpath:";
private final ClasspathResourceLoader classpath;
private final FileResourceLoader fs;
public CustomMultiLoader(ClassLoader classLoader) {
classpath = new ClasspathResourceLoader(classLoader);
fs = new FileResourceLoader();
}
#Override
public Iterable<Resource> resources(String path, String suffix) {
if (isClasspathPath(path)) {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
String locationPattern = path.replace(CLASSPATH_SCHEME_TO_REPLACE, CLASSPATH_SCHEME) + "/**/*" + suffix;
org.springframework.core.io.Resource[] resources;
try {
resources = resolver.getResources(locationPattern);
} catch (IOException e) {
resources = null;
e.printStackTrace();
}
return convertToCucumberIterator(resources);
} else {
return fs.resources(path, suffix);
}
}
private Iterable<Resource> convertToCucumberIterator(org.springframework.core.io.Resource[] resources) {
List<Resource> results = new ArrayList<Resource>();
for (org.springframework.core.io.Resource resource : resources) {
results.add(new ResourceAdapter(resource));
}
return results;
}
public static String packageName(String gluePath) {
if (isClasspathPath(gluePath)) {
gluePath = stripClasspathPrefix(gluePath);
}
return gluePath.replace('/', '.').replace('\\', '.');
}
private static boolean isClasspathPath(String path) {
if (path.startsWith(CLASSPATH_SCHEME_TO_REPLACE)) {
path = path.replace(CLASSPATH_SCHEME_TO_REPLACE, CLASSPATH_SCHEME);
}
return path.startsWith(CLASSPATH_SCHEME);
}
private static String stripClasspathPrefix(String path) {
if (path.startsWith(CLASSPATH_SCHEME_TO_REPLACE)) {
path = path.replace(CLASSPATH_SCHEME_TO_REPLACE, CLASSPATH_SCHEME);
}
return path.substring(CLASSPATH_SCHEME.length());
}
}
Step2: Create an adapter between org.springframework.core.io.Resource and cucumber.runtime.io.Resource:
package cucumber.runtime.io;
import java.io.IOException;
import java.io.InputStream;
public class ResourceAdapter implements Resource {
org.springframework.core.io.Resource springResource;
public ResourceAdapter(org.springframework.core.io.Resource springResource) {
this.springResource = springResource;
}
public String getPath() {
try {
return springResource.getFile().getPath();
} catch (IOException e) {
try {
return springResource.getURL().toString();
} catch (IOException e1) {
e1.printStackTrace();
return "";
}
}
}
public String getAbsolutePath() {
try {
return springResource.getFile().getAbsolutePath();
} catch (IOException e) {
return null;
}
}
public InputStream getInputStream() throws IOException {
return this.springResource.getInputStream();
}
public String getClassName(String extension) {
String path = this.getPath();
if (path.startsWith("jar:")) {
path = path.substring(path.lastIndexOf("!") + 2);
return path.substring(0, path.length() - extension.length()).replace('/', '.');
} else {
path = path.substring(path.lastIndexOf("classes") + 8);
return path.substring(0, path.length() - extension.length()).replace('\\', '.');
}
}
}
Step3: Create your custom main class that uses your CustomMultiLoader:
package cucumber.runtime.io;
import static java.util.Arrays.asList;
import java.io.IOException;
import java.util.ArrayList;
import cucumber.runtime.ClassFinder;
import cucumber.runtime.Runtime;
import cucumber.runtime.RuntimeOptions;
public class CucumberStaticRunner {
public static void startTests(String[] argv) throws Throwable {
byte exitstatus = run(argv, Thread.currentThread().getContextClassLoader());
System.exit(exitstatus);
}
public static byte run(String[] argv, ClassLoader classLoader) throws IOException {
RuntimeOptions runtimeOptions = new RuntimeOptions(new ArrayList<String>(asList(argv)));
ResourceLoader resourceLoader = new CustomMultiLoader(classLoader);
ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
Runtime runtime = new Runtime(resourceLoader, classFinder, classLoader, runtimeOptions);
runtime.run();
return runtime.exitStatus();
}
}
Step4: Call your custom main class instead of cucumber.api.cli.Main.main:
String[] cucumberOptions = { "--glue", "my.test.pack", "--no-dry-run", "--monochrome", "classpath:features" };
CucumberStaticRunner.startTests(cucumberOptions);
Fixed it by following configuration:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<requiresUnpack>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
</dependency>
</requiresUnpack>
</configuration>
</plugin>
You should add cucumber-java dependency
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
</dependency>
private static byte run(String[] argv, ClassLoader classLoader) throws IOException {
// cucumber/Spring Boot classloader problem
// CucumberException: No backends were found. Please make sure you have a backend module on your CLASSPATH
RuntimeOptions runtimeOptions = new RuntimeOptions(new ArrayList<String>(asList(argv)));
ResourceLoader resourceLoader = new MultiLoader(classLoader);
ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
Reflections reflections = new Reflections(classFinder);
List<Backend> list = new ArrayList<>();
list.addAll(reflections.instantiateSubclasses(Backend.class, "cucumber.runtime", new Class[]{ResourceLoader.class}, new Object[]{resourceLoader}));
if (list.size() == 0) {
JavaBackend javaBackend = new JavaBackend(resourceLoader);
list.add(javaBackend);
}
Runtime runtime = new Runtime(resourceLoader, classLoader, list, runtimeOptions);
runtime.run();
return runtime.exitStatus();
}
I used below code in POM.xml file
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>1.2.4</version>
<scope>test</scope>
</dependency>
It is working fine now.

Hystrix Javanica fallback not working in Spring Cloud 1.0

I built a super simple Hystrix short circuit example based on #spencergibb feign-eureka spring cloud starter example. At first I thought I couldn't get the hystrix javanica default fallbackMethod to trigger due to feign.. now, removing feign, the hystrix default fallbackMethod still doesn't catch exceptions.
pom.xml
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>1.0.0.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
:
</dependencies>
Main file:
#SpringBootApplication
#EnableDiscoveryClient
#EnableHystrix
#RestController
public class HelloClientApplication {
#Autowired
HelloClientComponent helloClientComponent;
#RequestMapping("/")
public String hello() {
return helloClientComponent.makeMultipleCalls();
}
public static void main(String[] args) {
SpringApplication.run(HelloClientApplication.class, args);
}
}
HelloClientComponent.java (created because I know javanica expects to be inside a spring managed component or service):
#Component
public class HelloClientComponent {
#Autowired
RestTemplate restTemplate;
public String makeMultipleCalls() {
int cnt=20;
StringBuilder sb = new StringBuilder();
while (cnt-- > 0) {
String response = theServerRequestRoot();
sb.append(response).append(" ");
}
return sb.toString();
}
public String theServersRequestRootFallback() {
System.out.println("BOMB!!!!!!");
return "BOMB!!!!!!";
}
#HystrixCommand(fallbackMethod = "theServersRequestRootFallback", commandKey = "callToServers")
public String theServerRequestRoot() {
ResponseEntity<String> result = restTemplate.getForEntity("http://HelloServer", String.class);
System.out.println(result.getBody());
return result.getBody();
}
}
I start 2 servers, one which always succeeds and responds, and the other will fail 30% of the time with a 500 error. When I curl this client (to '/') things go normally for the non-forced failure calls. Round robining works fine as well. When the second server does return the 500 error, the fallbackMethod does not get called and the curl to '/' ends and returns with an error.
Update with solution per Spencer and Dave's suggestions. Change to the following:
Main Application file:
#SpringBootApplication
#EnableDiscoveryClient
#EnableHystrix
#RestController
public class HelloClientApplication {
#Autowired
HelloClientComponent helloClientComponent;
#RequestMapping("/")
public String hello() {
int cnt=20;
StringBuilder sb = new StringBuilder();
while (cnt-- > 0) {
String response = helloClientComponent.theServerRequestRoot(); // call directly to #Component in order for #HystrixCommand to intercept via AOP
sb.append(response).append(" ");
}
return sb.toString();
}
public static void main(String[] args) {
SpringApplication.run(HelloClientApplication.class, args);
}
}
HelloClientComponent.java:
#Component
public class HelloClientComponent {
#Autowired
RestTemplate restTemplate;
public String theServersRequestRootFallback() {
System.out.println("BOMB!!!!!!");
return "BOMB!!!!!!";
}
#HystrixCommand(fallbackMethod = "theServersRequestRootFallback", commandKey = "callToServers")
public String theServerRequestRoot() {
ResponseEntity<String> result = restTemplate.getForEntity("http://HelloServer", String.class);
System.out.println(result.getBody());
return result.getBody();
}
}
#HystrixCommand only works because Spring makes a proxy to call that method on. If you call the method from within the proxy it doesn't go through the interceptor. You need to call the #HystrixCommand from another #Component (or use AspectJ).

Resources