飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

功能和集成测试 (FIT) 框架

时间:2021-12-29  作者:电脑狂魔  

我们将首先向您介绍高级架构洞察力。然后,我们将引导您完成框架的开发。

我们将介绍测试事务 SDK 所涉及的各种问题及其解决方案,并在整个框架的开发过程中使用相关示例。尽管本博客并未提及该框架的所有技术细节,但它肯定会尝试提供该框架的整体图景。

Couchbase 目前在多个 SDK 中提供交易:Java、Dotnet 和 CXX,并计划在不久的将来支持其他 SDK。提供相同功能的测试 SDK 在测试自动化过程中会带来多个问题。测试自动化冗余是每个人都会想到的第一个。除了冗余之外,我们还必须确保所有 SDK 都具有类似的 Couchbase 交易实现。例如:所有 SDK 都完全相同地完成错误处理。这些只是几个问题。本博客主要关注交易,将提供我们在测试多个 SDK 时会遇到的各种问题,以及我们在 Couchbase 是如何解决这些问题的。

Couchbase 交易简介

分布式 ACID 事务确保当需要修改多个文档时,只有所有的修改成功才能证明任何修改都是正确的,要么所有修改都成功发生,要么都不发生。可以在此处找到符合 ACID 属性的 Couchbase 。

分布式环境中的事务

单节点集群:  Couchbase 事务在多节点和单节点集群上工作。但是,Couchbase应该支持集群配置。

N1QL 查询的事务支持:确保集群中至少有一个节点具有查询服务

Couchbase 交易 SDK 测试

在框架的设计阶段,对测试计划及其自动化的深入分析给我们带来了多重挑战。以下是一些主要挑战及其解决方案。展望未来,我们将讨论问题及其解决方案。随着这些问题的讨论,我们也会看到框架的开发进度。

问题一:冗余问题

在 Couchbase,我们目前支持 3 种不同 SDK 中的交易:Java、Dotnet 和 CXX。在不久的将来,我们将支持更多的 SDK,包括 Golang。这显然给 QE 带来了冗余问题,即我们可能必须为每个 SDK 多次自动化相同的测试用例。 

解析: 每个测试用例可以分为 3 个主要部分:

  1. 测试准备,例如:测试数据、测试基础设施等。 

  2. 测试执行例如:事务操作执行即插入、替换等。 

  3. 结果验证。

仔细观察这 3 个部分会发现 SDK 测试只涉及测试执行阶段,而测试准备和结果验证实际上独立于 SDK,即使用哪个 SDK 并不重要。这导致我们设计了一个由驱动程序和执行程序两部分组成的框架。驱动程序负责完整的测试准备和结果验证。驱动程序驱动测试执行,但只是抽象的(我们将在下面了解更多信息),即向执行者发出命令,执行者接受这些并执行实际的测试执行。

FIT 框架 是在客户端-服务器模型中设计的,其中驱动程序充当客户端,执行器充当服务器。

域名


驱动程序: 包括所有测试准备和结果验证。所有测试都是经典的 Junit 测试,可以作为单个单独的测试或特定的测试套件或整个测试套件执行。所有的测试只写一次。这些测试可以重复用于所有 SDK。

Performer:这是一个简单的应用程序,为每个 SDK 编写一次。在驱动程序中,每个测试都以 Java 对象的形式进行模制并发送到gRPC层。gRPC 协议负责将此 Java 对象转换为特定于语言的测试对象并将其发送给执行者。执行者获取这个测试对象,读取指令,并执行所需的事务操作。交易完成后,执行者检索结果并通过 gRPC 协议将其发送回驱动程序

驱动程序接收到结果对象后,驱动程序继续进行结果验证测试 

开发过程:既然我们对驱动程序和执行程序如何在 FIT 框架内运行有了一个顶级概念,让我们通过几个简单的示例测试来看看它的技术方面以及它们如何相互交互。

示例 1:使用单个操作测试事务:基本的“替换”操作 

驱动程序代码: 


   @Test
   public void oneUpdateCommitted() {
       域名rt(docId, initial);   // Test Preparation
       TransactionResult result = 域名te(shared)
           .replace(docId, updated)
           .sendToPerformer();      // Test Execution
           //Result validation
       assertCompletedInSingleAttempt(shared, collection, result);      
       assertDocExistsAndNotInTransactionAndContentEquals(collection, docId, updated);
   }


如您所见,所有测试总是只编写一次,并且是 Junit 测试。

