如何使用Mockito来模拟包含EntityManager

时间:2018-05-25 11:47:52

标签: java unit-testing mockito

目前在学校,我们正在开展一个相当大的项目。然而,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 :(得分:1)

方法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
       // ...
    }
}

答案 1 :(得分:0)

1)EntityManager不应与控制器关联:

return EntityController.getEntityManager();

在设计方面,不希望:低层和高层不应混合,否则为什么要使用它们? 在测试getAllSkeletons()方面,这种耦合也会使单元测试更难设置和写入。

2)实际方法没有测试逻辑但是例外情况:您只需创建一个查询,执行它并返回结果。
对于集成测试(没有模拟DB层),这是一个很好的例子,对于单元测试来说更少 因为它会使单元测试变得复杂并且没有太多价值。

Mockito可以得到的例子,我不建议你这样做 使EntityManager成为受测试类的依赖关系:是否注入 在您的单元测试中,模拟这种依赖性 它可能看起来像:

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

真的不要写这种只描述调用流程的代码。
它只会使测试变得脆弱,很少会出现回归。