因为当我尝试对我的服务进行单元测试时遇到问题,所以我最近一直撞到了墙上。
我有一个User,它与Product,Customer和Quotation类有多个单向关系。 我使用SpringBoot和Spring JPA存储库来使用Hibernate作为ORM对MySQL数据库执行操作。
我也有不同的服务调用存储库来对数据库执行这些操作,正是这些我试图进行单元测试。
当我单独运行单元测试类时,它们都可以成功运行,但是当我一起运行它们时,QuotationService测试完全失败。但值得注意的是,它们总是在CustomerService测试之后运行。
在查看堆栈跟踪之后,我发现当我尝试将报价保存到测试类中的数据库时,每个测试都因为外键约束而失败。
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
当我尝试保存新报价时,部分测试总是失败的是@Before部分。
@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());
}
我搜索了很多东西试图解决这个问题,包括使关系双向,但没有真正起作用,错误仍然存在。
供参考:
用户类:
@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 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);
}
}
//Omitted getters and setters
}
报价类:
@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;
//Omitted getters and setters
}
报价服务:
@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);
}
}
}
答案 0 :(得分:0)
我很确定我找到了问题的解决方案。正如斯科特威尔登建议我检查我的数据库是否每次运行都重置。我的hibernate被配置为create-drop,但似乎我还需要通过放置来强制Spring配置重新加载
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
在每个测试类之上。
这解决了我的问题。