pact-go

command module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 6, 2016 License: MIT Imports: 1 Imported by: 0

README

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 the Ruby 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.

wercker status Coverage Status Go Report Card GoDoc

Installation

  • Download a release 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 the matcher 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 about flexible 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

Documentation

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

Developing

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

Vendoring

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

Roadmap

The roadmap for Pact and Pact Go is outlined on our main website.

Contributing

See CONTRIBUTING.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL