用于连接到DB的Rest API的Junit测试用例

时间:2015-08-26 05:46:57

标签: java rest unit-testing junit automated-tests

我有一个JAVA休息服务,它与数据库交互,进行一些操作并返回数据。

我正在尝试为这些API编写测试用例。

我正在尝试使用以下链接来实现此目的。

http://www.developer.com/java/other/article.php/10936_3882311_2/Mockito-Java-Unit-Testing-with-Mock-Objects.htm

这里,对数据库的调用被抑制,dto被模拟为虚拟值。

是否存在一种替代方法,我们实际上可以在与数据库通信时运行查询(内存数据库可能是?)

任何代码示例或参考都会有很大的帮助。

2 个答案:

答案 0 :(得分:0)

HSQLDB是我熟悉的内存数据库中的一个。这里的示例显示了与hibernate和JPA一起使用的HSQLDB。 http://uaihebert.com/tdd-with-hsqldb-jpa-and-hibernate/

但是,我认为在你的情况下,为什么你更愿意连接到内存数据库而不是模拟数据库? 它归结为您尝试实现的测试单元/集成的范围。 你在试图测试其余层中的manupilation逻辑是什么?嘲笑就足够了。 您是否正在尝试测试其余部分如何处理数据访问行为(例如数据库错误处理等),而不是在内存中可能稍好一些。 你正在测试的东西是依赖于数据设置/测试数据设置,在这种情况下内存db可能更接近,因为你可以使用相同/类似的sql创建来测试inmemory db。

答案 1 :(得分:0)

对于一个纯粹的HashMap解决方案,这样的东西会起作用,但你会失去对SQL查询功能的访问权限(除非你也模拟了查询)。

public class MockDatabase<T> {

protected Map<Serializable, T> fakeDatabase = Maps.newHashMap();

private final CustomRepository<T,Serializable> repository;

private Validator validator;

public void setValidator(Validator validator) {
    this.validator = validator;
}

public static <T extends CustomRepository> T mock(Class<T> classToMock, Validator validator) {

    T repository = Mockito.mock(classToMock);
    MockDatabase md = new MockDatabase<T>(repository, validator);

    return repository;
}


public <ID extends Serializable> MockDatabase(CustomRepository<T, ID> repository, Validator validator){
    this.repository = (CustomRepository<T, Serializable>) repository;
    this.validator = validator;


    reset(repository);
    doAnswer(new Answer<Object>() {
        @Override
        public Object answer(InvocationOnMock invocation) throws Throwable {
            fakeDatabase.clear();
            return null;
        }
    }).when(repository).deleteAll();

    when(repository.save((T) anyObject())).thenAnswer(new Answer<T>() {
        @Override
        public T answer(InvocationOnMock invocation) throws Throwable {
            return saveOrSaveAndFlush(invocation);
        }
    });


    when(repository.getReference((ID)anyObject())).thenAnswer(new Answer<T>() {
        @Override
        public T answer(InvocationOnMock invocation) throws Throwable {
            return fakeDatabase.get(invocation.getArguments()[0]);
        }
    });

   when(repository.findOne((ID)anyObject())).thenAnswer(new Answer<T>() {
       @Override
       public T answer(InvocationOnMock invocation) throws Throwable {
           return fakeDatabase.get(invocation.getArguments()[0]);
       }
   });

    doAnswer(new Answer<T>() {
        @Override
        public T answer(InvocationOnMock invocation) throws Throwable {
            return fakeDatabase.remove(ReflectionTestUtils.invokeGetterMethod(invocation.getArguments()[0], "getId"));
        }
    }).when(repository).delete((T)anyObject());


    doAnswer(new Answer<ID>() {
        @Override
        public ID answer(InvocationOnMock invocation) throws Throwable {
           fakeDatabase.remove(((ID)invocation.getArguments()[0]));
            return null;
        }
    }).when(repository).delete((ID)anyObject());


    when(repository.saveAndFlush((T) anyObject())).thenAnswer(new Answer<T>() {
        @Override
        public T answer(InvocationOnMock invocation) throws Throwable {
            return saveOrSaveAndFlush(invocation);
        }


    });

    when(repository.exists((ID)anyObject())).thenAnswer(new Answer<Boolean>() {
        @Override
        public Boolean answer(InvocationOnMock invocation) throws Throwable {
            return fakeDatabase.containsKey(invocation.getArguments()[0]);
        }
    });


    when(repository.merge(anyObject())).thenAnswer(new Answer<T>() {
        @Override
        public T answer(InvocationOnMock invocation) throws Throwable {
            return (T) invocation.getArguments()[0];
        }
    });


    when(repository.findAll()).thenAnswer(new Answer<List<T>>() {
        @Override
        public List<T> answer(InvocationOnMock invocation) throws Throwable {
            return Lists.newLinkedList(fakeDatabase.values());
        }
    });

    customMethods();

}

private T saveOrSaveAndFlush(InvocationOnMock invocation) throws NoSuchMethodException {
    Object[] args = invocation.getArguments();
    Serializable id = (Serializable) ReflectionTestUtils.getField(args[0], "id");
    if (id == null) {
        Class<?> returnType = args[0].getClass().getMethod("getId").getReturnType();

        if (returnType.equals(Long.class)) {
            id = (Long) new Random().nextLong();
        } else if (returnType.equals(Integer.class)) {
            id = (Integer) new Random().nextInt();
        }
        ReflectionTestUtils.setField(args[0], "id", id);
    }

    Set<ConstraintViolation<T>> validations = validator.validate((T)args[0]);
    if (!validations.isEmpty()){
        throw new IllegalStateException("Object failed validations (it would also fail on a db): "+validations);
    }
    for (Method method: args[0].getClass().getDeclaredMethods()){
        if (method.isAnnotationPresent(Basic.class)){
            Annotation a = method.getAnnotation(Basic.class);
            if (!(boolean) AnnotationUtils.getValue(method.getAnnotation(Basic.class), "optional")){
                if (ReflectionTestUtils.invokeGetterMethod(args[0], method.getName()) == null){
                   throw new IllegalStateException(args[0].getClass().getSimpleName()+"."+method.getName() + " returned null, but marked with @Basic(optional=false) - it would also fail on a db: "+validations);

                }
            }
        }
    }
    fakeDatabase.put(id, (T) args[0]);

    return (T) args[0];
}

public void customMethods() {
    // override here if you want
}

}

如果您有@Entity注释POJO,那么使用hibernate库,您可以要求它导出到HSQLDB脚本然后使用它。例如,您通过以下方式导出:

Configuration configuration = new Configuration();
        try {
            classes().forEach(cl -> {
                configuration.addAnnotatedClass(cl);
            });
            configuration.setProperty("hibernate.dialect", HSQLCustomDialect.class.getName());
            SchemaExport schemaExport = new SchemaExport(configuration);
            schemaExport.setOutputFile("someFileName.sql");
            schemaExport.setFormat(false);
            schemaExport.setDelimiter(";");
            schemaExport.execute(true, false, false, true);

然后您将使用spring为您插入该SQL脚本:

@ActiveProfiles("test")
@SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)
 @SqlGroup({
    @Sql(statements = "DROP SCHEMA PUBLIC CASCADE"),
    @Sql(scripts = "classpath:yourGeneratedSQL.sql"),
})

公共类DAOIntegrationTest {