PactConsumerSwift 0.10.2

PactConsumerSwift 0.10.2

测试已测试
语言语言 SwiftSwift
许可 MIT
发布最后发布2021年9月
SPM支持 SPM

Andrew SpinksMarko JustinekMarko Justinek维护。



  • andrewspinks 和 markojustinek

Pact Consumer Swift

Build Codecov Carthage compatible Swift Package Manager Swift Badge w/ CocoaPod Version Badge w/ Supported Platforms License: MIT Twitter


新增功能包括Pact Specification v3,简化了安装流程和更好地管理模拟服务器进程,当前正在积极开发中,可在PactSwift找到。我们目前正在寻找试用并反馈的人。

此库为创建消费者Pacts提供了Swift/Objective C DSL。它支持在基于HTTP(以及某些实现中的消息队列)的集成间进行依赖系统之间的消费者驱动合同测试

为什么需要它? 为了测试您的应用程序和服务之间的通信边界。您可以在此了解如何在移动环境中使用Pact: Yow! Connected 2016 Andrew Spinks - 增加您服务集成的信心

实现了Pact Specification v2,包括灵活匹配

此DSL依赖于Ruby的pact-ruby-standalonebrew tap)来为测试提供模拟服务。

安装

注意:关于从0.2到0.3的更新,请参阅升级部分。

安装 Pact Mock Service

Homebrew

brew tap pact-foundation/pact-ruby-standalone
brew install pact-ruby-standalone

以下工具将会被安装:

pact
pact-broker
pact-message
pact-mock-service
pact-provider-verifier
pact-publish
pact-stub-service

手动安装

或者,您可以为您所在的平台下载并安装pact-ruby-standalone存档,并按照写在Pact Ruby Standalone 版本说明中的安装说明进行安装。

Xcode 设置

在 Xcode 中,编辑您的方案并添加针对 Testpre-post-actions 以启动和停止 pact-mock-service。确保您在选择下拉菜单中选择了您的目标 Provide build settings from

# Pre-actions
PATH=/path/to/your/standalone/pact/bin:$PATH
pact-mock-service start --pact-specification-version 2.0.0 --log "${SRCROOT}/tmp/pact.log" --pact-dir "${SRCROOT}/tmp/pacts" -p 1234

# Post-actions
PATH=/path/to/your/standalone/pact/bin:$PATH
pact-mock-service stop

注意:您的生成的 Pact 文件将存放在 "${SRCROOT}/tmp/pacts" 文件夹中。

Xcode Scheme Test Pre-actions

将 PactConsumerSwift 库添加到您的项目中

使用 Carthage

  • 查看使用 Carthage 对 iOS 目标使用 pact-consumer-swift 的示例项目,请参阅 PactSwiftExample Swift, Carthage Example - Build Status
  • 查看使用 Carthage 对 macOS 目标通过 pact-consumer-swift 的示例项目,请参阅 PactMacOSExample Build

使用 CocoaPods

  • 查看使用 CocoaPods 对 iOS 目标使用 pact-consumer-swift 的示例项目,请参阅 PactObjectiveCExample Build Status

使用 Swift Package Manager

  • 查看使用 Swift Package Manager 对终端运行的程序使用 pact-consumer-swift 库的示例项目,请参阅 PactSwiftPMExample Build

编写 Pact 测试

用 Swift 进行测试

编写与以下类似的单元测试(注:此示例使用 Quick 测试框架)

import PactConsumerSwift

...
  beforeEach {
    animalMockService = MockService(provider: "Animal Service", consumer: "Animal Consumer Swift")
    animalServiceClient = AnimalServiceClient(baseUrl: animalMockService!.baseUrl)
  }

  it("gets an alligator") {
    animalMockService!.given("an alligator exists")
                      .uponReceiving("a request for an alligator")
                      .withRequest(method:.GET, path: "/alligator")
                      .willRespondWith(status:200,
                                       headers: ["Content-Type": "application/json"],
                                       body: ["name": "Mary"])

    //Run the tests
    animalMockService!.run { (testComplete) -> Void in
      animalServiceClient!.getAlligator { (alligator) in
        expect(alligator.name).to(equal("Mary"))
        testComplete()
      }
    }
  }

在运行函数中可以包含一个可选的 timeout(秒)参数。默认值为30秒。

...
    animalMockService!.run(timeout: 60) { (testComplete) -> Void in
      animalServiceClient!.getAlligator { (alligator) in
        expect(alligator.name).to(equal("Mary"))
        testComplete()
      }
    }

使用Objective-C进行测试

编写与以下类似的单元测试

@import PactConsumerSwift;
...
- (void)setUp {
  [super setUp];
  self.animalMockService = [[MockService alloc] initWithProvider:@"Animal Provider"
                                                        consumer:@"Animal Service Client Objective-C"];
  self.animalServiceClient = [[OCAnimalServiceClient alloc] initWithBaseUrl:self.animalMockService.baseUrl];
}

