Skip to content

Usage Guide

Bas Dijkstra edited this page Jun 11, 2023 · 119 revisions

Welcome to the RestAssured.Net usage guide. RestAssured.Net is an idiomatic port of REST Assured for Java and brings the power of REST Assured to the C# .NET ecosystem.

Below you can find a long list of examples on how to use RestAssured.Net to write readable tests for your HTTP APIs. An even richer set of usage examples can be found in the RestAssured.Net.Tests project. The examples in that project double as acceptance tests for the library.

Getting started

  • Create a new test project using your testing framework of choice. RestAssured.Net targets .NET 7, .NET 6 and .NET Core 3.1.
  • Add the latest RestAssured.Net NuGet package to your project
  • Add the following using directive to your test class: using static RestAssured.Dsl; (or using static RestAssuredNet.RestAssuredNet; when using version 1.x.x)
  • Write and run your tests!

Response verification

HTTP status codes

Checking the HTTP status code of your responses can be done in several ways.

By specifying the expected status code as an integer

[Test]
public void StatusCodeIndicatingSuccessCanBeVerifiedAsInteger()
{
    Given()
    .When()
    .Get("http://localhost:9876/http-status-code-ok")
    .Then()
    .StatusCode(200);
}

By specifying the expected status code as an HttpStatusCode value

    .StatusCode(HttpStatusCode.OK);

Using an NHamcrest matcher

    .StatusCode(NHamcrest.Is.EqualTo(200));

Header values

By specifying the expected header value as a string

[Test]
public void MultipleResponseHeadersCanBeVerified()
{
    Given()
    .When()
    .Get("http://localhost:9876/custom-multiple-response-headers")
    .Then()
    .StatusCode(200)
    .And() // Example of using the And() syntactic sugar method in response verification.
    .Header("custom_header_name", "custom_header_value")
    .And()
    .Header("another_header", "another_value");
}

Using an NHamcrest matcher

    .Header("custom_header_name", NHamcrest.Contains.String("tom_header_val"));

The Content-Type header

By specifying the expected value as a string

[Test]
public void ResponseContentTypeHeaderCanBeVerified()
{
    Given()
    .When()
    .Get("http://localhost:9876/custom-response-content-type-header")
    .Then()
    .StatusCode(200)
    .ContentType("application/something");
}

Using an NHamcrest matcher

    .ContentType(NHamcrest.Contains.String("something"));

Response body (elements)

Verifying the entire response body

You can check the entire response body as a plaintext string. JSON strings work as well.

[Test]
public void JsonStringResponseBodyCanBeVerified()
{
    Given()
    .When()
    .Get("http://localhost:9876/plaintext-response-body")
    .Then()
    .StatusCode(200)
    .Body("{\"id\": 1, \"user\": \"John Doe\"}");
}

Using an NHamcrest matcher

    .Body(NHamcrest.Contains.String("John Doe"));

Verifying individual element values

You can also select individual elements, or collections of elements, using either a JsonPath expression (for JSON responses) or an XPath expression (for XML and HTML responses).

These elements or element collections can then be verified using NHamcrest matchers. Here's a JSON example:

[Test]
public void JsonResponseBodyElementStringValueCanBeVerified()
{
    Given()
    .When()
    .Get("http://localhost:9876/json-response-body")
    .Then()
    .StatusCode(200)
    .Body("$.Places[0].Name", NHamcrest.Contains.String("City"));
}

or

    .Body("$.Places[0:].Name", NHamcrest.Has.Item(NHamcrest.Is.EqualTo("Sun City")));

and here's an example for XML:

[Test]
public void XmlResponseBodyElementStringValueCanBeVerifiedUsingNHamcrestMatcher()
{ 
    Given()
    .When()
    .Get("http://localhost:9876/xml-response-body")
    .Then()
    .StatusCode(200)
    .Body("//Place[1]/Name", NHamcrest.Is.EqualTo("Sun City"));
}

From version 2.6.0 onwards, you can also use XPath to verify HTML response body elements. See these tests for examples.

RestAssured.NET uses Json.NET or System.Xml to evaluate the expressions used to select elements from the response body.

