reecetech/pactman

EachLike failing test cases

Closed this issue · 7 comments

Hey,

I've been stuck trying to figure out why my EachLike matcher is failing my test case. I'm quite positive I have the syntax correct. I haven't been able to find much help online since this is relatively new.

I'd be happy to share more of the code with whoever can help, but here is what I am seeing
Screen Shot 2020-04-26 at 4 18 25 PM

I'm new to pactman/matchers so just trying to get some help here, ty.

Hi @matthewhassonsonos it looks like there's something going on in your test there - can you supply the test source code please?

Thanks for the reply. I'm not sure why some of the code is not formatted in a code block below.
I also want to point out: if I set the body to an empty dictionary, the test passes fine. The issue is with something in the body.

import json
import os
import glob
import requests
from pactman import Consumer, Includes, Like, EachLike, Provider, Term
from ..search_uri import SearchClient
from ..config_parser import ConfigParser

log = logging.getLogger(__name__)

@pytest.fixture(scope="module")
def pact(request):
	# 
	# creates pact object and start/stop the service
	# :param
	# 	request (object): request object to introspect command line args
	# 
	pact = Consumer('Discovery').has_pact_with(
		Provider('Universal-Search'),
		host_name='localhost',
		port=3000,
		pact_dir=ConfigParser.get_value('pact_dir'))
	log.info('starting pact service ')
	pact.start_service()
	atexit.register(pact.stop_service)
	yield pact

	# push_to_broker("0.0.4")
	version = request.config.getoption('--publish-pact')
	log.info("version: {}".format(version))
	if version:
		push_to_broker(version)


@pytest.fixture
def client():
	# 
	# returns SearchClient instance
	# 
	log.info('creating client')
	url = 'http://{host}:{port}'.format(host=ConfigParser.get_value('host'), port=ConfigParser.get_value('port'))
	return SearchClient(url)


def push_to_broker(version):
	# 
	# publishes the pact file to broker
	# 
	# :params:
	# 	version (string): version of consumer pact file
	# 
	log.info('publishing pact file to broker')
	pact_files = glob.glob(ConfigParser.get_value('pact_dir')+'/*.json')
	for pact_file in pact_files:
		log.info("pact file: {}".format(pact_file))
		with open(pact_file) as pf:
			json_data = json.load(pf)
		broker_url = "{}/{}".format(ConfigParser.get_value('broker_url'), version)
		log.info("broker url: {}".format(broker_url))
		r = requests.put(
			broker_url,
			json=json_data
			)
		log.info("publishing to broker status code: {}".format(r.status_code))
		if not r.ok:
			log.error('Pact file: {} could not be published to broker.')
			r.raise_for_status()


def test_search_beatles_in_deezer(pact, client):
	# 
	# searches an artist in given music service and creates contract to have expected search result from body dictionary
	# :params
	# 	pact (pactman.Consumer)     : pact instance with Consumer as Discovery and provider name as Universal-Search
	# 	client (SearchClient) :  instance to return search result
	# 

	body = {
	    "metadata": {
	        "count": 123
	    },
	    "order": ['artists'],
	    "artists": {
	        "metadata": {
	            "count": 3
	        },
	        "results": EachLike({
				"id": {
					"objectId": 'string',
					"serviceId": 1,
					"serviceName": "deezer",
					"accountId": 123
				},
				"name": "string",
				"type": "artist",
				"images": EachLike({"url": "string"})
								}, minimum=3)
			}
		}


	artists = str(ConfigParser.get_value('ResourceType')["type"][0])
	search_path = "{}/households/{}/search".format(ConfigParser.get_value('api_version_path'),
												   ConfigParser.get_value('household_id'))
	msp_deezer_code = str(ConfigParser.get_value('MusicServicesCode')['deezer'])
	(pact
		.given('Search Result for a popular artist')
		.upon_receiving('a Deezer query for the Beatles with a count of 4')
		.with_request(method='get',
					  path=search_path,
					  headers={'x-sonos-user-id': ConfigParser.get_value('user_id'), 'content-type':'application/json'},
					  query={'q': 'beatles',
					  		 'services': msp_deezer_code,
					  		  'count': '3',
							  'resources': 'artists'})
		.will_respond_with(200, body=body))
	with pact:
		result = client.search_uri('beatles', musicservices=msp_deezer_code, count='3', resource=artists)
	assert result == body
`

Right, so that final assert is incorrect in pact land. The result will be generated data from the mock server using the sample data you've provided in body, whereas body contains the pact variable content matcher objects (your EachLikes).

The intent with pact here is twofold:

  1. to ensure that search_uri generates a request that matches the pact's with_request specification, and
  2. to ensure that the generated data from the mock server works with your client code - that search_uri works correctly with that data. Making assertions about the specifics of the contents of that response data is not really any value - you've defined what it is going to be in the pact!

FWIW typically you'll get better value out of pact if you test functionality that consumes client.search_uri and use pact to mock the values that it generates, rather than just invoking client.search_uri to exercise the pact directly.

I hope this helps. Please close the issue if it does :)

Thanks for the reply. My follow up question is: what good is 'result' if I am not asserting anything? What alternative way would I implement it, since its functionality is vital to the test? Basically, can you elaborate on a 'pact land' way for me to implement using client.search_uri to mock the values it generates?

I guess you're saying I should not be asserting anything?

Nevermind, figured it out. I'll go ahead and close this. Thanks!

Great to hear. Good luck!