class MailServer {
void send(String smtpMessage) throws IOException {
// Opens a connection to an SMTP server, sends the SMTP message
}
}
JUnit and Mockito tips
TweetPosted on Friday Jul 03, 2020 at 06:40PM in Technology
In this entry I’ll share some tips about JUnit and Mockito for making unit tests better.
Mockito annotations
Mockito has some annotations that can be used for reducing redundancy of tests:
-
@Mock
-
@InjectMocks
-
@Captor
Before looking into the usage of those annotations, let’s assume we have the following production code which consists of 2 classes.
The first one is called MailServer, which has a method called send() that sends an SMTP message which this object receives as the parameter of the method. Note that the MailServer class most probably needs to be mocked out when you want to write a unit test for a class which uses the MailServer class because it really opens a TCP connection to an SMTP server, which is not a preferable thing for unit tests.
The other class is called Messenger, which depends on the MailServer class. This class requires an instance of the MailServer class in its constructor. This class has a method called sendMail(), which has 3 parameters. The responsibility of this method is first constructing an SMTP message based on those 3 parameters and then asking the MailServer object to send the SMTP message. It also does quick error handling which translates IOException into an unchecked one with embedding the content.
class Messenger {
private final MailServer mailServer;
Messenger(MailServer mailServer) {
this.mailServer = mailServer;
}
void sendMail(String from, String to, String body) {
String smtpMessage = String.join("\n", "From: " + from, "To: " + to, "", body);
try {
mailServer.send(smtpMessage);
} catch (IOException e) {
throw new UncheckedIOException("Error! smtpMessage=" + smtpMessage, e);
}
}
}
Let’s try writing a unit test for the Messenger class. But we don’t want to use the real MailServer class because it really tries to open a connection to an SMTP server. It will make testing harder because in order to test with the real MailServer class, we really need an SMTP server which is up and running. Let’s avoid doing that and try using a mocked version of a MailServer instance for the testing here.
A happy path test case would look like the following:
class MessengerPlainTest {
MailServer mailServer;
Messenger sut;
@BeforeEach
void setUp() {
mailServer = Mockito.mock(MailServer.class);
sut = new Messenger(mailServer);
}
@Test
@DisplayName("Messenger constructs the SMTP message and feeds MailServer")
void test() throws IOException {
String expected = "From: joe@example.com\n"
+ "To: jane@example.com\n\n"
+ "Hello!";
sut.sendMail("joe@example.com", "jane@example.com", "Hello!");
Mockito.verify(mailServer).send(expected);
}
}
In the setUp() method, a mock MailServer object is created and injected into the constructor of the Messenger class. And in the test() method, first we create the expected SMTP message which the Messenger class has to create, then we call the sendMail() method and finally we verify that the send() method of the mock MailServer object has been called with the expected SMTP message.
With annotations, the test above can be written as follows:
@ExtendWith(MockitoExtension.class)
class MessengerTest {
@Mock
MailServer mailServer;
@InjectMocks
Messenger sut;
@Test
@DisplayName("Messenger constructs the SMTP message and feeds MailServer")
void test() throws IOException {
String expected = "From: joe@example.com\n"
+ "To: jane@example.com\n\n"
+ "Hello!";
sut.sendMail("joe@example.com", "jane@example.com", "Hello!");
Mockito.verify(mailServer).send(expected);
}
}
First we annotate the test class with @ExtendWith(MockitoExtension.class) (Note that it’s a JUnit5 specific annotation, so for JUnit4 tests we need something different). Having the test class annotated with that one, when there is a field annotated with @Mock in the test class, Mockito will automatically create a mock for the field and inject it. And when there is a field annotated with @InjectMocks, Mockito will automatically create a real instance of the declared type and inject the mocks that are created by the @Mock annotation.
This is especially beneficial when many mock objects are needed because it reduces the amount of repetitive mock() method calls and also removes the need for creating the object which gets tested and injecting the mocks into the object.
And also it provides a clean way to create a mock instance of a class which has a parameterized type. When we create a mock instance of the Consumer class, a straightforward way would be the following:
Consumer<String> consumer = Mockito.mock(Consumer.class);
The problem here is that it produces an unchecked assignment warning. Your IDE will complain about it and you will get this warning when the code compiles with -Xlint:unchecked :
Warning:(35, 49) java: unchecked conversion
required: java.util.function.Consumer<java.lang.String>
found: java.util.function.Consumer
With the @Mock annotation, we can get rid of the warning:
@ExtendWith(MockitoExtension.class)
class MyTest {
@Mock
Consumer<String> consumer;
...
There is another useful annotation called @Captor. Let’s see the following test case:
@ExtendWith(MockitoExtension.class)
class MessengerCaptorTest {
@Mock
MailServer mailServer;
@InjectMocks
Messenger sut;
@Captor
ArgumentCaptor<String> captor;
@Test
@DisplayName("Messenger constructs the SMTP message and feeds MailServer")
void test() throws IOException {
sut.sendMail("joe@example.com", "jane@example.com", "Hello!");
Mockito.verify(mailServer).send(captor.capture());
String capturedValue = captor.getValue();
assertTrue(capturedValue.endsWith("Hello!"));
}
}
The @Captor annotation creates an object called ArgumentCaptor which captures a method parameter of a method call of a mock object. In order to capture a parameter with an ArgumentCaptor, first we need to call the capture() method in a method call chain of the Mockito.verify() method. Then we can get the captured value with the getValue() method and we can do any assertion against it. It’s especially useful in a situation where checking the equality is not sufficient and a complex verification is needed.
AssertJ
AssertJ is an assertion library for unit tests written in Java. It provides better readability and richer assertions than its older equivalents like the one shipped with JUnit. Let’s see some example code from the official website:
// entry point for all assertThat methods and utility methods (e.g. entry)
import static org.assertj.core.api.Assertions.*;
// basic assertions
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron);
// chaining string specific assertions
assertThat(frodo.getName()).startsWith("Fro")
.endsWith("do")
.isEqualToIgnoringCase("frodo");
// collection specific assertions (there are plenty more)
// in the examples below fellowshipOfTheRing is a List<TolkienCharacter>
assertThat(fellowshipOfTheRing).hasSize(9)
.contains(frodo, sam)
.doesNotContain(sauron);
A unique feature of AssertJ is that all of the assertions here begin with the method assertThat() which receives the parameter that gets asserted. After that we specify the conditions the parameter needs to fulfill. An advantage of this approach is that we can specify multiple conditions with method call chain. It’s more readable and less verbose than the old way where repetitive assertTrue() or assertEquals() calls are involved. It also provides rich assertions for widely used classes like List, Set or Map.
Another useful feature of AssertJ is for verifying an unhappy path where an Exception is involved. Let’s remember the production code we used in the Mockito annotation section and consider a situation where the MailServer cannot connect to the SMTP server. Due to that, the send() method throws IOException. In this situation, the Messenger class is expected to catch the IOException and translate it into UncheckedIOException with the SMTP message embedded. A unit test for this can be written as follows with AssertJ:
@ExtendWith(MockitoExtension.class)
class MessengerUnhappyTest {
@Mock
MailServer mailServer;
@InjectMocks
Messenger sut;
@Test
@DisplayName("Messenger throws UncheckedIOException with the SMTP message when MailServer has thrown IOException")
void test() throws IOException {
doThrow(new IOException("The server is down")).when(mailServer).send(any());
String expectedMessage = "From: joe@example.com\n"
+ "To: jane@example.com\n\n"
+ "Hello!";
assertThatThrownBy(() -> sut.sendMail("joe@example.com", "jane@example.com", "Hello!"))
.isInstanceOf(UncheckedIOException.class)
.hasMessage("Error! smtpMessage=%s", expectedMessage)
.hasCauseInstanceOf(IOException.class);
}
}
First we make the mock MailServer instance throw IOException when its send() method is called. After that we pass a lambda expression which calls the sendMail() method to the assertThatThrownBy() method of AssertJ. After that we can do various assertions. What we are checking here is that the sendMail() method throws UncheckedIOException with the SMTP message embedded and it also contains a parent Exception whose class is IOException.
Conclusion
We’ve discussed some tips about Mockito and the basic uses of AssertJ for test cases that are written in JUnit. Both Mockito and AssertJ have extensive documents and rich functionality which greatly helps writing unit tests. I highly recommend checking the references below:
The pieces of code which are used in this entry can be found on GitHub: https://github.com/nuzayats/junittips
Tags: test
JPA Builder パターン
TweetPosted on Sunday Dec 04, 2016 at 12:00AM in Technology
このエントリは Java EE Advent Calendar 2016 の4日目の記事です.
昨日は opengl-8080 さんの「JPA マッピングカタログ」 でした.明日は tyru さん です.
JPA のおかげで,多数のカラムや複雑なリレーションを持つテーブルへのレコード生成は,プレーンな JDBC を使っていた時代に比べると,たいへん楽になりました.しかし,いぜん頭を悩ませる局面もあります.例えば,以下の図のようなスキーマを考えてみてください:
このスキーマ中の Employee テーブルへレコードを生成するコードを考えてみてください.以下のようになると思います:
public class EmployeeService {
private final EntityManager em;
EmployeeService(final EntityManager em) {
this.em = em;
}
public long create(long deptId,
String name,
boolean temporary,
Set<Long> projectIds,
Set<String> phoneNumbers) {
// instantiating and setting attributes of employee
final Employee employee = new Employee();
employee.setName(name);
employee.setTemporary(temporary);
employee.setProjects(new HashSet<>());
employee.setPhones(new HashSet<>());
// making a relation between employee and dept
final Dept dept = em.find(Dept.class, deptId);
employee.setDept(dept);
em.persist(employee);
dept.getEmployees().add(employee);
// making relations between employee and projects
for (final Long projectId : projectIds) {
final Project project = em.find(Project.class, projectId);
project.getEmployees().add(employee);
employee.getProjects().add(project);
}
// creating phones
for (final String phoneNumber : phoneNumbers) {
final Phone phone = new Phone();
phone.setNumber(phoneNumber);
phone.setEmployee(employee);
em.persist(phone);
employee.getPhones().add(phone);
}
em.flush(); // making sure a generated id is present
return employee.getId();
}
}
いま書いたメソッド create() を呼び出すコードは,以下のようになります:
final Set<Long> projectIds = new HashSet<>();
Collections.addAll(projectIds, project1Id, project2Id);
final Set<String> phoneNumbers = new HashSet<>();
Collections.addAll(phoneNumbers, "000-0000-0001", "000-0000-0002", "000-0000-0003");
final long savedEmployeeId = service.create(
engineeringDeptId,
"Jane Doe",
true,
projectIds,
phoneNumbers);
悪くはありません.しかし,もっと複雑なリレーションや省略可能なカラムが多数存在するケースを考えてみてください.それらに対応するメソッドの引数も多くなるのにしたがって,省略可能な引数に対応するためのオーバーロードや null の引数が並ぶメソッド呼び出しも増殖していき,だんだんメンテナンスが大変になっていきます.
このようなケースでは,私が個人的に「JPA Builder パターン」と呼んでいる書き方がおすすめです.以下に示すような非 static のネストされた Builder クラスと,そのインスタンスを生成するためのメソッドを,前述の EmployeeService クラスに対して追加します:
...
public Builder builder(long deptId, String name) {
return new Builder(deptId, name);
}
public final class Builder { // non-static
private final long deptId;
private final String name;
private boolean temporary;
private Set<Long> projectIds = new HashSet<>();
private Set<String> phoneNumbers = new HashSet<>();
private Builder(final long deptId, final String name) {
this.deptId = deptId;
this.name = name;
}
public Builder temporary(boolean temporary) {
this.temporary = temporary;
return this;
}
public Builder projectIds(Long... ids) {
Collections.addAll(projectIds, ids);
return this;
}
public Builder phoneNumbers(String... numbers) {
Collections.addAll(phoneNumbers, numbers);
return this;
}
public long build() {
// In reality, passing "this" instead of actual values (deptId, name, ...) is recommended
return EmployeeService.this.create(deptId, name, temporary, projectIds, phoneNumbers);
}
}
これを呼び出すコードは,以下のようになります:
final long savedEmployeeId = service.builder(engineeringDeptId, "Jane Doe")
.temporary(true)
.projectIds(project1Id, project2Id)
.phoneNumbers("000-0000-0001", "000-0000-0002", "000-0000-0003")
.build();
今回の例のようにリレーションや省略可能なカラムの数がさほど多くない場合,あまりメリットがないように見えるかもしれませんが,現実には,もっと多い数になることは珍しくありません.そのようなケースでは,このパターンを使うことで保守性・可読性を維持・向上させることができます.
このエントリで使用したクラスとテストを含むサンプルの一式は以下にありますので,実際に動かしてみたい方はチェックしてみてください.EclipseLink と Hibernate での動作を確認しています.DB のセットアップなどは必要なく,単に "mvn test" を叩くとインメモリの Apache Derby 上でテストが走ります:
宣伝: 求人のご案内
現在,私の勤めている (株) L is B では,エンジニアを募集しています.主に "direct" という企業向けメッセンジャーの開発と運用を行っている会社です.
残念ながら,今のところ GlassFish や WildFly のようなフルの Java EE コンテナは使われていないのですが,Java EE の構成要素,例えば JPA や JAX-RS などはヘビーに使われています.「Java EE を使って自社サービス開発したい!」という方,ぜひ以下をチェックしてみてください:
Tags: jpa
Jersey 1.x ExceptionMapper examples
TweetPosted on Saturday Oct 29, 2016 at 12:42PM in Technology
CAUTION: this posting is about Jersey 1.x which is obsoleted. If you use more modern JAX-RS implementation such as Jersey 2.x or above, please check if there are any better approaches.
JAX-RS (Jersey) provides a mechanism called ExceptionMapper which is an universal way to map an exception that thrown by a JAX-RS implementation itself or application code, to any HTTP response. In this posting, I’ll introduce some basic but useful usages of it that I have found.
ExceptionMapper for RuntimeException
When your resource method throw an Exception, say, an unintentional NullPointerException which caused by a bug or something, typically this produces a 500 Error page which created by your application container. You can catch, log those exceptions and produce a customer-friendly response with an ExceptionMapper which is something like following:
@Provider
public class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException> {
private static final Logger LOGGER = Logger.getLogger(RuntimeExceptionMapper.class.getName());
@Override
public Response toResponse(final RuntimeException e) {
// taken from http://stackoverflow.com/questions/13716793/jersey-how-to-register-a-exceptionmapper-that-omits-some-subclasses
if (e instanceof WebApplicationException) {
return ((WebApplicationException) e).getResponse();
}
LOGGER.log(Level.WARNING, "RuntimeException occurred", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Sorry, something went wrong")
.build();
}
}
ExceptionMapper for NotFoundException
When your app receive a request which has no corresponding resource method, typically this produces a 404 Error page created by your container as well as an uncaught Exception. You can handle this situation with an ExceptionMapper as follows:
@Provider
public class NotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {
private static final Logger LOGGER = Logger.getLogger(NotFoundExceptionMapper.class.getName());
@Override
public Response toResponse(final NotFoundException e) {
LOGGER.log(Level.FINE, "NotFoundException occurred", e);
return Response.status(Response.Status.NOT_FOUND)
.entity("Check the destination path of your request - we have no API here")
.build();
}
}
ExceptionMapper for ParamExceptionMapper
Let’s say you have a value class which is something like following:
public class EmployeeId {
private final long value;
public EmployeeId(final long value) {
if (value < 1) {
throw new IllegalArgumentException("EmployeeId must be larger than zero");
}
this.value = value;
}
public EmployeeId(final String value) {
this(Long.parseLong(value));
}
// getter and toString omitted
}
And you have a resource method which receives an EmployeeId
as follows:
@Path("myresource")
public class MyResource {
@GET
@Path("emp")
@Produces(MediaType.TEXT_PLAIN)
public String emp(@QueryParam("id") EmployeeId id) {
...
}
}
When the resource method receives a valid id
, say 123
, Jersey automatically constructs an EmployeeId
instance and passes it to the application code. That’s fine, but consider if a malicious user has sent an invalid value, say -1
. Typically this produces an error page which created by your container as well. You may want to return a more informational response with HTTP status code 400
with an ExceptionMapper which is something like:
@Provider
public class ParamExceptionMapper implements ExceptionMapper<ParamException> {
private static final Logger LOGGER = Logger.getLogger(ParamExceptionMapper.class.getName());
@Override
public Response toResponse(final ParamException e) {
LOGGER.log(Level.FINE, "ParamException occurred", e);
final StringBuilder sb = new StringBuilder("Your parameter '" + e.getParameterName() + "' is invalid");
final Throwable cause = e.getCause();
if (cause instanceof IllegalArgumentException) {
final String message = cause.getMessage();
if (message != null && !message.isEmpty()) {
sb.append(": ").append(message);
}
}
return Response.status(Response.Status.BAD_REQUEST)
.entity(sb.toString())
.build();
}
}
Conclusion
ExceptionMapper helps making error responses of your REST APIs more helpful. And it reduces repetitive exception handling code in your resource classes that tend to be tons of boilarplate.
You can obtain complete code based on Jersey 1.x and testcases that powered by Arquillian, Embedded Tomcat and Apache HttpClient, from my GitHub repository.
Tags: arquillian jax-rs jersey
JPA Builder Pattern
TweetPosted on Sunday Oct 16, 2016 at 06:07PM in Technology
Thanks to JPA, creating an entity which has tons of fields and complex relations has become much easier than the plain old JDBC era. but there are still some difficulties with it. for example, consider that you have a database schema which is something like following diagram:
With those entities, let’s say you have to write some code for creating an employee. this would be something like:
public class EmployeeService {
private final EntityManager em;
EmployeeService(final EntityManager em) {
this.em = em;
}
public long create(long deptId,
String name,
boolean temporary,
Set<Long> projectIds,
Set<String> phoneNumbers) {
// instantiating and setting attributes of employee
final Employee employee = new Employee();
employee.setName(name);
employee.setTemporary(temporary);
employee.setProjects(new HashSet<>());
employee.setPhones(new HashSet<>());
// making a relation between employee and dept
final Dept dept = em.find(Dept.class, deptId);
employee.setDept(dept);
em.persist(employee);
dept.getEmployees().add(employee);
// making relations between employee and projects
for (final Long projectId : projectIds) {
final Project project = em.find(Project.class, projectId);
project.getEmployees().add(employee);
employee.getProjects().add(project);
}
// creating phones
for (final String phoneNumber : phoneNumbers) {
final Phone phone = new Phone();
phone.setNumber(phoneNumber);
phone.setEmployee(employee);
em.persist(phone);
employee.getPhones().add(phone);
}
em.flush(); // making sure a generated id is present
return employee.getId();
}
}
And you will use the method create()
as follows:
final Set<Long> projectIds = new HashSet<>();
Collections.addAll(projectIds, project1Id, project2Id);
final Set<String> phoneNumbers = new HashSet<>();
Collections.addAll(phoneNumbers, "000-0000-0001", "000-0000-0002", "000-0000-0003");
final long savedEmployeeId = service.create(
engineeringDeptId,
"Jane Doe",
true,
projectIds,
phoneNumbers);
Not so bad, but think about if there are more complex relations or attributes that may be optional. the arguments of the method will be much longer, and hard to maintain.
In such a case, a pattern which I call "JPA builder pattern" would be nice. you create a non-static nested builder class and a method which creates a builder, into the class EmployeeService
, as follows:
...
public Builder builder(long deptId, String name) {
return new Builder(deptId, name);
}
public final class Builder { // non-static
private final long deptId;
private final String name;
private boolean temporary;
private Set<Long> projectIds = new HashSet<>();
private Set<String> phoneNumbers = new HashSet<>();
private Builder(final long deptId, final String name) {
this.deptId = deptId;
this.name = name;
}
public Builder temporary(boolean temporary) {
this.temporary = temporary;
return this;
}
public Builder projectIds(Long... ids) {
Collections.addAll(projectIds, ids);
return this;
}
public Builder phoneNumbers(String... numbers) {
Collections.addAll(phoneNumbers, numbers);
return this;
}
public long build() {
// In reality, passing "this" instead of actual values (deptId, name, ...) is recommended
return EmployeeService.this.create(deptId, name, temporary, projectIds, phoneNumbers);
}
}
And you will use the builder as follows:
final long savedEmployeeId = service.builder(engineeringDeptId, "Jane Doe")
.temporary(true)
.projectIds(project1Id, project2Id)
.phoneNumbers("000-0000-0001", "000-0000-0002", "000-0000-0003")
.build();
It doesn’t make much sense if relations or attributes that may be optional are not that many as this example, but in reality, entities likely to have those much more. in such a case, I believe this pattern makes your code much clean, readable and maintainable.
You can obtain the entire project which contains entities, the service class and executable tests that run with an embedded database, from my GitHub repo.
Tags: jpa
Jukito integration with JPA and guice-persist
TweetPosted on Sunday Jul 31, 2016 at 11:28AM in Technology
A DI container such as Guice helps you to assemble loosely-coupled classes that easy to write unit tests.
But integration tests, that are necessary thing as well as unit tests, it’s a different story from writing unit tests. writing integration tests involve many cumbersome initialization code of frameworks such as DI container, JPA provider or an application server. they tend to spread across testcases and make testcases messy.
If you use Guice as DI container, Jukito helps you to write integration tests. its official documentation covers some simple usecases, but not mentioned about integration with guice-persist. so in this entry I’ll introduce you how to integrate and begin writing clean testcases without messy boilarplate code with them.
Create an application to be tested
Consider we have a simple JPA entity and a service class which uses it.
An entity class named Employee
:
@Entity
public class Employee implements Serializable {
@Id
@GeneratedValue
private Long id;
private String name;
public Employee(final String name) {
this.name = name;
}
...
The service class to be tested named EmployeeService
:
public class EmployeeService {
@Inject
private EntityManager em;
@Transactional
public Long save(final String name) {
final Employee employee = new Employee(name);
em.persist(employee);
em.flush();
return employee.getId();
}
public List<String> findAllNames() {
return em.createQuery("SELECT e.name FROM Employee e ORDER BY e.name", String.class).getResultList();
}
}
Create a module for testing
Next, we will create a module for testing (BTW I recommend you to use Module overriding for creating one for testing, based on production one). this would be something like:
// Taken from https://gist.github.com/JWGmeligMeyling/785e459c4cbaab606ed8 , thanks!
public class DatabaseModule extends AbstractModule {
@Override
protected void configure() {
install(new JpaPersistModule("myPU"));
bind(JPAInitializer.class).asEagerSingleton();
}
@Singleton
private static class JPAInitializer {
@Inject
public JPAInitializer(final PersistService service) {
service.start();
}
}
}
Create JpaJukitoRunner
Then, create a special TestRunner which extends JukitoRunner
as follows:
public class JpaJukitoRunner extends JukitoRunner {
public JpaJukitoRunner(final Class<?> klass) throws InitializationError, InvocationTargetException, InstantiationException, IllegalAccessException {
super(klass);
}
public JpaJukitoRunner(final Class<?> klass, final Injector injector) throws InitializationError, InvocationTargetException, InstantiationException, IllegalAccessException {
super(klass, injector);
}
private UnitOfWork unitOfWork;
@Override
protected Object createTest() throws Exception {
this.unitOfWork = getInjector().getInstance(UnitOfWork.class);
this.unitOfWork.begin();
return super.createTest();
}
@Override
public void run(final RunNotifier notifier) {
notifier.addListener(new RunListener() {
@Override
public void testFinished(final Description description) throws Exception {
// this ensures every tests use distinct entity manager instances
unitOfWork.end();
}
});
super.run(notifier);
}
}
This ensures every tests use distinct EntityManager
instances for each execution. without this, only one EntityManager
instance will be used for all of executions of test cases because it is stored in a ThreadLocal
and JUnit uses only one Thread by default for all test executions.
That means that many entities will be kept managed during an execution. consider if you have thousands of test classes in your project - entities will be shared across all of test execution. it will make your EntityManager fat, also your tests may get affected by 1st level cache which got dirty by other test executions.
Create a testcase
Finally, you can write a testcase as follows:
@RunWith(JpaJukitoRunner.class)
@UseModules(DatabaseModule.class)
public class EmployeeServiceTest {
@Inject
private EmployeeService sut;
@Inject
private EntityManager em;
@Before
@Transactional
public void setUp() throws Exception {
em.createQuery("DELETE FROM Employee").executeUpdate();
}
@Test
public void saveShouldPersistJohnDoe() throws Exception {
final String name = "John Doe";
final long id = sut.save(name);
final Employee employee = em.find(Employee.class, id);
assertThat(employee.getName(), is(name));
}
@Test
public void findAllNamesShouldReturnExpectedResult() throws Exception {
sut.save("Jane Doe");
sut.save("John Doe");
final List<String> result = sut.findAllNames();
assertThat(result.size(), is(2));
}
}
You can see that there are no any cumbersome initialization code of JPA or database stuff. and note that you can use declarative transaction management by @Transactional
, for both the sut class and populating test data into your database.
Cleaning 1st level cache
I have mentioned about bad effect of 1st level cache of EntityManager earlier in this entry, so you may consider that you want to clean 1st level cache. you can do it with em.clear()
, or also something like following:
@RunWith(JpaJukitoRunner.class)
@UseModules(DatabaseModule.class)
public class EmployeeServiceTestManagesUOW {
@Inject
private Provider<EmployeeService> sut;
@Inject
private Provider<EntityManager> em;
@Inject
private UnitOfWork unitOfWork;
@Before
@Transactional
public void setUp() throws Exception {
em.get().createQuery("DELETE FROM Employee").executeUpdate();
}
@Test
public void saveShouldPersistJohnDoe() throws Exception {
final String name = "John Doe";
newEntityManager();
final long id = sut.get().save(name);
newEntityManager();
final Employee employee = em.get().find(Employee.class, id);
assertThat(employee.getName(), is(name));
}
private void newEntityManager() {
unitOfWork.end();
unitOfWork.begin();
}
}
In this test, there are three distinct EntityManagers involved in a test execution. the one in setUp()
, another one in invocation of sut#save()
and finally one for em#find()
which is used for assertion.
But in my opinion, this is an overkill and using one shared EntityManager for one test execution would be sufficient. if it didn’t work well, something may wrong with your usage of JPA.
Conclusion
We have seen how testcases which involve JPA and guice-persist could be written cleanly with Jukito. it enables us to eliminate cumbersome boilarplate code for managing and invoking Injector
, JPA initialization and manual transaction management. now our testcases look pretty clean.
Executable testcases and the example project can be obtained from my GitHub repository.
References
-
https://gist.github.com/JWGmeligMeyling/785e459c4cbaab606ed8 - many part of this entry taken from this gist. thank you so much!
Tags: guice guice-persist jpa jukito test