目前在学校,我们正在开展一个相当大的项目。然而,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测试此方法?
答案 0 :(得分: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);
}
}
但是,从测试和设计的角度来看,您可以通过将实体管理器的创建外部化为工厂来简化事情。
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);
}
真的不要写这种只描述调用流程的代码。
它只会使测试变得脆弱,很少会出现回归。