45

How should I test an EJB 3.1 which gets an instance of EntityManager injected?

A possible EJB:

@Stateless
@LocalBean
public class CommentService {

    @PersistenceContext
    private EntityManager em;

    public List<Comment> findAll() {
        TypedQuery<Comment> query = em.createNamedQuery(
            Comment.FIND_ALL, Comment.class
        );
        return query.getResultList();
    }

}

A possible test:

@Test
public void testFindAll() {
    List<Comment> all = service.findAll();
    Assert.assertEquals(8, all.size());
}

I am only using GlassFish 3.1 and Eclipse Indigo for Java EE Developers. I already tried things like that:

@Before
public void setUp() throws Exception {
    ejbContainer = EJBContainer.createEJBContainer();
    service = (CommentService) ejbContainer.getContext()
        .lookup("java:global/classes/CommentService");
}

But all I got was:

javax.ejb.EJBException:
No EJBContainer provider available: no provider names had been found.
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Matthias
  • 7,432
  • 6
  • 55
  • 88

4 Answers4

88

The accepted answer requires mocking a lot of code, including the persistence layer. Use an embedded container to test the actual beans, instead; otherwise, mocking the persistence layer results in code that barely tests anything useful.

Use a session bean with an entity manager that references a persistence unit:

@Stateless
public class CommentService {

    @PersistenceContext(unitName = "pu")
    private EntityManager em;

    public void create(Comment t) {
        em.merge(t);
    }

    public Collection<Comment> getAll() {
        Query q = em.createNamedQuery("Comment.findAll");
        Collection<Comment> entities = q.getResultList();
        return entities;
    }
}

The entity bean:

@Entity
@NamedQueries({@NamedQuery(name = "Comment.findAll", query = "select e from Comment e")})
public class Comment implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

This persistence unit is defined in the persistence.xml file as follows:

<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

  <persistence-unit name="pu" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>org.glassfish.embedded.tempconverter.Comment</class>
    <properties>
      <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

The transaction type must be JTA.

Then write a test that creates and destroys the EJB container (GlassFish embedded container):

public class CommentTest extends TestCase {

     private Context  ctx;
     private EJBContainer ejbContainer;

    @BeforeClass
    public  void setUp() {
        ejbContainer = EJBContainer.createEJBContainer();
        System.out.println("Opening the container" );
        ctx = ejbContainer.getContext();
    }

    @AfterClass
    public  void tearDown() {
        ejbContainer.close();
        System.out.println("Closing the container" );
    }

    public void testApp() throws NamingException {

        CommentService converter = (CommentService) ctx.lookup("java:global/classes/CommentService");
        assertNotNull(converter);

        Comment t = new Comment();
        converter.create(t);
        t = new Comment();
        converter.create(t);
        t = new Comment();
        converter.create(t);
        t = new Comment();
        converter.create(t);

        Collection<Comment> ts = converter.getAll();

        assertEquals(4, ts.size());
    }
}

Next, add two dependencies (such as to a Maven POM):

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.8.2</version>
    <scope>test</scope>
    <type>jar</type>
</dependency>
<dependency>
    <groupId>org.glassfish.main.extras</groupId>
    <artifactId>glassfish-embedded-all</artifactId>
    <version>3.1.2</version>
    <scope>compile</scope>
</dependency>

