简体   繁体   English

用于连接到数据库的Rest API的Junit测试用例

[英]Junit test cases for Rest APIs connecting to DB

I have a JAVA rest service that interacts with the database , does some manipulation and returns the data. 我有一个与数据库交互,进行一些操作并返回数据的JAVA rest服务。

I am trying to write test cases for these APIs. 我正在尝试为这些API编写测试用例。

I am trying to use the below link for reference to implement this. 我正在尝试使用以下链接作为参考来实现此目的。

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

Here, calls made to the database are suppressed and the dto is mocked with made up values. 在这里,将禁止对数据库的调用,并使用组合值模拟dto。

Is there an alternate method where we actually get to run the queries w/o talking to the db , (an in-memory db may be? ) 有没有一种替代方法,我们实际上可以在不与db对话的情况下运行查询(可能是内存db?)

Any code sample or reference would be of great help. 任何代码示例或参考都将有很大帮助。

HSQLDB is one in memory db i'm familiar with. HSQLDB是我熟悉的内存数据库之一。 The examples showed here against HSQLDB used with hibernate and JPA. 此处显示的示例针对与hibernate和JPA一起使用的HSQLDB。 http://uaihebert.com/tdd-with-hsqldb-jpa-and-hibernate/ . http://uaihebert.com/tdd-with-hsqldb-jpa-and-hibernate/

However i think it'll be useful to ask why you would prefer connecting to an in memory db than mocking the db in your situation? 但是,我认为询问您为什么更喜欢连接到内存中的数据库而不是根据您的情况模拟数据库是有用的? It boils down to what scope of testing unit/integration you are trying to achieve. 归结为您要达到的测试单元/集成范围。 What are you trying to test the manupilation logic in the rest layer? 您正在尝试什么测试其余层的操作逻辑? Where mocking is sufficient. 嘲笑就足够了。 Are you trying to test how the rest handles the data access behavior, such as db error handling etc, than in memory might be slightly better. 您是否正在尝试测试其余部分如何处理数据访问行为(例如db错误处理等),所以比在内存中可能要好一些。 Is the thing you are testing dependent on data setup/ testing data setup , in which case in memory db might be closer, since you can use the same/similar sql creation to test in inmemory db. 您要测试的东西是否取决于数据设置/测试数据设置,在这种情况下,内存db中的距离可能会更近,因为您可以使用相同/相似的sql创建来在内存db中进行测试。

For a pure HashMap solution, something like this would work though then you would loose access to SQL query functionality (unless you mock the query too). 对于纯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
}

} }

If you had @Entity annotated POJOs, then with say hibernate library you can ask it to export to HSQLDB script and then use that. 如果您有@Entity注释的POJO,则可以使用休眠库说出它可以导出到HSQLDB脚本,然后使用它。 Eg you export via: 例如,您通过以下方式导出:

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

and thereafter you would use spring to insert that SQL script for you: 然后,您将使用spring为您插入该SQL脚本:

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

public class DAOIntegrationTest { 公共类DAOIntegrationTest {

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

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