测试准备和结果验证独立于 SDK,因此在 Junit 测试本身中完成。 

但是,测试执行部分是以抽象的方式完成的。在顶部,它看起来像是在驱动程序本身中执行的。但是它在Remote过程调用之后从事分布式计算。整个测试使用 Transaction Builder 类转换为 Java 对象,在我们的 FIT 框架中命名为“TransactionBuilder”对象,然后使用“sendToPerfomer”方法通过 gRPC 层发送到执行者。 

在我们尝试测试事务替换操作的这个示例中,我们创建了一个包含所有详细信息的 Java 对象:

  1. 交易应该在其上执行的文档 ID。 

  2. 更新值,即我们希望交易强加给文档的新值

  3. 在这种情况下,事务操作是“替换”。

一旦你创建了这样一个 java 对象,sendToPerformer 就会调用gRPC调用将它发送到服务器。

因此,在第一步中,执行者读取测试对象并检查它需要执行的操作。在我们的示例中,由于它是一个替换操作,域名eplace() 将返回 true,域名nsert()、域名emove() 等将返回 false。

在替换代码块中,执行者检索文档 id、文档的位置和文档的更新值。一旦检索到所有相关信息,执行者就会执行事务,即 域名ace() 操作。

一旦事务成功执行,结果将发送回驱动程序,然后驱动程序类似地从结果对象中检索相关信息并执行结果验证。

测试的功能示例: 该框架的这一特性帮助我们测试事务 SDK,不仅针对文档内容,还针对事务元数据,即在需要的地方提供预期的元数据,并在需要的地方删除元数据。

现在我们对 FIT 框架有了一些技术见解,让我们更详细地了解一下:

Eg2:使用多个操作测试事务:

驱动程序代码:


   @Test
    void insertReplaceTest() {
        域名rt(docId2, initial); //Test preparation
        TransactionResult result = 域名te(shared)
                .insert(docId1, initial)
                .replace(docId2, updated)
                .sendToPerformer();  //Actual Test Execution
        //Result validation
        assertCompletedInSingleAttempt(shared, collection, result);      
        assertDocExistsAndNotInTransactionAndContentEquals(collection, docId1, initial);
        assertDocExistsAndNotInTransactionAndContentEquals(collection, docId2, updated);
    }

在这个测试中,事务使用 docId1 插入文档并使用 docId2 替换文档。因此,我们必须将“插入”和“替换”添加到测试对象中,并将每个这些操作所需的所有信息发送给执行者。


由于我们已经插入并替换了操作。插入将返回真,执行者检索所需信息并执行插入,然后 域名ace() 将返回真,然后执行者执行替换操作并将结果返回给驱动程序。

功能测试示例:最初我们不支持在同一事务中对同一文档进行所有有效的多事务操作。添加后,我们可以使用框架的这种行为来测试该功能。此外,还测试了对不同文档的常规多次交易操作。交易无法替换/删除文件和到期等问题在此支持下得到了很好的测试

我们在两个示例中都看到了交易有望成功的情况。但是,对于负面情况,我们预计事务会引发错误/异常。这些错误/异常是 SDK 特定的,因此它们需要由执行者处理。所以驱动程序需要告诉执行者什么错误/异常,而执行者需要做这个验证

问题 2:错误验证

  1. 对于不同的原因,事务应该了解原因并抛出相关的错误/异常。所以我们不仅要测试事务的功能,还要测试它们抛出的错误代码和异常。

  2. 对于每个错误/异常,事务异常处理是不同的。例如:文档未找到异常的处理方式应与某些瞬态异常不同。

  3. 即使对于相同的异常,它发生的事务阶段也会导致对其进行不同的处理。例如:插入/替换的写-写冲突的处理方式与忘记操作不同。

解决方案: 驱动程序应将原因和异常的代码发送给执行者。执行者将读取故障原因的代码并使用 Hooks 诱导它们。

Hooks 是内部 Couchbase 实现,有助于测试失败场景。在我们下面的示例中,我们只是尝试在插入文档之前创建一个过期时间

一旦引发失败,执行者还将期望此事务应该抛出的错误/异常。因此,执行者将检索异常并对其进行验证。如果未抛出异常或抛出不正确的期望,则执行者将无法通过测试并将结果对象中的失败发送给驱动程序。驱动程序读取这个结果对象并给出预期的和实际的故障作为输出。

Eg3:测试否定案例场景:

