A Go Lang implementation of the Ruby consumer driven contract library, Pact. Pact is based off the specification found at https://github.com/bethesque/pact_specification.
Currently pact-go is compatible with v1.1 of pact specification. The package is fairly stable but in beta phase, so potentially there could be breaking changes in future.
Read more about Pact and the problems it solves at https://github.com/realestate-com-au/pact
Please feel free to contribute, we do accept pull requests.
go get github.com/SEEK-Jobs/pact-go
Which may look something like this
package client
import (
"fmt"
"net/http"
"encoding/json"
)
type ProviderAPIClient struct {
baseURL string
}
type Resource struct {
Name string
}
func (c *ProviderAPIClient) GetResource(id int) (*Resource, error) {
url := fmt.Sprintf("%s/%d", c.baseURL, id)
req, _ := http.NewRequest("GET", url, nil)
client := &http.Client{}
resp, err:= client.Do(req)
if err != nil { return nil, err}
defer resp.Body.Close()
var res Resource
decoder := json.NewDecoder(resp.Body)
if err := decoder.Decode(&res); err != nil {
return nil, err
}
return &res, nil
}
In your test file describe and configure your pact
import (
pact "github.com/SEEK-Jobs/pact-go"
)
func buildPact() {
return pact.
NewConsumerPactBuilder(&pact.Config{PactPath: "./pacts"}).
ServiceConsumer("my consumer").
HasPactWith("my provider")
}
Add your test method to register and verify your interactions with provider
import (
"testing"
pact "github.com/SEEK-Jobs/pact-go"
"github.com/SEEK-Jobs/pact-go/provider"
"net/http"
)
func buildPact() {
return pact.
NewConsumerPactBuilder(&pact.Config{PactPath: "./pacts"}).
ServiceConsumer("consumer client").
HasPactWith("provider api")
}
func TestPactWithProvider(t *testing.T) {
builder := buildPact()
ms, msUrl := builder.GetMockProviderService()
request := provider.NewJSONRequest("GET", "/23", nil, nil)
header := make(http.Header)
header.Add("content-type", "application/json")
response := provider.NewJSONResponse(200, header)
response.SetBody(`{"name": "John"}`)
//Register interaction for this test scope
if err := ps.Given("there is a resource with id 23").
UponReceiving("get request for resource with id 23").
With(*request).
WillRespondWith(*response); err != nil {
t.Error(err)
t.FailNow()
}
//test
client := &ProviderClient{baseUrl: msUrl}
if res, err := client.GetResource(23); err != nil {
t.Error(err)
t.FailNow()
}
//Verify registered interaction
if err := ms.VerifyInteractions(); err != nil {
t.Error(err)
t.FailNow()
}
//Clear interaction for this test scope, if you need to register and verify another interaction for another test scope
ms.ClearInteractions()
//Finally, build to produce the pact json file
if err := builder.Build(); err != nil {
t.Error(err)
}
}
go test -v ./...
Build your api using any web framework like Goji, Gin, Gorilla or just net/http. Ensure your pipeline is composable to simplify testing and introduce mock behaviors.
Create a new test file in your service provider api to verify pact with the consumer.
import (
"testing"
pact "github.com/SEEK-Jobs/pact-go"
"net/httptest"
)
func TestProviderHonoursPactWithConsumer(t *testing.T) {
//Stand up a test server with the mux which has all your
//middlewares and handlers registered, for e.g.
//mux := http.NewServeMux()
//mux.HandleFunc("/user", handler)
server := httptest.NewServer(mux)
defer server.Close()
u, _ := url.Parse(server.URL)
verifier := pact.NewPactFileVerifier(nil, nil, pact.DefaultVerifierConfig).
HonoursPactWith("consumer client").
ServiceProvider("provider api", &http.Client{}, u).
//pact uri could be a local file
PactUri("./pacts/consumer_client-provider_api.json", nil).
//or could be a web url e.g. pact broker. You can also provide authorisation value in the config parameter
PactUri("http://pact-broker/pacts/provider/provider%20api/consumer/consumer%20client/version/latest", nil).
ProviderState("there is a resource with id 23", ensureResourceExists, nil)
if err := v.Verify(); err != nil {
t.Error(err)
}
}
func ensureResourceExists() error {
//implemenation to add the resource, so the api could return the expected data
return nil
}
Run your test to verify all the interactions.
go test -v ./...