By default, selection of the evaluator is made based on the value of the response Content-Type header. You can override this and force a specific evaluator to be used by passing in an additional argument to the Body() method (useful when the API you're testing sends incorrect Content-Type header values, for example):

.Body("$.places[0].name", NHamcrest.Is.EqualTo("Beverly Hills"), VerifyAs.Json)

JSON schema validation

A JSON response body can be validated against a JSON schema like this:

[Test]
public void JsonSchemaCanBeSuppliedAndVerifiedAsString()
{
    Given()
    .When()
    .Get("http://localhost:9876/json-schema-validation")
    .Then()
    .StatusCode(200)
    .And()
    .MatchesJsonSchema(jsonSchema);
}

The jsonSchema object can be supplied either as a string or as a Newtonsoft.Json.Schema.JSchema object. If supplied as a string, MatchesJsonSchema() will throw a ResponseVerificationException if the schema cannot be parsed by Json.NET.

XML schema and DTD validation

An XML response body can be validated against an XSD like this:

[Test]
public void XmlSchemaCanBeSuppliedAndVerifiedAsString()
{
    Given()
    .When()
    .Get("http://localhost:9876/xml-schema-validation")
    .Then()
    .StatusCode(200)
    .And()
    .MatchesXsd(xmlSchema);
}

The xmlSchema object can be supplied either as a string or as a System.Xml.Schema.XmlSchemaSet object. If supplied as a string, MatchesXsd() will throw a ResponseVerificationException if the schema cannot be parsed by System.Xml.

Additionally, an XML response can be validated against inline DTDs, too:

[Test]
public void XmlResponseCanBeVerifiedAgainstInlineDtd()
{
    Given()
    .When()
    .Get("http://localhost:9876/matching-dtd-validation")
    .Then()
    .StatusCode(200)
    .And()
    .MatchesInlineDtd();
}

Deserializing a response payload into a C# object

RestAssured.Net supports deserialization of both JSON and XML response payloads into C# objects.

For JSON, RestAssured.Net uses Json.NET as its deserializer. For XML, RestAssured.Net uses System.Xml.Serializer as its deserializer.

[Test]
public void ObjectCanBeDeserializedFromJson()
{
    Location responseLocation = (Location)Given()
    .When()
    .Get("http://localhost:9876/json-deserialization")
    .As(typeof(Location));

    Assert.That(responseLocation.Country, Is.EqualTo("United States"));
    Assert.That(responseLocation.Places?.Count, Is.EqualTo(2));
}

By default, selection of the deserializer is made based on the value of the response Content-Type header. You can override this and force a specific deserializer to be used by passing in a second argument (useful when the API you're testing sends incorrect Content-Type header values, for example):

.As(typeof(Location), DeserializeAs.Json)

If no appropriate deserializer can be determined, a DeserializationException is thrown.

If you want to, you can also perform some assertions on the response before deserializing:

[Test]
public void ObjectCanBeDeserializedFromJsonAfterVerifications()
{
    Location responseLocation = (Location)Given()
        .When()
        .Get("http://localhost:9876/json-deserialization")
        .Then()
        .StatusCode(200)
        .And()
        .Extract()
        .As(typeof(Location));

    Assert.That(responseLocation.Country, Is.EqualTo("United States"));
    Assert.That(responseLocation.Places?.Count, Is.EqualTo(2));
}

As of version 2.6.0, the method alias DeserializeTo() is available as well. It is functionally equivalent to the As() method. Choose whichever you feel is easier on the eyes.

Extracting values from the response

Extracting JSON response body values

You can extract singular values from a JSON response body for later re-use using JsonPath:

[Test]
public void JsonResponseBodyElementStringValueCanBeExtracted()
{
    string placeName = (string)Given()
    .When()
    .Get("http://localhost:9876/json-response-body")
    .Then()
    .StatusCode(200)
    .Extract().Body("$.Places[0].Name");

    Assert.That(placeName, NUnit.Framework.Is.EqualTo("Sun City"));
}

This works for other primitive data types as well.

Note: for now, integer values need to be stored in a variable of type long (and then converted to int if you need it), as Json.NET deserializes values to Int64, not Int32. Suggestions (or even a PR) that address this are welcome.

Extracting multiple values works, too, but at the moment you have to store them in an object of type List<object>:

[Test]
public void JsonResponseBodyMultipleElementsCanBeExtracted()
{
    List<object> placeNames = (List<object>)Given()
    .When()
    .Get("http://localhost:9876/json-response-body")
    .Then()
    .StatusCode(200)
    .Extract().Body("$.Places[0:].IsCapital");

    Assert.That(placeNames.Count, NUnit.Framework.Is.EqualTo(2));
}

Extracting XML or HTML response body values

You can extract singular values from an XML response body for later re-use using XPath:

[Test]
public void XmlResponseBodyElementValueCanBeExtracted()
{
    string placeName = (string)Given()
    .When()
    .Get("http://localhost:9876/xml-response-body")
    .Then()
    .StatusCode(200)
    .Extract().Body("//Place[1]/Name");

    Assert.That(placeName, Is.EqualTo("Sun City"));
}

Note: for XML, all element values will be returned as a string (based on the InnerText property of System.Xml.XmlNode).

Extracting multiple values works, too, but at the moment you have to store them in an object of type List<string> for the same reasons:

[Test]
public void XmlResponseBodyMultipleElementsCanBeExtracted()
        {
    List<string> placeNames = (List<string>)Given()
    .When()
    .Get("http://localhost:9876/xml-response-body")
    .Then()
    .StatusCode(200)
    .Extract().Body("//Place/Name");

    Assert.That(placeNames.Count, Is.EqualTo(2));
}

From version 2.6.0 onwards, you can also extract HTML response body elements in the same way. See these tests for examples.

RestAssured.NET uses Json.NET or System.Xml to evaluate the expressions used to extract elements from the response body.

By default, selection of the evaluator is made based on the value of the response Content-Type header. You can override this and force a specific evaluator to be used by passing in an additional argument to the Body() method (useful when the API you're testing sends incorrect Content-Type header values, for example):

.Extract().Body("$.places[0].name", ExtractAs.Json)

Extracting response header values

[Test]
public void JsonResponseHeaderCanBeExtracted()
{
    string responseHeaderValue = Given()
    .When()
    .Get("http://localhost:9876/json-response-body")
    .Then()
    .StatusCode(200)
    .Extract().Header("custom_header");

    Assert.That(responseHeaderValue, NUnit.Framework.Is.EqualTo("custom_header_value"));
}

Extracting the entire response

You can also get the entire response as an object of type System.Net.Http.HttpResponseMessage:

[Test]
public void EntireResponseCanBeExtracted()
{
    HttpResponseMessage response = Given()
    .When()
    .Get("http://localhost:9876/json-response-body")
    .Then()
    .StatusCode(200)
    .Extract().Response();

    Assert.That(response.StatusCode, NUnit.Framework.Is.EqualTo(HttpStatusCode.OK));
    Assert.That(response.Headers.GetValues("custom_header").First(), NUnit.Framework.Is.EqualTo("custom_header_value"));
}

Request specification

Header values

Custom headers and their values

[Test]
public void HeaderWithASingleValueCanBeSupplied()
{
    Given()
    .Header("my_header", "my_header_value")
    .When()
    .Get("http://localhost:9876/single-header-value")
    .Then()
    .StatusCode(200);
}

Content-Type header and content encoding

    Given()
    .ContentType("application/xml")
    .ContentEncoding(Encoding.ASCII)

Accept header

    Given()
    .Accept("application/xml")

You can also set headers as part of a RequestSpecification.

Specifying query parameters

While you can add query parameters and their values to your tests by building the endpoint dynamically using string interpolation, string concatenation or even a StringBuilder, RestAssured.Net also provides two utility methods to make the process easier and your tests more readable:

[Test]
public void SingleQueryParameterCanBeSpecified()
{
    Given()
    .QueryParam("name", "john")
    .When()
    .Get("http://localhost:9876/single-query-param")
    .Then()
    .StatusCode(200);
}

The endpoint invoked here is http://localhost:9876/single-query-param?name=john.

To add multiple query parameters, you can call QueryParam() multiple times, or pass a Dictionary to QueryParams():

[Test]
public void MultipleQueryParametersCanBeSpecifiedUsingADictionary()
{
    Dictionary<string, object> queryParams = new Dictionary<string, object>
    {
        { "name", "john" },
        { "id", 12345 },
    };

    Given()
    .QueryParams(queryParams)
    .When()
    .Get("http://localhost:9876/multiple-query-params")
    .Then()
    .StatusCode(200);
}

Specifying path parameters

Similar to query parameters, you can add a path parameter either by string interpolation, string concatenation or a StringBuilder, but RestAssured.Net provides utility methods for improved readability here, too:

[Test]
public void SinglePathParameterCanBeSpecified()
{
    Given()
    .PathParam("userid", 1)
    .When()
    .Get("http://localhost:9876/user/{{userid}}")
    .Then()
    .StatusCode(200);
}

To define the location of a path parameter, use the 'double handlebar' notation with a placeholder name that matches the one in the PathParam() definition. RestAssured.Net uses Stubble to create the path, which is http://localhost:9876/user/1 in this example.

To define multiple path parameters, call PathParam() multiple times, or pass a Dictionary to PathParams():

[Test]
public void MultiplePathParameterCanBeSpecifiedUsingADictionary()
{
    Dictionary<string, object> pathParams = new Dictionary<string, object>
    {
        { "userid", 1 },
        { "accountid", "NL1234" },
    };

    Given()
    .PathParams(pathParams)
    .When()
    .Get("http://localhost:9876/user/{{userid}}/account/{{accountid}}")
    .Then()
    .StatusCode(200);
}

Authentication details

Basic authentication

[Test]
public void HeaderWithASingleValueCanBeSupplied()
{
    Given()
    .BasicAuth("username", "password")
    .When()
    .Get("http://localhost:9876/basic-auth")
    .Then()
    .StatusCode(200);
}

OAuth2 authentication tokens

    Given()
    .OAuth2("this_is_my_token")

You can also set Basic and OAuth2 authentication details as part of a RequestSpecification.

Cookies

You can add a cookie to a request like this:

[Test]
public void CookieWithASingleValueCanBeSupplied()
{
    Given()
    .Cookie("my_cookie", "my_cookie_value")
    .When()
    .Get("http://localhost:9876/single-cookie-value")
    .Then()
    .StatusCode(200);
}

The Cookie() method has overloads that take either an argument of type System.Net.Cookie or of System.Net.CookieCollection, so those can be added, too. See the tests for cookies for examples.

Timeouts

For some requests, you might want to change the default timeout, which is the System.Net.Http.HttpClient default of 100,000 milliseconds:

[Test]
public void CustomTimeoutCanBeSuppliedInTest()
{
    Given()
    .Timeout(TimeSpan.FromSeconds(200))
    .When()
    .Get("http://localhost:9876/timeout-ok")
    .Then()
    .StatusCode(200);
}

You can also set a custom timeout in a RequestSpecification.

User agent

If you want to set the user agent for a specific HTTP request, you can do that by specifying the product name and product value like this:

[Test]
public void CustomUserAgentCanBeSuppliedUsingProductNameAndProductVersionInTest()
{
    Given()
    .UserAgent("MyUserAgent", "1.0")
    .When()
    .Get("http://localhost:9876/user-agent")
    .Then()
    .StatusCode(200);
}

An overload is available to allow passing in an object of type System.Net.Http.Headers.ProductInfoHeaderValue.

You can also set the user agent as part of a RequestSpecification.

Proxy

In case your API calls need to go through a proxy, you can specify those in your requests like this:

[Test]
public void ProxyCanBeSpecified()
{
    IWebProxy proxy = new WebProxy("http://yourproxy.com:80", true);

    Given()
    .Proxy(proxy)
    .When()
    .Get("http://localhost:9876/proxy")
    .Then()
    .StatusCode(200);
}

You can also set the proxy as part of a RequestSpecification.

Disabling SSL checks

In case you want to hit an HTTPS endpoint without having to care about SSL, you can turn off SSL validation on the HttpClient used by RestAssured.Net using RelaxedHttpsValidation():

[Test]
public void HttpsConnectionsCanBeUsed()
{
    Given()
    .RelaxedHttpsValidation()
    .When()
    .Get("https://localhost:8443/ssl-endpoint")
    .Then()
    .StatusCode(200);
}

You can also disable SSL validation as part of a RequestSpecification, or globally through the static RestAssuredConfiguration.

Payload

Payload as a plaintext string

[Test]
public void PlaintextRequestBodyCanBeSupplied()
{
    Given()
    .Body("Here's a plaintext request body.")
    .When()
    .Post("http://localhost:9876/plaintext-request-body")
    .Then()
    .StatusCode(201);
}

Payload as a JSON string

    Given()
    .Body("{\"id\": 1, \"user\": \"John Doe\"}")

Serializing an object into JSON

RestAssured.Net uses Json.NET as its JSON serializer.

[Test]
public void ObjectCanBeSerializedToJson()
{
    Location location = new Location
    {
        Country = "United States",
        State = "California",
        ZipCode = 90210,
    };

    Given()
    .ContentType("application/json")
    .Body(location)
    .When()
    .Post("http://localhost:9876/json-serialization")
    .Then()
    .StatusCode(201);
}

This also works with a Dictionary:

[Test]
public void DictionaryCanBeSerializedToJson()
{
    Dictionary<string, object> post = new Dictionary<string, object>
    {
        { "Id", 1 },
        { "Title", "My post title" },
        { "Body", "My post body" },
    };

    Given()
    .Body(post)
    .When()
    .Post("http://localhost:9876/object-serialization")
    .Then()
    .StatusCode(201);
}

It works with anonymous types, too:

[Test]
public void AnonymousObjectCanBeSerializedToJson()
{
    var post = new
    {
        Id = 1,
        Title = "My post title",
        Body = "My post body",
    };

    Given()
    .Body(post)
    .When()
    .Post("http://localhost:9876/object-serialization")
    .Then()
    .StatusCode(201);
}

Serializing an object into XML

RestAssured.Net uses System.Xml.Serializer as its XML serializer.

[Test]
public void ObjectCanBeSerializedToXml()
{
    Location location = new Location
    {
        Country = "United States",
        State = "California",
        ZipCode = 90210,
    };

    Given()
    .ContentType("application/xml")
    .Body(location)
    .When()
    .Post("http://localhost:9876/xml-serialization")
    .Then()
    .StatusCode(201);
}

x-www-form-urlencoded form data

If you need to uploaded x-www-form-urlencoded form data, you can do that using the FormData() method:

[Test]
public void FormDataCanBeSupplied()
{
    var formData = new[]
    {
        new KeyValuePair<string, string>("name", "John Doe"),
        new KeyValuePair<string, string>("email", "[email protected]"),
    };

    Given()
    .FormData(formData)
    .When()
    .Post("http://localhost:9876/form-data")
    .Then()
    .StatusCode(201);
}

The resulting request payload for this example will be name=John+Doe&email=johndoe%40example.com.

Multipart form data

If you need to upload multipart form data (or in other words, upload a file), you can do that using the MultiPart() method:

[Test]
public void MultiPartFormDataWithDefaultControlNameCanBeSupplied()
{
    Given()
    .MultiPart(new FileInfo(@"C:\Users\Bas\some_file_to_upload.pdf"))
    .When()
    .Post("http://localhost:9876/simple-multipart-form-data")
    .Then()
    .StatusCode(201);
}

The default control name used in the multipart upload is file, and the MIME type will be automatically determined based on the file extension. You can control these values by supplying them as arguments to MultiPart(). See the tests for examples.

GraphQL support

Next to support for 'plain' REST request payloads, RestAssured.Net also supports working with GraphQL APIs by means of the GraphQLRequest and GraphQLRequestBuilder objects:

[Test]
public void ParameterizedGraphQLQueryCanBeSupplied()
{
    string parameterizedQuery = @"query getRocketData($id: ID!)
            {
                rocket(id: $id) {
                    name
                    country
                }
            }";

    Dictionary<string, object> variables = new Dictionary<string, object>
    {
        { "id", "falcon1" },
    };

    GraphQLRequest request = new GraphQLRequestBuilder()
        .WithQuery(parameterizedQuery)
        .WithOperationName("getRocketData")
        .WithVariables(variables)
        .Build();

    Given()
    .GraphQL(request)
    .When()
    .Post("http://localhost:9876/graphql-with-variables")
    .Then()
    .StatusCode(200)
    .Body("$.data.rocket.country", NHamcrest.Is.EqualTo("Republic of the Marshall Islands"));
}

Using request specifications

With request specifications, you can supply common characteristics of requests to make in your tests in a single place:

private RequestSpecification? requestSpecification;

[SetUp]
public void CreateRequestSpecifications()
{
    this.requestSpecification = new RequestSpecBuilder()
        .WithScheme("http")
        .WithHostName("localhost")
        .WithBasePath("api")
        .WithPort(9876)
        .WithTimeout(TimeSpan.FromSeconds(200))
        .WithUserAgent("MyUserAgent", "1.0")
        .WithProxy(new WebProxy("http://yourproxy.com:80", true)
        .WithHeader("my_request_spec_header", "my_request_spec_header_value")
        .WithContentType("application/json")
        .WithContentEncoding(Encoding.ASCII)
        .WithBasicAuth("username", "password")
        .WithOAuth2("this_is_my_token")
        .WithRelaxedHttpsValidation()
        .WithRequestLogLevel(RequestLogLevel.All)
        .Build();
}

and then use those in your test:

[Test]
public void RequestSpecificationCanBeUsed()
{
    Given()
    .Spec(this.requestSpecification)
    .When()
    .Get("/request-specification")
    .Then()
    .StatusCode(200);
}

The test will use the scheme, host and port supplied in the request specification to construct the desired URL http://localhost:9876/api/request-specification. Here's the business logic:

  • If the endpoint passed to the method calling the endpoint (e.g., Get()) is passed a valid URI, the scheme, host and port set in the request specification will be ignored.
  • The hostname should be supplied without the scheme (http, https, etc.) to construct a valid URI.
  • Base paths can be supplied with or without a leading or trailing /.
  • Sensible defaults are used when no values are supplied: scheme defaults to http, hostname defaults to localhost, port defaults to -1 (to ensure that the default for the set scheme will be used), base path defaults to an empty string.

Logging request details

For debugging and exploratory testing purposes, you can log request details to the console:

[Test]
public void RequestDetailsCanBeWrittenToStandardOutputForJson()
{
    Given()
    .Log(RequestLogLevel.All)
    .When()
    .Get("http://localhost:9876/log-json-response")
    .Then()
    .StatusCode(200);
}

This prints the endpoint, request headers and the request body to the console. Both JSON and XML will be pretty printed.

Other available request log levels are RequestLogLevel.None, RequestLogLevel.Headers, RequestLogLevel.Body and RequestLogLevel.Endpoint.

You can also configure the request logging level as part of a RequestSpecification, or globally through the static RestAssuredConfiguration.

The Log().All(), Log().Endpoint(), Log().Headers() and Log().Body() methods are deprecated as of version 2.2.0 and will be removed in version 3.0.0.

Logging response details

For debugging and exploratory testing purposes, you can also log response details to the console:

[Test]
public void ResponseDetailsCanBeWrittenToStandardOutputForJson()
{
    Given()
    .When()
    .Get("http://localhost:9876/log-json-response")
    .Then()
    .Log(ResponseLogLevel.All)
    .And()
    .StatusCode(200);
}

This prints the response status code, response headers, the response body and the response time to the console. Both JSON and XML will be pretty printed.

Other available response log levels are

  • ResponseLogLevel.None - logs no response details
  • ResponseLogLevel.Headers - logs the response status code and the response headers
  • ResponseLogLevel.Body - logs the response status code and the response body
  • ResponseLogLevel.ResponseTime - logs the response status code and the response round-trip time
  • ResponseLogLevel.OnError - logs the same as ResponseLogLevel.All, but only if the response status code is 4xx or 5xx
  • ResponseLogLevel.OnVerificationFailure - logs the same as ResponseLogLevel.All, but only if a verification fails, i.e., when RestAssured.Net throws a ResponseVerificationException

You can also configure the response logging level globally through the static RestAssuredConfiguration.

The Log().All(), Log().Status(), Log().Headers(), Log().Body() and Log().Time() methods are deprecated as of version 2.2.0 and will be removed in version 3.0.0.

Logging when using xUnit

By design, xUnit does not automatically print Console.WriteLine() and other print statements to the console. Since RestAssured.Net relies on Console.WriteLine() for logging request and response details, this means that you won't see anything in the console when you're using xUnit.

Here's an example workaround that redirects logging statements to the console when using xUnit:

public class ExampleXunitLoggingTest
{
    private readonly ITestOutputHelper output;

    public ExampleXunitLoggingTest(ITestOutputHelper output)
    {
        this.output = output;
    }

    public class ConsoleWriter : StringWriter
    {
        private ITestOutputHelper output;

        public ConsoleWriter(ITestOutputHelper output)
        {
            this.output = output;
        }

        public override void WriteLine(string? m)
        {
            output.WriteLine(m);
        }
    }

    [Fact]
    public void TestLogging()
    {
        Console.SetOut(new ConsoleWriter(output));

        Given()
            .Log(RequestLogLevel.All)
        .When()
            .Get("https://jsonplaceholder.typicode.com/users/1")
        .Then()
            .Log(ResponseLogLevel.All)
        .And()
            .StatusCode(200)
            .Body("$.name", NHamcrest.Is.EqualTo("Leanne Graham"));
    }
}
Clone this wiki locally