驱动程序代码:


@Test
    void expiryDuringFirstOpInTransactionEntersExpiryOvertime() {
        String docId = 域名d(collection, 0);
        TransactionResult result = 域名te(shared)
                .injectExpiryAtPoint(域名_INSERT)
                .insert(docId, updated, EXPECT_FAIL_EXPIRY)
                .sendToPerformer();
        域名rtNotStarted(collection, result);
        域名rtDocDoesNotExist(collection, docId);
        assertEquals(域名PTION_EXPIRED, 域名xception());
    }

所以在这个测试中,驱动程序告诉执行者执行插入,然后期望事务在这个插入操作期间到期。我们发送代码“EXPECT_FAIL_EXPIRY”以将其传达给表演者。


测试的功能示例: 所有错误/异常处理和错误代码都经过测试。与任何不支持或与商定的错误处理功能不同步的 SDK 相关的功能测试已经完成。在框架的这种支持下,交易到期功能得到了很好的测试。  

问题 3:版本管理

我们必须测试交易的不同库版本,并且以后的版本会具有以前版本中没有的新功能。因此,测试框架必须了解哪些功能不受支持并避免运行这些测试。 

解决方案: 我们使用了 Junit5 条件测试执行扩展。每个测试套件都用“@IgnoreWhen”条件注释。这里提到的所有条件都将被检索并在我们覆盖的“ExecuteWhen”方法中使用。在驱动程序开始执行任何测试之前,它会联系执行者并获得它支持的所有功能。“ExecuteWhen”方法将使用“@IgnoreWhen”中提供的信息和执行者能力来决定是否需要执行或忽略测试套件。 

测试的功能示例: 开发功能比其他 SDK 晚一点的 SDK,可以使用 FIT 的此功能在实现这些功能后打开这些测试。这有助于我们进行测试驱动的开发。

问题 4:多个执行者: 并行事务

事务可以并行执行。Couchbase 交易确认了隔离模型。即当在同一组文档上执行两个或多个事务时,不应导致脏写/读。为了测试这一点,如果我们随机并行运行“n”个事务,万一它导致文档损坏,将很难知道究竟是什么导致了损坏。每个事务可以有多个操作,每个操作都有多个阶段。如果我们需要解决问题,我们需要知道这些事务在什么操作和什么阶段发生冲突。

解决方案: 我们设计了一个闩锁机制,其中一个事务执行几个操作或一个操作中的几个阶段,并通知另一个事务开始。第一个事务现在等待第二个事务运行并达到所需的阶段。一旦第二笔交易到达特定阶段,它就会通知第一笔交易继续进行。即使对于并行事务,这也是有效的情况。所以我们想出了一组可能导致写-写冲突或脏读的冲突点,并使用锁存器来自动化这些测试用例。

测试功能/发现错误的示例: 使用此支持测试并发事务

问题 5:多个执行者:不同 SDK 的并行事务

由于我们支持多个 SDK 中的交易,因此在测试不同 SDK 中交易的并行执行时可以使用相同的逻辑。例如:Java 事务与 CXX 事务。在上面的例子中,我们连接到同一个执行者,因为我们想为同一个 SDK 运行并行事务。在这种情况下,TXN A 将连接到执行者 A(假设执行者 A 正在使用 Java 事务),而 Txn B 将连接到执行者 B(运行 CXX 事务)。

测试的功能/发现的错误示例: 使用此支持测试了与不同 SDK 客户端的并发事务。还帮助我们确保交易元数据完好无损。

结论

FIT框架的这种架构设计不仅帮助我们解决了给我们带来的问题,还帮助我们进行了效率低下的测试自动化,帮助了测试驱动开发(TDD)模式下的事务开发。 

高效的测试自动化:将框架拆分为单个驱动程序和多个执行程序帮助我们独立开发框架的各个部分。每个 SDK 的开发人员都为我们提供了执行器,QE 可以专注于测试自动化,即驱动程序。开发人员还可以将单元测试添加到驱动程序中,以便所有事务测试都由这个单一框架处理。

测试驱动开发 (TDD):我们已经开发了 Java 执行器并编写了所有需要的测试,以签署 Java SDK 的最初几个版本的事务。Java SDK 发布后,开始开发其他事务 SDK,即 CXX 和 dot net,我们的开发团队必须在重用相同的驱动程序应用程序的同时开发执行程序应用程序。这有助于他们以 TDD 方式开发他们的 SDK。

湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。