【问题标题】:Unit Testing an SOA WCF system...finding it difficult to get decent coverage对 SOA WCF 系统进行单元测试...发现很难获得良好的覆盖率
【发布时间】:2010-03-01 23:37:39
【问题描述】:

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

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

在 Visual Studio 中运行任何单元测试几乎是不可能的,因为几乎做任何事情都会导致某种跨服务调用。如果它不是数据访问它的其他服务之一。我认为我们可以获得大约 5% 的覆盖率。

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

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

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

【问题讨论】:

    标签: wcf unit-testing soa


    【解决方案1】:

    我不得不说,对 SOA 进行单元测试很像对其他任何东西进行单元测试。如果有的话,它应该更容易,因为它迫使你隔离依赖关系。

    单元测试不应该通常跨越服务边界。确实,服务 的想法是它是完全独立且不透明的。您直接测试服务 - 不是通过 WCF 通道,而是通过对构成服务的实际类进行单元测试。一旦您测试了服务本身(您应该能够获得接近 100% 的覆盖率),您就不需要在客户端测试中涉及它。

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

    有时让人们在这里绊倒的部分原因是,该服务可以根据具体信息做截然不同的事情。但是,涉及服务接口的客户端测试通常应该每个测试只测试一个或两个特定消息,因此很容易使用工具模拟给定客户端测试所需的确切请求/响应喜欢Rhino Mocks

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

    Mark Seeman 有一篇关于Unit-Testing Duplex WCF Clients 的非常好的博客文章,其中包含示例代码和所有内容。你应该读一读,我认为它会对你有所帮助。

    【讨论】:

      【解决方案2】:

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

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

      public class SomeSOA
      {
       public bool DoSomeDataAccess()
       {
        //call the real data service
        DataService dataService = new DataService()
        int getANumber = dataService.GetANumber();
        return getANumber == 2;
       }
      }
      

      略微重构以减少与 DataService 的耦合

      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
        }
      }
      

      现在使用重构的代码,您可以修改测试以使用可以返回预期结果的存根,而无需调用真正的 DataService。

      [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;
        }
      }
      

      现在所有这些都只是伪代码,但是将您的服务与 DataAccessServe 的实际实现分离将允许您对代码进行单元测试,而不是依赖真正的 DataAccessService 来让它们正确执行。

      希望这会有所帮助且有意义!

      【讨论】:

        【解决方案3】:

        这是我的建议:远离“单元测试”范式,并着手进行一套集成测试。

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

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

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

        您可能必须通过根据请求清除数据库来创建另一项服务于集成测试的服务。 (显然,您实际上不会部署那个......)

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

        【讨论】:

        • 我应该补充一点,更复杂的是,我们所做的很多事情都是通过 WCF 回调合同发布-订阅。很多事情都是异步的。那就是说您发布的内容确实很有意义,实际上我们可以通过这种方式进行很多测试。使用我们的 pub-sub 模型,从服务推送到客户端的大量“订阅更新”实际上是由用户操作触发的(通过 WCF 作为“命令消息”发送到我们的服务),所以即使我们是 pub-sub,我们也是在某种程度上仍然非常请求回复:当发送某些内容时,我们期待回复。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-04-06
        • 2013-01-05
        • 2016-05-04
        • 1970-01-01
        • 2019-01-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多