Phongthorn
02/24/2022, 5:54 PMServer error: `GET <http://localhost:38189/users/1>` resulted in a `500 Internal Server Error` response:
{"message":"Multiple interaction found for GET /users/1","matching_interactions":[{"description":"A get request to /user (truncated...)
at vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:113
109▕ if ($summary !== null) {
110▕ $message .= ":\n{$summary}\n";
111▕ }
112▕
➜ 113▕ return new $className($message, $request, $response, $previous, $handlerContext);
114▕ }
115▕
116▕ /**
117▕ * Obfuscates URI if there is a username and a password present
+10 vendor frames
11 tests/Consumer/Service/ConsumerServiceHelloTest.php:64
GuzzleHttp\Client::request()
This is my test code
<?php
namespace Tests\Consumer\Service;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use PhpPact\Consumer\InteractionBuilder;
use PhpPact\Consumer\Matcher\Matcher;
use PhpPact\Consumer\Model\ConsumerRequest;
use PhpPact\Consumer\Model\ProviderResponse;
use PhpPact\Standalone\MockService\MockServerEnvConfig;
use PHPUnit\Framework\TestCase;
class ConsumerServiceHelloTest extends TestCase
{
/**
* Example PACT test.
*
* @throws Exception
* @throws GuzzleException
*/
public function testGetUserWithGivenId()
{
$matcher = new Matcher();
// Create your expected request from the consumer.
$request = new ConsumerRequest();
$request
->setMethod('GET')
->setPath('/users/1')
->addHeader('Content-Type', 'application/json');
// Create your expected response from the provider.
$response = new ProviderResponse();
$response
->setStatus(200)
->addHeader('Content-Type', 'application/json')
->setBody([
'id' => 1
]);
// Create a configuration that reflects the server that was started. You can create a custom MockServerConfigInterface if needed.
$config = new MockServerEnvConfig();
$builder = new InteractionBuilder($config);
$builder
->uponReceiving('A get request to /users/{id}')
->with($request)
->willRespondWith($response); // This has to be last. This is what makes an API request to the Mock Server to set the interaction.
$client = new Client([
'base_uri' => $config->getBaseUri(),
]);
$result = $client->request('GET', '/users/1', [
'headers' => [
'Content-Type' => 'application/json',
],
]);
$builder->verify(); // This will verify that the interactions took place.
$this->assertEquals('', $result); // Make your assertions.
}
}
Debug
[2022-02-25T00:53:14.684488 #35930] DEBUG -- : {
"description": "A get request to /users/{id}",
"request": {
"method": "GET",
"path": "/users/1",
"headers": {
"Content-Type": "application/json"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"id": 1
}
},
"metadata": null
}
Please suggest to me 🙂Matt (pactflow.io / pact-js / pact-go)
uponReceiving
) and request details without a state to differentiate the requests. I can’t see it in your test, so this could be that there is a different test somewhere else with the same details.
Alternatively, the pact write mode could be merge
and the pact already has that request in it. In that case, you should clear out the pact file before running your testsPhongthorn
02/25/2022, 2:43 AMPhongthorn
02/25/2022, 2:49 AMMatt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
Phongthorn
02/25/2022, 3:00 AMPhongthorn
02/25/2022, 3:04 AM$builder->finalize()
to clear interactions when test end
see updated code:
$request = new ConsumerRequest();
$request->setMethod("GET")
->setPath("/users/1")
->addHeader("Accept", "application/json");
$response = new ProviderResponse();
$response->setStatus(200)
->addHeader("Content-Type", "application/json")
->setBody([
"id" => 1,
"name" => "John Doe",
"email" => "<mailto:john@example.com|john@example.com>"
]);
$config = new MockServerEnvConfig();
$config->setLog(true)
->setLogLevel('debug');
$builder = new InteractionBuilder($config);
$builder->given("User with id 1 exists")
->uponReceiving("GET user for id: 1")
->with($request)
->willRespondWith($response);
$client = new Client(["base_uri" => $config->getBaseUri()]);
$userApi = new UserApi($client);
$result = $userApi->getUserById(1);
$this->assertTrue($builder->verify());
$this->assertEquals(1, $result->id);
$this->assertEquals('John Doe', $result->name);
$this->assertEquals('<mailto:john@example.com|john@example.com>', $result->email);
$builder->finalize();
Now it can generate pact file, but need to configure running pact-mock-server with phpunit.xmlPhongthorn
02/25/2022, 3:07 AM<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
<listeners>
<listener class="PhpPact\Consumer\Listener\PactTestListener">
<arguments>
<array>
<element key="0">
<string>PhpPact Example Tests</string>
</element>
</array>
</arguments>
</listener>
</listeners>
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
<testsuite name="PhpPact Example Tests">
<directory>./tests/Consumer</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./app</directory>
</include>
</coverage>
<php>
<env name="APP_ENV" value="testing"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="MAIL_MAILER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="TELESCOPE_ENABLED" value="false"/>
<env name="PACT_MOCK_SERVER_HOST" value="localhost"/>
<env name="PACT_MOCK_SERVER_PORT" value="7200"/>
<env name="PACT_CONSUMER_NAME" value="Frontend"/>
<env name="PACT_CONSUMER_VERSION" value="1.0.0"/>
<env name="PACT_CONSUMER_TAG" value="master"/>
<env name="PACT_PROVIDER_NAME" value="UserApi"/>
<env name="PACT_OUTPUT_DIR" value=".\example\output"/>
<env name="PACT_MOCK_SERVER_HEALTH_CHECK_TIMEOUT" value="10"/>
<!-- <env name="PACT_BROKER_URI" value="<http://localhost>"/> -->
</php>
</phpunit>
Matt (pactflow.io / pact-js / pact-go)
Phongthorn
02/25/2022, 3:09 AMPhongthorn
02/25/2022, 3:11 AMPhongthorn
02/25/2022, 3:11 AMMatt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
$server->start();
before your tests and $server->stop();
after them, or b) use the listeners (I think you’re using the listeners)Phongthorn
02/25/2022, 3:14 AMPhongthorn
02/25/2022, 3:15 AMPhongthorn
02/25/2022, 3:17 AMMatt (pactflow.io / pact-js / pact-go)
Phongthorn
02/25/2022, 3:21 AMPhongthorn
02/25/2022, 3:25 AMMatt (pactflow.io / pact-js / pact-go)
/users/A
to return a 200
it might have a state “user is unauthorized” to return a 403
it might expect the state to be “backend is unavailable” for a request to return a 500
A state could require you to mock api calls, seed a database, stub internal components etc.Matt (pactflow.io / pact-js / pact-go)
200
or a 404
if it doesn’t exist)Phongthorn
02/25/2022, 4:31 AMMatt (pactflow.io / pact-js / pact-go)
Phongthorn
02/25/2022, 4:43 AMPhongthorn
02/25/2022, 4:44 AMGiven("User sally exists").
UponReceiving("A request to login with user 'sally'").
This is part of state, right?Matt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
Given
and UponReceiving
is the scenario you’re testingMatt (pactflow.io / pact-js / pact-go)
UponReceiving
but with different Given
to test different sub-scenariosMatt (pactflow.io / pact-js / pact-go)
Phongthorn
02/25/2022, 4:46 AMPhongthorn
02/25/2022, 4:47 AMPhongthorn
02/25/2022, 4:47 AMMatt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
Slackbot
02/25/2022, 4:48 AMPhongthorn
02/25/2022, 4:59 AM