Having the dependencies, session and entity bean, persistence file, test files implemented exactly as shown, then the test(s) should pass. (The examples on the Internet are woefully inadequate.)

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Oliver Watkins
  • 12,575
  • 33
  • 119
  • 225
  • 6
    100% ACK! Additionally you may also switch the embedded EJB container just by changing your maven dependency. I tend to use OpenEJB as it starts up a lot faster during the tests, and you may even think about running this kind of tests during a normal build as they don't take a lot of time. See some examples here: https://tomee.apache.org/examples-trunk/ – Big Bad Baerni Dec 17 '13 at 14:07
  • although i realise now that the persistence unit needs to be seperated in the test phase. I am still trying to figure that out. – Oliver Watkins Dec 17 '13 at 14:43
  • 3
    Definitley this answer over the accepted one. Even though the accepted one might be literally correct (unit vs integration tests), what matters is: does my code do what I expect it does. And to know this, you have to test it through some actual data. Mocking is fine, nice and easy, but it will never tell you that you built a complex criteria wrong. This will. – Psyx Jun 18 '14 at 09:59
  • Also, notice that there can be a conflict if you are using two or more PUs for the same classes. I've tried this successfully in order to solve that [link](http://stackoverflow.com/a/16839859/854481) – manelvf Dec 15 '14 at 13:24
  • 6
    The accepted answer correctly points out the difference between unit tests and integration tests. Unit tests are _supposed to_ test only very little. They should be an indication of some very specific breakage. Testing a bean in a container, with no mocked dependencies, means introducing the possibility of a lot of false positives about the class under test failing. That's not what you want from a _unit test_. Your test tests that your system works. In case of a failure you need to start digging, instead of having a precise pointer to a very specific error. – user625488 Jun 29 '16 at 16:31
  • but the accepted answer adds barely any value. You could add 10 things and get back 5 and it would still return pass. That is not what the original poster wants to know. Also the barrier between unit testing and integration testing is blurry. I would classify my answer as a unit test whereas someone else would classify it as an integration test – Oliver Watkins Jun 29 '16 at 20:40
  • Absolutely! Mocking an `EntityManager` as the accepted answer proposes is just a terribly bad idea. I can say that having actually written lots of *integration* tests for Java EE and Spring apps - much, much better than lame unit tests that don't really test what's important. – Rogério Sep 30 '16 at 19:08
  • 1
    "Also the barrier between unit testing and integration testing is blurry" I definitely do **not** agree with this statement, these 2 kind of tests must be separated, otherwise your build time increase too much during your development process. Also System integration test must be separated from those 2. – thermz Jul 12 '17 at 08:15
  • @thermz : embedded EJB containers are very fast. There is no pure definition of what a unit test is. If you really wanted a pure unit test just testing one single class, then you would have to mock everything away, including even String objects. Do you see how absurd that is? – Oliver Watkins Dec 06 '17 at 15:09
49

First of all, make sure you distinguish between unit tests and integration tests. JUnit is just a framework that helps you organize and run the tests, but you have to determine the scope of your tests.

I assume you're interested in defining a unit test of CommentService.findAll(). What does that mean? That means I'll verify that calling the findAll() method results in CommentService invoking the named query named by the FIND_ALL string constant.

Thanks to dependency injection and stubbing, you can easily achieve that using e.g. Mockito to stub out the EntityManager. For the unit test, we're only focusing on the business logic in findAll(), so I won't bother testing lookup of the Comment service either--testing that the Comment service can be looked up and is wired to a proper entity manager instance is in the scope of an integration test, not a unit test.

public class MyCommentServiceUnitTest {
    CommentService commentService;
    EntityManager entityManager;

    @Before
    public void setUp() {
        commentService = new CommentService();

        entityManager = mock(EntityManager.class);
        commentService.setEm(entityManager); // inject our stubbed entity manager
    }

    @Test
    public void testFindAll() {
        // stub the entity manager to return a meaningful result when somebody asks
        // for the FIND_ALL named query
        Query query = mock(Query.class);
        when(entityManager.createNamedQuery(Comment.FIND_ALL, Comment.class)).thenReturn(query);
        // stub the query returned above to return a meaningful result when somebody
        // asks for the result list
        List<Comment> dummyResult = new LinkedList<Comment>();
        when(query.getResultList()).thenReturn(dummyResult);

        // let's call findAll() and see what it does
        List<Comment> result = commentService.findAll();

        // did it request the named query?
        verify(entityManager).createNamedQuery(Comment.FIND_ALL, Comment.class);
        // did it ask for the result list of the named query?
        verify(query).getResultList();
        // did it return the result list of the named query?
        assertSame(dummyResult, result);

        // success, it did all of the above!
    }
}

With the unit test above, I tested the behavior of the findAll() implementation. The unit test verified that the correct named query is obtained and that the result returned by the named query was returned to the callee.

What's more, the unit test above verifies that the implementation of findAll() is correct independently of the underlying JPA provider and the underlying data. I don't want to test JPA and the JPA provider unless I suspect there are bugs in the 3rd party code, so stubbing out these dependencies lets me focus the test entirely on the business logic of the Comment service.

It can take a little while to adjust to the mindset of testing behavior using stubs, but it is a very powerful technique for testing the business logic of your EJB 3.1 beans because it lets you isolate and narrow the scope of each test to exclude external dependencies.

