Transcripted Summary

Depending on the authentication type of your provider, you may come across some issues when playing requests against the provider. Testing authentication and authorization using Pact can be a bit tricky so here are some suggestions.


Test Authentication Outside of Pact

You can test authentications outside of Pact. For example, a UI entry test is a good candidate because it is a unique, complex test type that needs to be handled separately.


Stub Authentication Service and Use Fake Hard-coded Credentials

You can also stub an authentication service and use fake hard-coded credentials. Be aware that this may reduce confidence in a consumer and provider functioning correctly in real life. Be sure to adequately cover authentication by employing multiple layers of tests.



Configure Users with Matching Credentials

Another suggestion is to configure users with matching credentials in the form of static authentication data to be used when running contract tests:


const credentials = { username: "test", password: "123"}

Use requestFilters to Send Different Headers

Use requestFilters to send different type of headers for each request. As seen in the screenshot below, request filters are defined in the provider options.



These functions have access to request and response objects from the contract file and can make modifications before passing them to be verified. You can see that tokens have a different value for each request.


const { Verifier } = require("@pact-foundation/pact")
const chai = require("chai")
const chaiAsPromised = require("chai-as-promised")
chai.use(chaiAsPromised)
const { server, importData, animalRepository } = require("../provider.js")
const path = require("path")

server.listen(8081, () => {
  importData()
  console.log("Animal Profile Service listening on http://localhost:8081")
})

// Verify that the provider meets all consumer expectations
describe("Pact Verification", () => {

  it("validates the expectations of Matching Service", () => {
	let token = "INVALID TOKEN"

	let opts = {
        provider: "Animal Profile Service",
        logLevel: "DEBUG",
        providerBaseUrl: "http://localhost:8081",

        requestFilter: (req, res, next) => {
            console.log(
            "Middleware invoked before provider API - injecting Authorization token"
            )

    	req.headers["MY_SPECIAL_HEADER"] = "my special value"

    	// e.g. ADD Bearer token
    	req.headers["authorization"] = `Bearer ${token}`
    	next()
  	},

        stateHandlers: {
            "Has no animals": () => {
                animalRepository.clear()
                token = "1234"
                return Promise.resolve(`Animals removed to the db`)
            },

            "Has some animals": () => {
                token = "6589"
                importData()
                return Promise.resolve(`Animals added to the db`)
            },

            "Has an animal with ID 1": () => {
                token = "7254"
                importData()
                return Promise.resolve(`Animals added to the db`)
            },

            "is not authenticated": () => {
                token = ""
                    Promise.resolve(`Invalid bearer token generated`)
                },
  	    },

        // Fetch pacts from broker
        pactBrokerUrl: "https://test.pact.dius.com.au/",

        // Fetch from broker with given tags
        consumerVersionTag: ["prod"],

        // Tag provider with given tags
        providerVersionTag: ["prod"],

        // Find _all_ pacts (not just latest) with tag prod
        //   consumerVersionSelectors: [{
        // 	tag: "prod",
        // 	all: true
        //   }
        // ],

        // Enables "pending pacts" feature
        enablePending: true,

        // Specific Remote pacts (doesn't need to be a broker)
        // pactUrls: ['https://test.pact.dius.com.au/pacts/provider/Animal%20Profile%20Service/consumer/Matching%20Service/latest'],
        // Local pacts
        // pactUrls: [
        //   path.resolve(
        // 	process.cwd(),
        // 	"./pacts/matching_service-animal_profile_service.json"
        //   ),
        // ],

        // If you're NOT using Pactflow, use the username/password option as per below
        pactBrokerUsername: "dXfltyFMgNOFZAxr8io9wJ37iUpY42M",
        pactBrokerPassword: "O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1",

        // if you're using Pactflow, you must authenticate using the bearer token option
        // You can obtain the token from https://<your broker>.pact.dius.com.au/settings/api-tokens
        // pactBrokerToken: "<insert your token here"
        publishVerificationResult: true,
        providerVersion: "1.0.0",
	}

	return new Verifier(opts).verifyProvider().then(output => {
  	console.log("Pact Verification Complete!")
  	console.log(output)
	})
  })
})

Use Long-Lived Tokens or Generate Your Own Before Tests

The final suggestion is to use long-lived tokens to avoid invalidating tests that have tokens with past expiration dates in case you forget to generate new ones. Alternately, generate your own prior to testing.



Resources



© 2021 Applitools. All rights reserved. Terms and Conditions Privacy Policy GDPR