Test cases for the conformance runner are configured in YAML files and are located here. Each file represents a single suite of tests.
Authoring new test cases involves either a new file, if a new suite is warranted, or an additional test case in an existing file/suite.
For the Protobuf definitions for the conformance runner, see the connectrpc/conformance module in the BSR.
A suite represents a series of tests that all test the same general functionality (cancellation, timeouts, etc.). Each
test suite YAML file represents a TestSuite message and the schema of this message defines the schema
of the YAML file. The representation of a Protobuf message in YAML is the same as its JSON format.
When defining a test suite in a YAML file, the only values that are required are the suite name (which should be unique across all suites) and at least one test case.
In addition, each suite can be configured with various directives which will apply to all tests within that suite. These directives can be used to constrain the tests run by the conformance runner or to signal various configurations to the runner that may be needed to execute the tests. The runner will use these directives to expand the tests in the suite into multiple permutations. This means that a single test defined in a suite will be run several times across various permutations.
The below directives are used to constrain tests within a suite:
-
modecan be used to specify a suite as applying only to a specific mode (i.e. client or server). For example, if you are writing a suite to target only clients under test, you would specifymode: TEST_MODE_CLIENTand the tests in the suite will be run only when themodespecified to the runner on the command line isclient. If not specified in a suite, tests are run regardless of the command linemode. -
relevantProtocolsis used to limit tests only to a specific protocol, such as Connect, gRPC, or gRPC-web. If not specified, tests are run for all protocols. -
relevantHttpVersionsis used to limit tests to certain HTTP versions, such as HTTP 1.1, HTTP/2, or HTTP/3. If not specified, tests are run for all HTTP versions. -
relevantCodecsis used to limit tests to certain codec formats, such as JSON or binary. If not specified, tests are run for all codecs. -
relevantCompressionsis used to limit tests to certain compression algorithms, such as gzip, brotli, or snappy. If not specified, tests are run for all compressions. -
connectVersionModeallows you to either require or ignore validation of the Connect version header or query param. This should be left unspecified if the suite is agnostic to this validation behavior.
The below reliesOn directives are used to signal to the test runner how the reference client or server should be
configured when running tests:
-
reliesOnTlsspecifies that a suite relies on TLS. Iftrue, the test cases will not be run against non-TLS server configurations. Defaults tofalse. -
reliesOnTlsClientCertsspecifies that the suite relies on the client using TLS certificates to authenticate with the server. Note that if this is set totrue,reliesOnTlsmust also betrue. Defaults tofalse. -
reliesOnConnectGetspecifies that the suite relies on the Connect GET protocol. Defaults tofalse. -
reliesOnMessageReceiveLimitspecifies that the suite relies on support for limiting the size of received messages. Whentrue, themodeproperty must be set to indicate whether the client or server should support the limit. Defaults tofalse.
Test cases are specified in the testCases property of the suite. Each test case starts with the request property
which defines the request which will be sent to a client during the test run. Each request must specify the following
fields:
testName- For naming conventions, see below.streamType- One ofSTREAM_TYPE_UNARY,STREAM_TYPE_CLIENT_STREAM,STREAM_TYPE_SERVER_STREAM,STREAM_TYPE_HALF_DUPLEX_BIDI_STREAM, orSTREAM_TYPE_FULL_DUPLEX_BIDI_STREAM.
Once the above are specified, you can then define your request. For a full list of fields to specify in the request,
see the ClientCompatRequest message in the Conformance Protobuf definitions.
The fields service and method are optional as a pair when writing test cases, meaning that they can both be omitted
or must be specified together. If they are omitted, the runner will auto-populate them as follows:
-
service-connectrpc.conformance.v1.ConformanceService. -
method- Based onstreamTypeaccording to the following table.Stream Type Method STREAM_TYPE_UNARYUnarySTREAM_TYPE_CLIENT_STREAMClientStreamSTREAM_TYPE_SERVER_STREAMServerStreamSTREAM_TYPE_HALF_DUPLEX_BIDI_STREAMBidiStreamSTREAM_TYPE_FULL_DUPLEX_BIDI_STREAMBidiStream
Important
The ClientCompatRequest message contains some fields that should not be specified in test cases because they are
automatically populated by the test runner. These fields are:
http_versionprotocolcodeccompressionhostportserver_tls_certclient_tls_credsmessage_receive_limit
If a test is specific to one of the first four fields, it should instead be indicated in the directives for the test suite itself.
The expected response for a test, in the expectedResponse field, can be auto-generated based on the request details.
The conformance runner will determine what the response should be according to the values specified in the individual
test case requests.
You also have the ability to explicitly specify your own expected response directly in the test definition. However, this is typically only needed for exception test cases. If the expected response is mostly re-stating the response definition that appears in the requests, you should rely on the auto-generation if possible. Otherwise, specifying an expected response can make the test YAML overly verbose and harder to read, write, and maintain.
If the test induces behavior that prevents the server from sending or client from receiving the full response definition, it will be necessary to define the expected response explicitly. Timeouts, cancellations, and exceeding message size limits are good examples of this.
If you do need to specify an explicit response, simply define an expectedResponse block for your test case and
this will override the auto-generated expected response in the test runner.
To see tests denoting an explicit response, search the test suites directory for the word expectedResponse.
There are some cases where a condition is obviously an error, based on the protocol specification, but that specification does not actually indicate how a client or server should behave in the face of that error. One example is if a gRPC server receives an HTTP/2 request that has an unexpected content-type or an HTTP method other than POST.
For these cases, the test author should put the most sensible error code as the expected error code in the
expectedResponse field of the test case, but then can also add other acceptable error codes in the
otherAllowedErrorCodes field. Even when the error code is not specified, it is not good practice to allow
any error code since many error codes will obviously not apply, and some error codes should never be used
by a client or server implementation library but are reserved for use by actual application logic (see the
table at the bottom of the gRPC status codes documentation).
There are two message types in the test case schema worth noting here - RawHTTPRequest and
RawHTTPResponse. Both allow for the ability to define a round-trip outside the scope of the
Connect framework. They are used for sending or receiving anomalous payloads during a test.
The RawHTTPRequest message can be set on the request property in a test case. Its purpose is to model a raw HTTP
request. This can be used to craft custom requests with odd properties (including certain kinds of malformed requests)
to test edge cases in servers. This value is only handled by the reference client and should only appear in files where
mode is set to TEST_MODE_SERVER.
The RawHTTPResponse message is the analog to RawHTTPRequest. It can be set in the response definition for a unary
or streaming RPC type and its purpose is to model a raw HTTP response. This can be used to craft custom responses with
odd properties (including returning aberrant HTTP codes or certain kinds of malformed responses) to test edge cases in
clients. This value is only handled by the reference server and should only appear in files where mode is set to
TEST_MODE_CLIENT.
Test suites and their tests within follow a loose naming convention.
Test files should be named according to the suite inside and the general functionality being tested. Additionally:
- If a suite applies only to a certain protocol, the file name should be prefixed with that protocol. If the suite applies to all protocols, this can be omitted.
- If a suite only contains client or server tests, the file name prefix should include
clientorserver. If the suite is for both client and server, this can be omitted.
The file names should use lower_snake_case convention.
For example:
connect_with_get.yamlcontains tests that test GET requests for the Connect protocol only.grpc_web_client.yamlcontains client tests for the gRPC-web protocol.client_message_size.yamlfile contains a suite of client tests only across all protocols.
The suite inside the file should either match the file name or be close to it. Reasons it might vary from the file name is to provide a little more detail as to the test suite's purpose.
The test suite names should use Title Case convention
Test names should be hyphen-delimited. If a suite contains tests of multiple stream types, the test name should be
prefixed with the stream type and a backslash (/).
Note
In the case of Bidi tests with separate cases for half-duplex operation vs. full-duplex
operation, you should also add full-duplex or half-duplex to the test name.
For example:
unary/errorserver-stream/error-with-responsesbidi-stream/full-duplex/stream-error-returns-success-http-code
If a suite contains tests for just a single stream type, the stream type can be omitted from the test name.
These conventions allow for more granular control over running tests via the conformance runner, such as only running tests for a specific protocol or only running the unary tests within a suite.
Aside from the / for separating elements on the name, test case names should use kebab-case convention.
To test new test cases, you can use make runconformance, to run the reference implementations against the new test
cases. But, while iterating on the test case definition, it is often valuable to just run the test cases in the new
file. This can be done using the --test-file option to the test runner:
# Build the test runner and the reference client and server.
make .tmp/bin/connectconformance .tmp/bin/referenceclient .tmp/bin/referenceserver
# Run just the tests in the new file
.tmp/bin/connectconformance \
--conf ./testing/reference-impls-config.yaml \
--mode client \
--test-file ./testsuites/new-test-suite.yaml \
-v --trace \
-- \
.tmp/bin/referenceclient
.tmp/bin/connectconformance \
--conf ./testing/reference-impls-config.yaml \
--mode server \
--test-file ./testsuites/new-test-suite.yaml \
-v --trace \
-- \
.tmp/bin/referenceserverIf the new test suite is specific to client or server mode then you'd only need to run one of the above instead of both.
You can debug the reference client and server by running the opposite mode above. For example, to debug the
reference server, run the first command above, which runs tests in client mode. In this mode, the reference
server is run in-process, so you can start the above connectconformance command with a debugger attached
and then step through the server code.
Taking all of the above into account, here is an example test suite that verifies a server returns the specified headers
and trailers. The below test will only run when mode is server and will be limited to the Connect protocol using the
JSON format and identity compression. Also, it will require the server verify the Connect version header.
name: Example Test Suite
# Constrain to only servers under test.
mode: TEST_MODE_SERVER
# Constrain these tests to only run over the Connect protocol.
relevantProtocols:
- PROTOCOL_CONNECT
# Constrain these tests to only use 'identity' compression.
relevantCompressions:
- COMPRESSION_IDENTITY
# Constrain these tests to only use the JSON codec.
relevantCodecs:
- CODEC_JSON
# Require Connect version header verification.
connectVersionMode: CONNECT_VERSION_MODE_REQUIRE
testCases:
- request:
testName: returns-headers-and-trailers
service: connectrpc.conformance.v1.ConformanceService
method: Unary
streamType: STREAM_TYPE_UNARY
requestMessages:
- "@type": type.googleapis.com/connectrpc.conformance.v1.UnaryRequest
responseDefinition:
responseHeaders:
- name: x-custom-header
value: ["foo"]
responseData: "dGVzdCByZXNwb25zZQ=="
responseTrailers:
- name: x-custom-trailer
value: ["bing"]