Kim Burgaard
  • 3,508
  • 18
  • 11
  • 2
    i+1 But it forces you to create a setter method (setEm). For me it's ok since in order to be fully testable, code should be written with testability in mind. Also you understand the difference between unit tests and integration tests. This is exactly what unit testing an "EJB" means – Kemoda May 13 '13 at 19:36
  • 2
    I dont understand how this is supposed to test that he has inserted 8 elements into his list of objects. This test doesn't go deep enough. – Oliver Watkins Dec 16 '13 at 13:54
  • I would stick to the convention of naming the test class for `Xxxx` `XxxxTest`. That would be `CommentServiceTest` in this case. The assert seems unnecessary, because after having verified that our mocked method `getResultList()` has been called, it’s a given that `result` contains `dummyResult`. – ᴠɪɴᴄᴇɴᴛ Jun 15 '16 at 15:55
  • 1
    The OP obviously wanted a *real* test where a real `EntityManager` is used. Mocking it in an isolated unit test misses the point. – Rogério Sep 30 '16 at 19:05
  • Agree, At best this test tests zero business logic. At worst this test gives false positives. – Oliver Watkins May 04 '18 at 11:40
8

Why not using Arquillian to write even unit tests and run them in a real container!?

No more mocks. No more container lifecycle and deployment hassles. Just real tests!

Mocks can be tactical, but more often than not, they are used to make code work outside of a real environment. Arquillian let's you ditch the mocks and write real tests. That's because Arquillian brings your test to the runtime, giving you access to container resources, meaningful feedback and insight about how the code really works.

More about Arquillian features.

Mehdi
  • 4,396
  • 4
  • 29
  • 30
  • 4
    Maybe because Arquillian is slow and needs a running container ? – jmcollin92 Feb 17 '13 at 18:30
  • These days containers are starting faster, also you can use a running remote container, then no need to run container for each test. – Mehdi Feb 18 '13 at 05:26
  • 10
    Arquillian is not at all user friendly, and I spend hours trying to get a test to run b/c it is not all that obvious, or very easy to forget to add a class/package/library to the Shrinkwrap Archive. Or maybe I should say that the Shrinkwrap Documentation is not well written. Documentation is everything, esp with Java when there are so many choices and jargon that it's very overwhelming for someone who just picks up and decides that they wish to try Arquillian, only to find it hard to get working. – user798719 May 30 '13 at 05:46
  • Arquillian pretty fast with embedded glassfish and embedded db. When testing ejbs its the integration test that matters – Magnus Smith Jul 11 '13 at 22:02
  • 4
    To me, Arquilian has not yet reached the point where the benefits it gives save more time than the setup it requires. I hope it does soon, but right now, it's a pain to setup properly. – Psyx Jun 18 '14 at 10:01
  • No matter how fast a container starts up, it's still too slow for TDD. If you have 100 tests for a module, and run them continuously (say via Infinitest in Eclipse), any container will kill your speed. – user625488 Jun 29 '16 at 16:36
  • "No more mocks", I totally agree on that. But Arquillian is just not a good solution. It requires the developer to write some awful, totally unnecessary code (the `@Deployment` methods), and the tests are too slow to run. A better approach is to go for *out-of-container* integration tests, with a real DB, and where each test runs in a transaction that always gets rolled-back at the end. – Rogério Sep 30 '16 at 19:12
  • I like the fact that Arquillian allows an EJB unit test to access a container without restarting it, but I don't understand the concept of `no mocks`. If I want to test EJB 1 that has EJB 2 injected, and I want to test a variety of scenarios of EJB2, what is the problem if I mock it? – ps0604 Mar 25 '19 at 21:20
1

It's possible to write unit tests that run against a container, but the caveat is that the container/appserver has to be up. Since that's not really practical, the general approach is to use a "mock" container to run your unit tests against. For that, check out JUnitEE or ejb3unit:

junitee

ejb3unit

Yusuf K.
  • 4,195
  • 1
  • 33
  • 69
Jonathan
  • 5,027
  • 39
  • 48
  • 2
    Warning: both `JUnitEE` and `EJB3Unit` are completely deprecated and useless nowadays. Use `Arquillian` or manage an embedded container yourself. – ᴠɪɴᴄᴇɴᴛ Jul 06 '16 at 17:00