So i've recently been hitting my head against the wall because of a problem I have when I try to unit test my services.
I have a User which has multiple unidirectional relatshionships to the Product, Customer and Quotation classes. I use SpringBoot with the Spring JPA repositories to perform opperations to the MySQL database using Hibernate as ORM.
I also have different services which call the repositories to perform these actions to the database, and it are these that I am trying to unit test.
When I run my unit test classes seperately They all succeed without problems, but when I run them all together the QuotationService tests fail completely. Noteworthy however is that they always run after the CustomerService tests.
After looking at the stacktrace I found out that every test fails because of a foreign key constraint when I try to save a quotation to the database in the testclass.
Caused by: java.sql.SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`kmobeheerdb`.`quotation`, CONSTRAINT `FK_fq5x6je1sicnskcj35yqhp85g` FOREIGN KEY (`quotation_id`) REFERENCES `user` (`user_id`))
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:683)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:663)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:653)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:108)
at com.mysql.cj.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2061)
at com.mysql.cj.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1819)
at com.mysql.cj.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2033)
at com.mysql.cj.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:1969)
at com.mysql.cj.jdbc.PreparedStatement.executeLargeUpdate(PreparedStatement.java:4953)
at com.mysql.cj.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1954)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208)
... 105 more
The part of the tests where they always fail is in the @Before section, when I try to save a new quotation.
@Autowired private QuotationService quotationService;
@Autowired private UserService userService;
private User user;
private Quotation quotation;
@Before
public void setup() throws UserServiceException, QuotationServiceException {
String userEmail = "tzestermail@mail.com";
String userName = "testername";
User newUser = new User(userEmail, userName);
user = userService.register(newUser);
//Tests fail on the createQuotation statement beneath
Quotation newQuotation = new Quotation();
quotation = quotationService.createQuotation(newQuotation, user.getUserId());
user = userService.getUser(user.getUserId());
assertNotNull(user);
assertNotNull(quotation);
assertEquals(1, user.getQuotations().size());
}
I searched around a lot to try and solve this issue, including making the relations bidirectional, but nothing really worked and the error persisted.
For reference:
User class:
@Entity
@Table(name = "user", uniqueConstraints = {
@UniqueConstraint(name = "UX_user_email", columnNames = {"email"})
})
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "user_id", nullable = false)
private int userId;
private String email;
private String password;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "productId", orphanRemoval = true)
private Set<Product> products;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "quotationId", orphanRemoval = true)
private Set<Quotation> quotations;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "customerId", orphanRemoval = true)
private Set<Customer> customers;
public User(String email, String password) {
this.email = email;
this.password = password;
}
public User() {
}
public void addCustomer(Customer customer){
if(customer != null){
if(customers == null){
customers = new HashSet<>();
}
customers.add(customer);
}
}
public void removeCustomer(Customer customer){
if(customer != null && customers.contains(customer)){
customers.remove(customer);
}
}
public void addQuotation(Quotation quotation){
if(quotation != null){
if(quotations == null){
quotations = new HashSet<>();
}
quotations.add(quotation);
}
}
public void removeQuotation(Quotation quotation){
if(quotation != null && quotations.contains(quotation)){
quotations.remove(quotation);
}
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Set<Product> getProducts() {
return products;
}
public void setProducts(Set<Product> products) {
this.products = products;
}
public Set<Quotation> getQuotations() {
return quotations;
}
public void setQuotations(Set<Quotation> quotations) {
this.quotations = quotations;
}
public Set<Customer> getCustomers() {
return customers;
}
public void setCustomers(Set<Customer> customers) {
this.customers = customers;
}
}
Customer class:
@Entity
@Table(name = "customer", uniqueConstraints = {
@UniqueConstraint(name = "UX_customer_email", columnNames = {"email"})
})
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "customer_id", nullable = false)
private int customerId;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "quotationId", cascade = CascadeType.REMOVE)
private List<Quotation> quotations;
@Column(nullable = false)
private String email;
private String name;
public Customer() {
}
public Customer(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public List<Quotation> getQuotations() {
return quotations;
}
public void setQuotations(List<Quotation> quotations) {
this.quotations = quotations;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Quotation class:
@Entity
public class Quotation {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "quotation_id", nullable = false)
private int quotationId;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "productId", cascade = CascadeType.PERSIST)
private Set<Product> products;
private double routeTotal;
private double total;
private String memo;
public Quotation() {
}
public int getQuotationId() {
return quotationId;
}
public void setQuotationId(int quotationId) {
this.quotationId = quotationId;
}
public Set<Product> getProducts() {
return products;
}
public void setProducts(Set<Product> products) {
this.products = products;
}
public double getRouteTotal() {
return routeTotal;
}
public void setRouteTotal(double routeTotal) {
this.routeTotal = routeTotal;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
}
User Service
@Transactional(rollbackFor = UserServiceException.class)
@Service
public class UserServiceImpl implements UserService {
private Logger logger = LogManager.getLogger();
@Autowired private PasswordEncoder passwordEncoder;
@Autowired private UserRepository userRepository;
@Autowired private ExceptionStrings exceptionStrings;
@Override
public User register(User user) throws UserServiceException{
if(user == null || Strings.isNullOrEmpty(user.getEmail()) || Strings.isNullOrEmpty(user.getPassword())){
logger.error(exceptionStrings.registerInputError);
throw new UserServiceException(exceptionStrings.registerInputError);
}
try{
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
} catch (DataIntegrityViolationException e){
logger.error(exceptionStrings.registerError, e);
throw new UserServiceException(exceptionStrings.registerError, e);
}
}
@Override
public User login(User user) throws UserServiceException {
if(user == null || Strings.isNullOrEmpty(user.getEmail()) || Strings.isNullOrEmpty(user.getPassword())){
logger.error(exceptionStrings.loginInputError);
throw new UserServiceException(exceptionStrings.loginInputError);
}
User checkUser = userRepository.findByEmail(user.getEmail());
if(checkUser == null || !passwordEncoder.matches(user.getPassword(), checkUser.getPassword())){
logger.error(exceptionStrings.loginError);
throw new UserServiceException(exceptionStrings.loginError);
}
checkUser.setPassword(null);
return checkUser;
}
@Override
public User updateUser(User user) throws UserServiceException {
if(user == null || user.getUserId() == 0 || userRepository.findOne(user.getUserId()) == null){
logger.error(exceptionStrings.updateUserInputError);
throw new UserServiceException(exceptionStrings.updateUserInputError);
}
return userRepository.save(user);
}
@Override
public void deleteUser(User user) throws UserServiceException {
if(user == null || user.getUserId() == 0 || userRepository.findOne(user.getUserId()) == null){
logger.error(exceptionStrings.deleteUserInputError);
throw new UserServiceException(exceptionStrings.deleteUserInputError);
}
userRepository.delete(user);
}
@Override
public User getUser(int userId) {
return userRepository.findOne(userId);
}
}
Quotation Service:
@Transactional(rollbackFor = QuotationServiceException.class)
@Service
public class QuotationServiceImpl implements QuotationService {
private Logger logger = LogManager.getLogger();
@Autowired private ExceptionStrings exceptionStrings;
@Autowired private UserService userService;
@Autowired private QuotationRepository quotationRepository;
@Override
public Quotation createQuotation(Quotation quotation, int userId) throws QuotationServiceException {
if (quotation == null || userId == 0 || userService.getUser(userId) == null) {
logger.error(exceptionStrings.createQuotationInputError);
throw new QuotationServiceException(exceptionStrings.createQuotationInputError);
}
try {
User user = userService.getUser(userId);
user.addQuotation(quotation);
return quotationRepository.save(quotation);
} catch (DataIntegrityViolationException e) {
logger.error(exceptionStrings.createQuotationError);
throw new QuotationServiceException(exceptionStrings.createQuotationError, e);
}
}
@Override
public Quotation getQuotation(int quotationId) {
return quotationRepository.findOne(quotationId);
}
@Override
public Quotation updateQuotation(Quotation quotation) throws QuotationServiceException {
if (quotation == null || quotation.getQuotationId() == 0 || quotationRepository.findOne(quotation.getQuotationId()) == null) {
logger.error(exceptionStrings.updateQuotationInputError);
throw new QuotationServiceException(exceptionStrings.updateQuotationInputError);
}
return quotationRepository.save(quotation);
}
@Override
public void deleteQuotation(Quotation quotation) throws QuotationServiceException {
if (quotation == null || quotation.getQuotationId() == 0 || quotationRepository.findOne(quotation.getQuotationId()) == null) {
logger.error(exceptionStrings.updateQuotationInputError);
throw new QuotationServiceException(exceptionStrings.updateQuotationInputError);
}
quotationRepository.delete(quotation);
}
}
Customer Service:
@Transactional(rollbackFor = CustomerServiceException.class)
@Service
public class CustomerServiceImpl implements CustomerService {
private Logger logger = LogManager.getLogger();
@Autowired private CustomerRepository customerRepository;
@Autowired private UserService userService;
@Autowired private ExceptionStrings exceptionStrings;
@Override
public Customer createCustomer(Customer customer, int userId) throws CustomerServiceException {
if(customer == null || userService.getUser(userId) == null ||
customerRepository.findByEmail(customer.getEmail()) != null){
logger.error(exceptionStrings.createCustomerInputError);
throw new CustomerServiceException(exceptionStrings.createCustomerInputError);
}
try {
User user = userService.getUser(userId);
user.addCustomer(customer);
return customerRepository.save(customer);
} catch (DataIntegrityViolationException e){
logger.error(exceptionStrings.createCustomerSaveError);
throw new CustomerServiceException(exceptionStrings.createCustomerSaveError, e);
}
}
@Override
public Customer getCustomer(int customerId) {
return customerRepository.findOne(customerId);
}
@Override
public Customer updateCustomer(Customer customer) throws CustomerServiceException {
if(customer == null || customer.getCustomerId() == 0 || customerRepository.findOne(customer.getCustomerId()) == null){
logger.error(exceptionStrings.updateCustomerInputError);
throw new CustomerServiceException(exceptionStrings.updateCustomerInputError);
}
return customerRepository.save(customer);
}
@Override
public void deleteCustomer(Customer customer) throws CustomerServiceException {
if(customer == null || customer.getCustomerId() == 0 || customerRepository.findOne(customer.getCustomerId()) == null){
logger.error(exceptionStrings.deleteCustomerInputError);
throw new CustomerServiceException(exceptionStrings.deleteCustomerInputError);
}
customerRepository.delete(customer);
}
}
Aucun commentaire:
Enregistrer un commentaire