简体   繁体   English

如何使用Mockito来模拟包含EntityManager

[英]How to use Mockito to mock including an EntityManager

Currently in school we are working on a rather large project. 目前在学校,我们正在开展一个相当大的项目。 However testing in Java wasn't really explained that well so I didn't really work TDD like I was suppose to. 然而,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();
}

So my question is, how do I test this method using Mockito? 所以我的问题是,如何使用Mockito测试这种方法?

Approach 1: Test the Code As Is 方法1:测试代码是

The getEntityManager method is private and it invokes a static method so, as things stand, you would need to use PowerMockito to provide a mocked instance of EntityManager in your test. getEntityManager方法是私有的,它调用静态方法,因此,您需要使用PowerMockito在测试中提供EntityManager的模拟实例。 For example: 例如:

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

Approach 2: Refactor For Separation of Concerns and Ease of Testing 方法2:重构问题和易于测试的重构

However, you could simplify things, from a testing and design perspective, by externalising the creation of the entity manager into a factory, for example. 但是,您可以通过将实体管理器的创建外部化为工厂,从测试和设计的角度简化操作。

public class EntityManagerFactory {

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

Next, inject an instance of EntityManagerFactory into whatever class contains getAllSkeletons() (ie the class you are testing). 接下来,将EntityManagerFactory注入任何包含getAllSkeletons()的类(即您正在测试的类)。 The simplest way of doing this is to declare it as a constructor argument: 最简单的方法是将它声明为构造函数参数:

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

Now, you can test this code using vanilla mockito. 现在,您可以使用vanilla mockito测试此代码。 For example: 例如:

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) The EntityManager should not be associated to the controller : 1)EntityManager不应与控制器关联:

return EntityController.getEntityManager();

In terms of design, it is not desirable : low layer and high layer should not be mixed, otherwise why use them ? 在设计方面,不希望:低层和高层不应混合,否则为什么要使用它们?
In terms of testing for getAllSkeletons() , this coupling will also make the unit test harder to set and to write. 在测试getAllSkeletons() ,这种耦合也会使单元测试更难设置和写入。

2) The actual method doesn't have logic to test but the exception case : you just create a query, execute it and return the result. 2)实际方法没有测试逻辑但是例外情况:你只需创建一个查询,执行它并返回结果。
It is a good case for an integration test (without mocking the DB layer), less for an unit test. 对于集成测试(没有模拟DB层),这是一个很好的例子,对于单元测试来说则更少。
As it will make the unit test complex and with not a lot of value. 因为它会使单元测试变得复杂并且没有太多价值。


Example of what you could get with Mockito and that I don't recommend. 你可以用Mockito得到的例子以及我不推荐的例子。
Make the EntityManager a dependency of the class under test : with injection or not. 使EntityManager成为被测试类的依赖关系:是否注入。
In your unit test, mock this dependency. 在您的单元测试中,模拟此依赖项。
It could look like : 它可能看起来像:

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

Really don't write this kind of code that just describes the flow of invocations. 真的不要写这种只描述调用流程的代码。
It makes just a test brittle that will very rarely catch regressions. 这只会使测试变得脆弱,很少会出现回归。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM