转载

Pact Go:Consumer Driven Contracts 测试框架 Pact 的 Go 语言版本

Pact Go

Golang version of Pact . Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service Provider project.

Implements Pact Specification v2 , including flexible matching .

From theRuby Pact website:

Define a pact between service consumers and providers, enabling "consumer driven contract" testing.

Pact provides an RSpec DSL for service consumers to define the HTTP requests they will make to a service provider and the HTTP responses they expect back. These expectations are used in the consumers specs to provide a mock service provider. The interactions are recorded, and played back in the service provider specs to ensure the service provider actually does provide the response the consumer expects.

This allows testing of both sides of an integration point using fast unit tests.

This gem is inspired by the concept of "Consumer driven contracts". See http://martinfowler.com/articles/consumerDrivenContracts.html for more information.

Read Getting started with Pact for more information on how to get going.

Pact Go:Consumer Driven Contracts 测试框架 Pact 的 Go 语言版本 Pact Go:Consumer Driven Contracts 测试框架 Pact 的 Go 语言版本

Installation

  • Download arelease for your OS.
  • Unzip the package into a known location, and add to the PATH .
  • Run pact-go to see what options are available.

Running

Due to some design constraints, Pact Go runs a two-step process:

  1. Run pact-go daemon in a separate process/shell. The Consumer and Provider DSLs communicate over a local (RPC) connection, and is transparent to clients.
  2. Create your Pact Consumer/Provider Tests. It defaults to run on port 6666 .

NOTE: The daemon is completely thread safe and it is normal to leave the daemon running for long periods (e.g. on a CI server).

Example - Consumer

  1. Start the daemon with ./pact-go daemon .
  2. cd <pact-go>/examples .
  3. go run consumer.go .
import "github.com/pact-foundation/pact-go/dsl" import ...  func TestSomeApi(t *testing.T) {      // Create Pact, connecting to local Daemon     // Ensure the port matches the daemon port!     pact := &dsl.Pact{         Port:     6666,                          Consumer: "My Consumer",         Provider: "My Provider",     }     // Shuts down Mock Service when done     defer pact.Teardown()      // Pass in your test case as a function to Verify()     var test = func() error {         _, err := http.Get("http://localhost:8000/")         return err     }      // Set up our interactions. Note we have multiple in this test case!     pact.         AddInteraction().         Given("User Matt exists"). // Provider State         UponReceiving("A request to login"). // Test Case Name         WithRequest(&dsl.Request{             Method: "GET",             Path:   "/login",         }).         WillRespondWith(&dsl.Response{             Status: 200,         })      // Run the test and verify the interactions.     err := pact.Verify(test)     if err != nil {         t.Fatalf("Error on Verify: %v", err)     }     // You should now have a pact file in the file `<pact-go>/pacts/my_consumer-my_provider.json` }

Example - Provider

Start your Provider API:

mux := http.NewServeMux() mux.HandleFunc("/setup", func(w http.ResponseWriter, req *http.Request) {     w.Header().Add("Content-Type", "application/json") }) mux.HandleFunc("/states", func(w http.ResponseWriter, req *http.Request) {     fmt.Fprintf(w, `{"My Consumer": ["Some state", "Some state2"]}`)     w.Header().Add("Content-Type", "application/json") }) mux.HandleFunc("/someapi", func(w http.ResponseWriter, req *http.Request) {     w.Header().Add("Content-Type", "application/json")     fmt.Fprintf(w, `         [             [                 {                     "size": 10,                     "colour": "red",                     "tag": [                         [                             "jumper",                             "shirt"                         ],                         [                             "jumper",                             "shirt"                         ]                     ]                 }             ]         ]`) }) go http.ListenAndServe(":8000"), mux)

Note that the server has 2 endpoints: /states and /setup that allows the verifier to setup provider states before each test is run.

You can now tell Pact to read in your Pact files and verify that your API will satisy the requirements of each of your known consumers:

response := pact.VerifyProvider(&types.VerifyRequest{     ProviderBaseURL:        "http://localhost:8000",     PactURLs:               []string{"./pacts/my_consumer-my_provider.json"},     ProviderStatesURL:      "http://localhost:8000/states",     ProviderStatesSetupURL: "http://localhost:8000/setup", })

See the Skip()'ed integration tests for a more complete E2E example.

Matching (Consumer Tests)

In addition to verbatim value matching, you have 3 useful matching functions in the dsl package that can increase expressiveness and reduce brittle test cases.

  • dsl.Term(example, matcher) - tells Pact that the value should match using a given regular expression, using example in mock responses. example must be a string.
  • dsl.Like(content) - tells Pact that the value itself is not important, as long as the element type (valid JSON number, string, object etc.) itself matches.
  • dsl.EachLike(content, min) - tells Pact that the value should be an array type, consisting of elements like those passed in. min must be >= 1. content may be a valid JSON value: e.g. strings, numbers and objects.

Example:

Here is a complex example that shows how all 3 terms can be used together:

jumper := Like(`"jumper"`) shirt := Like(`"shirt"`) tag := EachLike(fmt.Sprintf(`[%s, %s]`, jumper, shirt), 2) size := Like(10) colour := Term("red", "red|green|blue")  match := formatJSON(     EachLike(         EachLike(             fmt.Sprintf(                 `{                     "size": %s,                     "colour": %s,                     "tag": %s                 }`, size, colour, tag),             1),         1))

This example will result in a response body from the mock server that looks like:

[   [     {       "size": 10,       "colour": "red",       "tag": [         [           "jumper",           "shirt"         ],         [           "jumper",           "shirt"         ]       ]     }   ] ]

See thematcher tests for more matching examples.

NOTE : One caveat to note, is that you will need to use valid Ruby regular expressions and double escape backslashes.

Read more aboutflexible matching.

Output Logging

Pact Go uses a simple log utility (logutils) to filter log messages. The CLI already contains flags to manage this, should you want to control log level in your tests, you can set it like so:

pact := &Pact{   ...     LogLevel: "DEBUG", // One of DEBUG, INFO, ERROR, NONE }

Contact

  • Twitter: @pact_up
  • Google users group: https://groups.google.com/forum/#!forum/pact-support

Documentation

Additional documentation can be found at the main Pact website and in thePact Wiki.

Developing

For full integration testing locally, Ruby 2.1.5 must be installed. Under the hood, Pact Go bundles thePact Mock Service and Pact Provider Verifier projects to implement up to v2.0 of the Pact Specification. This is only temporary, untilPact Reference work is completed.

  • Git clone https://github.com/pact-foundation/pact-go.git
  • Run make dev to build the package and setup the Ruby 'binaries' locally

Vendoring

We useGovend to vendor packages. Please ensure any new packages are added to vendor.yml prior to patching.

Roadmap

Theroadmap for Pact and Pact Go is outlined on our main website.

Contributing

SeeCONTRIBUTING.

原文  https://github.com/pact-foundation/pact-go
正文到此结束
Loading...