如何对REST Web服务进行单元测试?


16

我是单元测试的新手,我有一个REST Web方法,它仅调用DB并填充DTO。伪代码是

public object GetCustomer(int id)
{
  CustomerDTO objCust = //get from DB
  return objCust;
}

我的疑问是如何编写针对这些方法的测试以及要包括的测试类型(集成/单元)。对于单元测试,是否需要命中数据库。如果是这样,并且我传递了一个客户ID并执行了一些断言,则数据可能会更改,最终导致失败。

我想我在这里缺少了解这些概念的内容。


5
在发布的代码中,要测试的内容包括:(1)可以使用int作为参数调用GetCustomer吗?(2)它是否返回CustomerDTO对象?(3)是否按预期从数据库中填充了该对象。(4)如果使用与有效客户不对应的int进行调用,是否会发生预期的行为?这些都与REST无关。当您准备编写响应RESTful请求的代码时,您将为此编写测试。
DavidO 2013年

@DavidO:“是否按预期从数据库中填充了该对象。” 绝对不是单元测试(关于OP的代码)。那是一个集成测试。
扁平化,

是的,你是对的。如果我可以返回并更改注释以提及在集成测试中,您将验证数据库组件,否则将对其进行模拟,那么我将进行编辑,但是如果有5分钟,则注释的编辑窗口将注释设为6几年前。:)
DavidO

Answers:


18

在进行单元测试时,不希望您使用数据库进行测试,或者至少不要对尚未准备进行单元测试的数据库进行测试。使用数据库进行测试,并因此同时测试应用程序的不同层通常被视为集成测试。使用单元测试时,您应该仅测试方法的作用,根据不同参数返回的结果以及何时(或不应该)失败。

非常期望在您的方法中可以从其他类调用X方法。您没有测试这些X方法,因此您需要模拟这些方法。

我想您正在用Java编写代码,在这种情况下,您将拥有诸如Mockito之类的出色模拟框架,这可能会对您有所帮助。无论您是否使用模拟框架都是您的选择,我只是说它们会为您节省很多时间,至少我提到的框架并不复杂。

如果您只想编写自己的模拟程序进行实验,那么假设您具有以下CustomerRepository类:

public class CustomerRepository {
 public CustomerDTO getCustomer(int id) {
   ...
 }
}

您可以CustomerRepository通过以下方式编写自己的模拟类和肮脏类:

public class MockedCustomerRepository extends CustomerRepository {
 public boolean bThrowDatabaseException;
 public boolean bReturnNull;
 public boolean bReturnCustomerWrongId;
 public boolean bReturnCustomerWithId;
 public CustomerDTO getCustomer(int id) {
  if(bThrowDatabaseException) { 
    throw new DatabaseException("xxx"); 
  } else if(bReturnNull) { 
    return null; 
  } else if(bReturnCustomerWrongId) { 
    throw new CustomerNotExistException(id);
  } else if(bReturnCustomerWithId) { 
    return new CustomerDTO(id); 
  }
 }
}

然后,在测试用例中,基本上将您的“标准”实例替换CustomerRepository为模拟实例,该实例将允许您测试方法的各种结果getCustomer

public class CustomerRestTest {
  public void testGetCustomer_databaseFailure() {
    MockedCustomerRepository dto = new MockedCustomerRepository();
    dto.bThrowDataBaseException = true;
    yRestClass rest = new MyRestClass();
    rest.dto = dto;
    rest.getCustomer(0);
    // depending on what you do in your getCustomer method, you should check if you catched the exception, or let it pass, etc.. Make your assertions here

  public void testGetCustomer_customerNotExist() {
    // etc.
  }
}

通常,每种测试方法都只能测试一件事,这有助于使测试范围缩小,并专注于一项任务。

我将重复一遍:-)编写一个完整的模拟类需要花费一些时间。考虑使用模拟框架,编写代码的人越少,产生的错误就越少,对吗?模拟抛出异常或为给定参数返回给定值的方法简直是小菜一碟,需要2或3行(至少包含嘲笑)

希望这有助于测试您的REST方法。


4
通常,您的DTO类中没有逻辑,尤其是没有与数据存储交互的逻辑。
JustAnotherUserYouMayKnowOrNot 2013年

1
这只是一个例子,但是你是绝对正确的。我将更改示例,以便它们更适合理论。
Jalayn 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.