0

I'm trying to introduce contract testing to my team, and Pact seems to be the only reliable tool out there.

The complication is that the documentation suggests to use it with microservices and as a part of CI pipeline. But we can't make such a big change to our infrastructure, and instead I try to modify Pact usage to fit our needs.

I want to use Pact to test communications between JavaScript (Karma + Mocha) frontend and Rails (Rspec) backend via REST API. Everything is working fine, except I can't make it work reliably on CI (Circle CI). We don't have a CI pipeline, as it is suggested in Pact docs, instead we want to simply run Consumer and Provider tests in one container one after another. And the artifact will be just stored in the temporary folder in the container and removed afterwards.

The question: is there any established and reliable way of running Pact tests on CI in one container without a pipeline? Or Pact will not be the best tool for this job?

I wrote a ruby script to accomplish that, but it often crashes with a timeout:

def test_pact
  # wait for pact server to start, identify it by pid in the STDOUT
  server_init_message = "WEBrick::HTTPServer#start: pid="
  server_error_message = "(Errno::EADDRINUSE)"
  read_stream, write_stream = IO.pipe
  proc = ChildProcess.build("bundle", "exec", "rake", "pact:server")
  proc.io.stdout = proc.io.stderr = write_stream
  proc.leader = true # makes sure to kill all child forks of this process when it dies
  begin
    proc.start
    output_line = read_stream.gets
    until output_line.include?(server_init_message)
      puts "Waiting for Pact server to boot: #{Time.now}"
      output_line = read_stream.gets
      puts "Server output: #{output_line}"
      raise "Server boot error" if output_line.include?(server_error_message)
    end
    puts "Pact Server is booted"
    # Run JS tests:
    Dir.chdir("js") do
      run_or_exit("./build-support/circle.js", 'contract-karma.conf.js')
    end
    proc.stop
    # Run Ruby tests:
    run_or_exit("bundle exec rake pact:verify")
  rescue ChildProcess::TimeoutError, RuntimeError
    proc.stop
  end
end

And a Rake task:

require 'pact/tasks'
task :default => 'pact:verify'

# rake pact:server
namespace :pact do
  desc 'Run Pact Mock Server'
  task server: :environment do
    puts 'starting Pact mock server'
    system 'pact-mock-service --host localhost --port 1234 --pact_dir spec/contracts --consumer "Some Service" --provider "Some Service Provider" --pact_specification_version 2'
  end
end
Vladimir Mikhaylovskiy
  • 1,955
  • 4
  • 19
  • 28

2 Answers2

2

Pact is completely compatible with CircleCI and other hosted CI / build services. Having a pact exist for a single build, however, does not really make sense.

Based on your current use case, I would suggest the following:

  1. Split the Consumer and Provider tests into separate Circle CI builds.
  2. Setup an independent Pact Broker to store the pacts (e.g. Host your own Pact Broker or get a free one at http://pact.dius.com.au/).
  3. Publish pacts from your consumer build (JS project) to your Pact Broker.
  4. In your Provider build, ensure the pacts are satisfied else fail the build. You can use the pact_broker-client gem for this purpose.

Running your Provider build directly after your Consumer somewhat reduces the benefits of Pact, in that each service should be independently deployable.

Matthew Fellows
  • 3,669
  • 1
  • 15
  • 18
1

Have a look at karma pact while you're at it.

Beth Skurrie
  • 1,333
  • 7
  • 8