Как издеваться над сохранением и Entity с помощью Mockito и jUnit

Я пытаюсь найти способ проверить свою сущность с помощью Mockito;

Это простой метод проверки:

@Mock
private EntityManager em;

@Test
public void persistArticleWithValidArticleSetsArticleId() {
    Article article = new Article();
    em.persist(article);
    assertThat(article.getId(), is(not(0L)));
}

Как мне лучше всего издеваться над поведением, когда EntityManager меняет идентификатор с 0L на, например, 1L? Возможно, с наименьшими препятствиями в читаемости.

Изменить: дополнительная информация; Вне области тестирования EntityManager создается контейнером приложения.


person Patrick    schedule 15.11.2014    source источник
comment
Некоторая дополнительная информация; Вне области тестирования EntityManager создается контейнером приложения (стеклянная рыба).   -  person Patrick    schedule 15.11.2014
comment
Вы можете просто протестировать свои методы setId() и getId() для класса Article. Я думаю, что нет необходимости издеваться над EntityManager и указывать ему установить идентификатор статьи. Этот тест будет более подходящим при модульном тестировании самого EntityManager, когда вы хотите проверить, сгенерирован ли идентификатор и установлен ли объект, который вы передаете для сохранения.   -  person pomkine    schedule 15.11.2014
comment
Это не лучший случай для использования mocking library. Внимательно посмотрите на свой тест - здесь вы проверяете, работает ли библиотека для насмешек, а не тестируете свой код. Макеты полезны, если вам нужно имитировать/заглушить поведение внутреннего компонента вашего тестируемого класса, и вы знаете, как этот компонент ведет себя в определенных ситуациях.   -  person Szymon Stepniak    schedule 15.11.2014


Ответы (3)


public class AssignIdToArticleAnswer implements Answer<Void> {

    private final Long id;

    public AssignIdToArticleAnswer(Long id) {
        this.id = id;
    }

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        Article article = (Article) invocation.getArguments()[0];
        article.setId(id);
        return null;
    }
}

А потом

doAnswer(new AssignIdToArticleAnswer(1L)).when(em).persist(any(Article.class));
person JB Nizet    schedule 15.11.2014
comment
Стоит отметить, что с помощью этого решения вы можете сделать AssignIdToArticleAnswer внутренним классом вашего тестового класса, а затем использовать один и тот же вызов doAnswer в нескольких ваших тестовых методах. Вы даже можете передавать разные идентификаторы, если вам это удобно. Это делает это решение более универсальным, чем мое решение, если вы делаете что-то большее, чем разовый тест. +1. - person Dawood ibn Kareem; 15.11.2014
comment
Идея действительно та же, но я предпочитаю этот метод из-за возможности повторного использования и лучшей читабельности самого метода тестирования. - person Patrick; 15.11.2014
comment
не имеет большого значения, но для пользовательского ответа я бы создал статический фабричный метод, который возвращает ответ. - person Brice; 16.11.2014

Для этого вы можете использовать Mockito Answer.

doAnswer(new Answer<Object>(){
     @Override
     public Object answer(InvocationOnMock invocation){
        Article article = (Article) invocation.getArguments()[0];
        article.setId(1L);
        return null;
     }
  }).when(em).persist(any(Article.class));

Это говорит Mockito, что при вызове метода persist для первого аргумента должен быть вызван его метод setId.

Но если вы сделаете это, я не понимаю, какова будет цель теста. На самом деле вы просто проверяете, работает ли механизм Mockito Answer, а не то, что код Article или EntityManager работает правильно.

person Dawood ibn Kareem    schedule 15.11.2014
comment
Более менее. Мне было интересно увидеть, что вы сохранили новый идентификатор в поле объекта Answer. Я бы и не подумал об этом, если бы не хотел повторно использовать Answer в нескольких местах. Но почему ты удалил свой? Я думаю, что он достаточно отличается от моего, поэтому стоит иметь оба здесь. @JBNizet - person Dawood ibn Kareem; 15.11.2014
comment
Я удалил его, потому что я был в основном таким же, как ваш, но пришел позже. Я восстановил его, так как вы думаете, что он что-то добавляет. В этом конкретном случае сохранение идентификатора бесполезно. На самом деле я позаимствовал код, который я фактически использую там, где класс Answer действительно находится в классе верхнего уровня, и может использоваться для любого типа объекта, что делает его многоразовым (и повторно используемым). - person JB Nizet; 15.11.2014
comment
Спасибо за этот ответ. Причина этого теста больше связана с практикой. Я ищу способ издеваться над методом void, который запускает строки кода при вызове; Для дальнейшего использования. - person Patrick; 15.11.2014
comment
@Patrick, тогда я рекомендую вам изучить документы Mockito, относящиеся к классу Answer. Это довольно богатая область API Mockito, и Mockito имеет множество встроенных методов для создания Answer объектов для различных часто используемых операций. - person Dawood ibn Kareem; 15.11.2014
comment
То же, что JB: не имеет большого значения, но для пользовательского ответа я бы создал статический фабричный метод, который возвращает ответ. - person Brice; 16.11.2014
comment
@Brice Может быть, вы могли бы опубликовать это как ответ с фрагментом кода, просто для наглядности, вместо того, чтобы хоронить его в комментариях под моими ответами и ответами JB. - person Dawood ibn Kareem; 16.11.2014
comment
;) Я знаю, но я сижу на телефоне, так что у меня все просто. - person Brice; 17.11.2014
comment
Разве вы не должны использовать when(em).persist(isA(Article.class)) вместо этого? - person Villat; 16.05.2019
comment
Не совсем, это не имеет абсолютно никакого значения, если только OP не тестирует метод, который сохраняет множество сущностей разных классов. Если бы это было так, он бы упомянул об этом в вопросе. @Виллат - person Dawood ibn Kareem; 16.05.2019

аналогичный ответ, как указано выше, но с лямбда-выражениями

   doAnswer((InvocationOnMock invocation) -> {
        Article article = (Article) invocation.getArguments()[0];
        article.setId(1L);
        return null;
    }).when(em).persist(any(Article.class));
person gmode    schedule 15.09.2015