Let's take the following classes which are a simplification of more complex classes and their relationships.
@Data
@Builder
public class UserAccount {
private String username;
private String password;
private Language contactLanguage;
public static UserAccount.UserAccountBuilder defaultUserAccount() {
return UserAccount.builder()
.username("default_username")
.password("default_password")
.contactLanguage(defaultLanguage().build());
}
}
@Data
@Builder
public class Language {
private String name;
private String iso2;
private String iso3;
public static Language.LanguageBuilder defaultLanguage() {
return Language.builder()
.name("default_language_name")
.iso2("default_iso2")
.iso3("default_iso3");
}
}
Using Lombok's @Builder
annotation, I can easily construct an object like this, especially for testing:
UserAccount.builder()
.username("foo")
.password("bar")
.contactLanguage(Language.builder()
.name("English")
.iso2("EN")
.iso3("ENG")
.build())
.build();
// Or even like this...
defaultUserAccount().build();
This works fine for unit tests or any tests where such generated objects are only required to exist in memory.
However I'd also like to use this approach for integration tests with an underlying database (using Spring Boot 2.4 + JPA + Hibernate). And this is where some issues come up I couldn't solve so far. Let's have a look:
Each UserAccount
needs to have a contactLanguage
, but Language
lives on its own. Other entities might use it as well. When constructing a user account with defaultUserAccount().build()
, then persisting this entity fails because the Language
object has not been persisted yet. There is no persist cascade on contactLanguage
because I don't want "any" Language
being created upon creating a UserAccount
.
My only idea would be to use defaultLanguage().build()
and persist this before defaultUserAccount().build()
. But I feel that this will become complex and flaky as soon as there are more levels of nested builders or relationship to other entites.
Another thing is: Even if I managed to persist the defaultLanguge
, I would run into a collision as soon as another test calls defaultUserAccount().build()
because then the langauge already exists and cannot be inserted again.
Are there any patterns or approaches for persisting such test data objects?
Update #1
After more searching, I found this question on SO which looks almost identical.