对SOA WCF系统进行单元测试…发现很难获得适当的覆盖率

对SOA WCF系统进行单元测试…发现很难获得适当的覆盖率,wcf,unit-testing,soa,Wcf,Unit Testing,Soa,我们目前正在用一个内置于.NET3.5中的现代SOA WCF系统替换一个有20年历史的基于C的系统。我们的行业需要严格的测试,包括良好的自动化单元测试。我们遇到了一些问题,但是对SOA系统进行单元测试的程度接近于对基于C的系统进行单元测试的程度 最大的一个问题是,系统中的大多数方法实际上都依赖于跨服务边界调用代码,例如,我们是高度数据驱动的,但我们不直接在系统中访问数据库:我们调用WCF数据访问服务 在VisualStudio中运行任何单元测试几乎都是不可能的,因为几乎任何操作都会导致某种类型的

我们目前正在用一个内置于.NET3.5中的现代SOA WCF系统替换一个有20年历史的基于C的系统。我们的行业需要严格的测试,包括良好的自动化单元测试。我们遇到了一些问题,但是对SOA系统进行单元测试的程度接近于对基于C的系统进行单元测试的程度

最大的一个问题是,系统中的大多数方法实际上都依赖于跨服务边界调用代码,例如,我们是高度数据驱动的,但我们不直接在系统中访问数据库:我们调用WCF数据访问服务

在VisualStudio中运行任何单元测试几乎都是不可能的,因为几乎任何操作都会导致某种类型的跨服务调用。如果它没有数据访问它的另一个服务。我估计我们可以得到大约5%的保险

我看到很多人在努力测试SOA,所以我认为这不是我们独有的。问题是QA会质疑为什么我们不对系统进行更多的单元测试

老实说,我认为VSTS单元测试更多的是回归测试,而不是验证(适合使用)工具。SOA单元测试有哪些选项?根据人们的经验,实现良好的覆盖率是否现实?有没有办法模拟数据访问服务(或任何服务:注意,我们不使用WCF代理)或者我们必须向QA解释单元测试能力在过去20年中已经倒退了


欢迎任何类型的建议,我想这是一个普遍的意见问题。

我的建议是:远离“单元测试”范式,开发一套集成测试

我假设您的系统有一个调用服务的前端

编写一个测试套件,实际连接到正在运行的服务(显然是在您的测试环境中),并进行与前端相同的调用序列

您的测试环境将在一个空的测试数据库上运行最新的服务。就像单元测试一样,每个测试都会进行调用,用它所需要的内容填充测试数据,调用功能,测试可见信息现在是否匹配它应该匹配的内容,然后再次清除数据库

您可能需要创建另一个服务,该服务通过在请求时清除数据库来为集成测试提供服务。(很明显,你不会真的部署那个…)


虽然它们不是“单元测试”,但您将得到全面的覆盖。

听起来您现在正在进行的测试是系统测试。测试方法调用外部源的地方。要真正对服务执行单元测试,您需要以某种方式抽象外部调用,以便可以模拟(例如)或存根对该服务的调用

例如,与数据访问服务紧密耦合的代码:

public class SomeSOA
{
 public bool DoSomeDataAccess()
 {
  //call the real data service
  DataService dataService = new DataService()
  int getANumber = dataService.GetANumber();
  return getANumber == 2;
 }
}
略微重构以减少与数据服务的耦合

public class SomeSOA
{
 IDataService _dataService;
 public SomeSOA(IDataService dataService)
{
  _dataService = dataService;
}
public SomeSOA() :this(new DataServiceWrapper()){}
 public bool DoSomeDataAccess()
 {
  int getANumber = _dataService.GetANumber();
  return getANumber == 2;
 }
}

public DataServiceWrapper : IDataService
{
  public int GetANumber()
  {
   // call the real data service
  }
}
现在,通过重新分解的代码,您可以修改测试以使用存根,该存根可以返回预期结果,而无需调用真正的数据服务

[TestMethod]
public void GetANumber_WithAValidReturnedNumber_ReturnsTure()
{

  IDataService dataService = new DataServiceFake();
  SomeSOA someSoa = new SomeSOA(dataService);
  Assert.IsTrue(someSoa.DoSomeDataAccess();
}

public class DataServiceFake :IDataService
{
  public int DoSomeDataAccess()
  {
    //return a fake result from the dataService.
    return 2;
  }
}
现在所有这些都只是伪代码,但将您的服务与DataAccessSerive的实际实现分离将允许您对代码进行单元测试,而不是依赖真正的DataAccessService来正确执行它们


希望这有帮助,有意义

我不得不说,对SOA进行单元测试与对其他任何东西进行单元测试非常相似。如果有什么问题的话,它应该更容易,因为它迫使您隔离依赖关系

单元测试通常不应该跨越服务边界。实际上,服务的概念是它是完全独立和不透明的。您可以直接测试服务—不是通过WCF通道,而是通过单元测试组成服务的实际类。一旦您测试了服务本身(您应该能够获得接近100%的覆盖率),就不需要将其包含在客户端测试中

对于客户端单元测试,可以模拟服务。WCF实际上使这对您来说非常容易,因为每个WCF客户机都实现了一个接口;如果您通常在代码中使用
FooService
FooServiceSoapClient
,请将其更改为使用代理类实现的相应
IFooService
FooServiceSoap
。然后,您可以编写自己的
MockFooService
,实现相同的接口,并在客户端测试中使用该接口

有时让人们感到困惑的一部分是,这项服务可以根据特定的信息做完全不同的事情。但是,涉及服务接口的客户机测试通常只应在每个测试中测试一个或两个特定消息,因此可以很容易地使用类似的工具模拟给定客户机测试所需的确切请求/响应

双工变得有点棘手,但请记住,双工服务也应该基于接口;甚至还引入了
iCalculator DuplexCallback
。回调将是接口,因此就像您可以在客户端模拟服务方法一样,您可以在服务端模拟客户端回调。只需使用用于服务单元测试的模拟/假回调


MarkSeeman有一篇很好的博客文章,里面有示例代码和所有内容。您应该仔细阅读一下,我想这会对您有所帮助。

我应该补充一点,更复杂的是,我们所做的很多事情都是通过WCF回调合同发布订阅。很多事情都是异步的。也就是说,你所发布的内容确实很有意义,我们确实可以用这种方式进行很多测试。在我们的发布-订阅模型中,从服务推送到客户端的许多“订阅更新”实际上都是由用户操作触发的(这些操作通过WCF作为“命令消息”发送到我们的服务),因此,即使我们是发布-订阅模型,在某种程度上我们仍然非常需要请求-回复:当发送某些内容时,我们希望得到回复。