Подтвердить что ты не робот

Тестирование EJB с помощью JUnit

Как мне проверить EJB 3.1, который получает экземпляр EntityManager, который был введен?

Возможный 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();
    }

}

Возможный тест:

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

Я использую только GlassFish 3.1 и Eclipse Indigo для разработчиков Java EE. Я уже пробовал такие вещи:

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

Но все, что я получил, было:

javax.ejb.EJBException:
No EJBContainer provider available: no provider names had been found.
4b9b3361

Ответ 1

Прежде всего, убедитесь, что вы проводите различие между модульными тестами и интеграционными тестами. JUnit - это всего лишь основа, которая помогает вам организовывать и запускать тесты, но вы должны определить объем своих тестов.

Я предполагаю, что вас интересует определение unit теста CommentService.findAll(). Что это значит? Это означает, что я проверю, что вызов метода findAll() приводит к тому, что CommentService вызывает именованный запрос, названный строковой константой FIND_ALL.

Благодаря инъекции зависимостей и удалению, вы можете легко достичь этого, используя, например, Mockito, чтобы заглушить EntityManager. Для unit test мы ориентируемся только на бизнес-логику в findAll(), поэтому я не буду утруждать себя проверкой поиска службы комментариев - тестирование того, что служба комментария может быть просмотрена и подключена к соответствующему экземпляр диспетчера объектов находится в рамках теста интеграции, а не 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 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!
    }
}

С помощью unit test выше я проверил поведение реализации findAll(). unit test подтвердил, что правильный именованный запрос получен и что результат, возвращенный именованным запросом, был возвращен вызываемому абоненту.

Что еще, unit test выше проверяет правильность реализации findAll() независимо от базового поставщика JPA и базовых данных. Я не хочу тестировать JPA и JPA-провайдера, если я не подозреваю, что есть ошибки в стороннем коде, поэтому устранение этих зависимостей позволяет полностью сосредоточить тест на бизнес-логике службы комментариев.

Может потребоваться некоторое время, чтобы приспособиться к настроению тестирования поведения с помощью заглушек, но это очень мощный метод тестирования бизнес-логики вашего EJB 3.1 beans, поскольку он позволяет изолировать и сузить область каждого тест для исключения внешних зависимостей.

Ответ 2

Я не согласен с принятым ответом на этот вопрос. В принятом ответе выше говорится о насмехании большого количества кода, включая уровень сохранения. Вы должны использовать встроенный контейнер, чтобы протестировать фактический beans, иначе вычеркнуть слой с сохранением оставит вас с тестом, который проверяет практически ничего полезного.

Оригинальный плакат должен иметь сеанс bean, как это, с диспетчером сущностей, который указывает на единицу сохранения:

@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;
    }
}

Сущность 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;
    }
}

Эта единица сопротивления определена в файле persistence.xml и выглядит следующим образом:

<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>

Обратите внимание, что тип транзакции должен быть JTA.

Все, что вам нужно сделать, это написать такой тест, который создает и уничтожает контейнер EJB (встроенный контейнер из стеклянной рыбы):

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());
    }
}

Вам просто нужно добавить эти два в свои зависимости. Вот как мои зависимости выглядят в моем 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>

Если у вас есть зависимые, сеанс и сущность bean, файл persistence, test, реализованный точно так же, как у меня здесь, тогда ваш тест должен пройти. Мне потребовалось целых два дня, чтобы понять это (примеры в Интернете ужасны).

Ответ 3

Почему бы не использовать Arquillian для записи даже модульных тестов и запускать их в реальном контейнере!?

Нет больше насмешек. Больше жизненных циклов контейнера и проблем с развертыванием. Просто настоящие тесты!

Моксы могут быть тактическими, но чаще всего они используются для работы кода вне реальной среды. Аркильян позволил вам бросить насмешки и написать настоящие тесты. Это потому, что Arquillian приносит ваш тест во время выполнения, предоставляя вам доступ к ресурсам контейнеров, содержательную обратную связь и понимание того, как код действительно работает.

Подробнее о Функции Arquillian.

Ответ 4

Можно написать единичные тесты, которые выполняются против контейнера, но оговорка заключается в том, что контейнер/сервер приложений должен быть включен. Поскольку это не совсем практично, общий подход заключается в использовании "макетного" контейнера для запуска ваших модульных тестов. Для этого проверьте JUnitEE или ejb3unit:

junitee

ejb3unit