- (void)testGetAlligator {
  typedef void (^CompleteBlock)();

  [[[[self.animalMockService given:@"an alligator exists"]
                             uponReceiving:@"oc a request for an alligator"]
                             withRequestHTTPMethod:PactHTTPMethodGET
                                              path:@"/alligator"
                                             query:nil headers:nil body:nil]
                             willRespondWithHTTPStatus:200
                                               headers:@{@"Content-Type": @"application/json"}
                                                  body: @"{ \"name\": \"Mary\"}" ];

  [self.animalMockService run:^(CompleteBlock testComplete) {
      Animal *animal = [self.animalServiceClient getAlligator];
      XCTAssertEqualObjects(animal.name, @"Mary");
      testComplete();
  }];
}

在运行函数中可以包含一个可选的 timeout(秒)参数。默认值为30秒。

...
  [self.animalMockService run:^(CompleteBlock testComplete) {
      Animal *animal = [self.animalServiceClient getAlligator];
      XCTAssertEqualObjects(animal.name, @"Mary");
      testComplete();
  } timeout:60];
}

使用XCTest进行测试

编写与以下类似的单元测试

import PactConsumerSwift
...
  var animalMockService: MockService?
  var animalServiceClient: AnimalServiceClient?

  override func setUp() {
    super.setUp()

    animalMockService = MockService(provider: "Animal Provider", consumer: "Animal Service Client")
    animalServiceClient = AnimalServiceClient(baseUrl: animalMockService!.baseUrl)
  }

  func testItGetsAlligator() {
    // Prepare the expecated behaviour using pact's MockService
    animalMockService!
      .given("an alligator exists")
      .uponReceiving("a request for alligator")
      .withRequest(method: .GET, path: "/alligator")
      .willRespondWith(status: 200,
                       headers: ["Content-Type": "application/json"],
                       body: [ "name": "Mary" ])

    // Run the test
    animalMockService!.run(timeout: 60) { (testComplete) -> Void in
      self.animalServiceClient!.getAlligator { (response) -> in
        XCTAssertEqual(response.name, "Mary")
        testComplete()
      }
    }
  }
  ...

在运行函数中可以包含一个可选的 timeout(秒)参数。默认值为30秒。

...
    // Run the test
    animalMockService!.run(timeout: 60) { (testComplete) -> Void in
      self.animalServiceClient!.getAlligator { (response) -> in
        XCTAssertEqual(response.name, "Mary")
        testComplete()
      }
    }

关于如何测试 https 的示例,请参阅 PactSSLSpec.swift

匹配

除了逐字匹配之外,在 Matcher 类中还有3个有用的匹配函数,可以增加表达性并减少脆弱的测试案例。

  • Matcher.term(matcher, generate) - 告诉Pact使用给定的正则表达式来匹配值,在模拟响应中用 generategenerate 必须是一个字符串。
  • Matcher.somethingLike(content) - 告诉Pact值本身并不重要,只要元素 类型(有效的JSON数字、字符串、对象等)本身匹配。
  • Matcher.eachLike(content, min) - 告诉Pact值应该是数组类型,由传入的类似元素组成。 min 必须大于等于1。 content 可以是有效的JSON值:例如字符串、数字和对象。

注意:需要注意的一个限制是,您将需要使用有效的Ruby 正则表达式,并使用双反斜杠进行转义。

请参阅 PactSpecs.swiftPactObjectiveCTests.m 以了解如何期待错误响应、如何使用查询参数和匹配器。

有关请求/响应匹配的更多信息,请参阅[匹配][getting_started/matching]。

在你的持续集成中使用

Xcode的预操作后操作不会尊重非零脚本退出状态,因此如果您在发布到Pact代理失败时,您的构建也不会失败。如果您希望在CI过程中将Pact文件上传到Pact代理,我们建议您在CI工作流中创建一个专门的步骤来执行此操作。

请见pact-ruby-standalone页面获取安装说明和如何使用pact-broker客户端的信息。

验证您的客户端与您正在集成的服务

如果您的设置正确,并且您的测试是对打包模拟服务器运行的,那么您应该在这里看到日志文件:$YOUR_PROJECT/tmp/pact.log以及生成的pacts在这里:$YOUR_PROJECT/tmp/pacts/...

将生成的pacts文件发布到您的Pact代理托管Pact代理,这样您的API提供者就可以始终从单一位置检索它们,即使pacts发生更改。或者,甚至只需通过简单地将pacts文件发送给您的API提供者开发者,让他们可以在他们的API响应测试中使用它们。有关更多信息,请参阅验证pacts。有关具有Ruby后端服务的端到端示例,请参阅KatKit示例

还可以查看关于使用带有提供者状态的这个基于Docker的Node.js服务的文章。

更多阅读

  • Pact网站Pact
  • Swift库在底层使用的Pact模拟服务器Pact模拟服务
  • 用于管理生成的pacts文件的Pact代理(因此您不必手动复制它们!)Pact代理

贡献

请阅读CONTRIBUTING.md