Как использовать Mockito для насмешек, включая EntityManager - PullRequest
0 голосов
/ 25 мая 2018

В настоящее время в школе мы работаем над довольно крупным проектом.Однако тестирование на Java не очень хорошо объяснялось, поэтому я не работал с TDD, как предполагалось.

protected EntityManager getEntityManager() {
    return EntityController.getEntityManager();
}

// Get all exam skeletons from the DB
@Override
public List<ExamSkeleton> getAllSkeletons() {
    EntityManager entityManager = getEntityManager();
    try {
        TypedQuery<ExamSkeleton> query = entityManager.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s", ExamSkeleton.class);

        List<ExamSkeleton> skeletons = query.getResultList();

        return skeletons;
    } catch (IllegalArgumentException exception) {
        LOGGER.error(exception);
    }
        return Collections.emptyList();
}

Итак, мой вопрос: как мне протестировать этот метод с помощью Mockito?

Ответы [ 2 ]

0 голосов
/ 25 мая 2018

Подход 1. Проверка кода Как есть

Метод getEntityManager является закрытым и вызывает статический метод, поэтому при существующем состоянии вам потребуется использовать PowerMockito для обеспеченияподдельный экземпляр EntityManager в вашем тесте.Например:

@RunWith(PowerMockRunner.class)
@PrepareForTest({EntityController.class})
public class SomeTest {

    @Test
    public void aTest() {
        PowerMockito.mockStatic(EntityController.class);

        EntityManager entityManager = Mockito.mock(EntityManager.class);
        Mockito.when(EntityController.getEntityManager()).thenReturn(entityManager);

        TypedQuery<ExamSkeleton> query = (TypedQuery<ExamSkeleton>) Mockito.mock(TypedQuery.class);

        Mockito.when(entityManager.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s")).thenReturn(query);

        List<ExamSkeleton> expected = new ArrayList<>();
        Mockito.when(query.getResultList()).thenReturn(expected);

        ExamRepository examRepository = new ExamRepository();

        List<ExamSkeletons> actual = examRepository.getAllSkeletons();

        // this assertion verifies that getAllSkeletons gives you the result of the above SQl query
        assertSame(expected, actual);
    }
}

Подход 2: Фактор для разделения проблем и простоты тестирования

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

public class EntityManagerFactory {

    public EntityManager create() {
        return EntityController.getEntityManager();
    }
} 

Затем вставьте экземпляр EntityManagerFactory в любой класс, содержащий getAllSkeletons() (т. е. класс, который вы тестируете).Самый простой способ сделать это - объявить его в качестве аргумента конструктора:

public class SomeDao {
    private final EntityManagerFactory entityManagerFactory;

    public SomeDao(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }

    @Override
    public List<ExamSkeleton> getAllSkeletons() {
        try {
            TypedQuery<ExamSkeleton> query = entityManager.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s", ExamSkeleton.class);

            List<ExamSkeleton> skeletons = query.getResultList();

            return skeletons;
        } catch (IllegalArgumentException exception) {
            LOGGER.error(exception);
        }
            return Collections.emptyList();
    }
}

Теперь вы можете протестировать этот код, используя vanilla mockito.Например:

public class SomeDaoTest {

    @Test
    public void canGetAllSkeletons() {
       EntityManagerFactory entityManagerFactory = Mockito.mock(EntityManagerFactory.class);

       Mockito.when(entityManagerFactory.create()).thenReturn(entityManager);

       SomeDao sut = new SomeDao(entityManagerFactory.class);

       // now SomeDao will use your mocked EntityManager so you can set expectations
       // on createQuery etc to drive your test scenarios
       // ...
    }
}
0 голосов
/ 25 мая 2018

1) EntityManager не должен быть связан с контроллером:

return EntityController.getEntityManager();

С точки зрения дизайна, это нежелательно: нижний слой и верхний слой не следует смешивать, иначе зачем их использовать?
С точки зрения тестирования getAllSkeletons(), эта связь также усложнит настройку и запись модульного теста.

2) Реальный метод имеет не логику для тестирования, а исключительный случай: вы просто создаете запрос, выполняете его и возвращаете результат.
Это хороший пример для интеграционного теста (безнасмешка над слоем БД), меньше для юнит-теста.
Так как это сделает юнит-тест сложным и не слишком ценным.


Пример того, что вы можете получить с помощью Mockito, и что я не рекомендую.
Сделать EntityManager зависимостью тестируемого класса: с внедрением или без.
InВаш модульный тест, смоделируйте эту зависимость.
Это может выглядеть так:

@Mock
EntityManager entityManagerMock;

@Test
public void getAllSkeletons(){
   TypedQuery<ExamSkeleton> queryByMock =  (TypedQuery<ExamSkeleton>)   Mockito.mock(TypedQuery.class);
   Mockito.when(entityManagerMock.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s"))
          .thenReturn(queryByMock);
   List<ExamSkeleton> skeletons = new ArrayList<>();
   Mockito.when(queryByMock.getResultList())
          .thenReturn(skeletons);
   Foo foo = new Foo(); 
   foo.setEntityManager(entityManagerMock);
   // action
   List<ExamSkeleton> actualSkeletons = foo.getAllSkeletons();
   // assertion
   Assert.assertSame(skeletons, actualSkeletons);        
}

На самом деле не пишите такой код, который просто описывает поток вызовов.
Это делает просто тестовую ломкость, которая очень редко ловит регрессии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...