-
-
Notifications
You must be signed in to change notification settings - Fork 22
Usage Guide
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.
- Create a new test project using your testing framework of choice. RestAssured.Net targets .NET 6, .NET 7, .NET 8 and .NET 9.
- Add the latest RestAssured.Net NuGet package to your project
- Add the following using directive to your test class:
using static RestAssured.Dsl;
- Write and run your tests!
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));
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"));
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"));
You can verify the value of a response cookie (including secure and HTTP-only cookies) using an NHamcrest matcher:
[Test]
public void GenericCookieValueCanBeVerified()
{
Given()
.When()
.Get("http://localhost:9876/response-with-generic-cookie")
.Then()
.Cookie("my_cookie", NHamcrest.Is.EqualTo("my_value"));
}
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)
You can verify the response time of the API call (measured using a System.Diagnostics.Stopwatch
) against an NHamcrest matcher like this:
[Test]
public void ResponseTimeCanBeVerified()
{
Given()
.When()
.Get($"http://localhost:9876/delayed-response")
.Then()
.ResponseTime(NHamcrest.Is.GreaterThan(TimeSpan.FromMilliseconds(200)));
}
If you want to check that the response body length (calculated using the actual response body, not the value of the Content-Length
header) matches certain expectations, you can do that using an NHamcrest matcher:
[Test]
public void ResponseBodyLengthCanBeVerified()
{
Given()
.When()
.Get("http://localhost:9876/json-response-body")
.Then()
.Log(RestAssured.Response.Logging.ResponseLogLevel.All)
.ResponseBodyLength(NHamcrest.Is.GreaterThan(25));
}
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 an NJsonSchema.JsonSchema
object. If supplied as a string, MatchesJsonSchema()
will throw a ResponseVerificationException
if the schema cannot be parsed by NJsonSchema.
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();
}
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")
.DeserializeTo(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):
.DeserializeTo(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()
.DeserializeTo(typeof(Location));
Assert.That(responseLocation.Country, Is.EqualTo("United States"));
Assert.That(responseLocation.Places?.Count, Is.EqualTo(2));
}
As of version 3.0.0, you can also pass in custom Newtonsoft.Json.JsonSerializerSettings
when deserializing an object from JSON:
[Test]
public void CustomJsonSerializerSettingsCanBeSuppliedWhenDeserializing()
{
JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.MissingMemberHandling = MissingMemberHandling.Error;
Place place = (Place)Given()
.When()
.Get("http://localhost:9876/object-deserialization-custom-settings")
.Then()
.UsingJsonSerializerSettings(jsonSerializerSettings)
.DeserializeTo(typeof(Place));
}
You can extract the entire response body as a string:
[Test]
public void ResponseBodyCanBeExtracted()
{
string responseBodyAsString = Given()
.When()
.Get($"{MOCK_SERVER_BASE_URL}/json-response-body")
.Then()
.StatusCode(200)
.Extract().Body();
}
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));
}
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)
[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"));
}
You can extract the value of a response cookie (including secure and HTTP-only cookies) as a string:
[Test]
public void GenericCookieValueCanBeExtracted()
{
string cookieValue = Given()
.When()
.Get("http://localhost:9876/response-with-generic-cookie")
.Then()
.Extract().Cookie("my_cookie");
}
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"));
}
RestAssured.Net supports making HTTP calls using GET, POST, PUT, PATCH, DELETE, HEAD and OPTIONS. You can select the HTTP verb to be used by using the corresponding method (Get()
for an HTTP GET, etc.).
Alternatively (and this is especially useful when checking for Broken Function Level Authorization), you can use Invoke(string endpoint, System.Net.Http.HttpMethod httpMethod)
to invoke an endpoint with a specific HTTP verb.
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.
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 an object of type IEnumerable<KeyValuePair<string, object>>
to QueryParams()
:
[Test]
public void MultipleQueryParametersCanBeSpecifiedUsingAListOfKeyValuePairs()
{
var queryParams = new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("name", "ny_name"),
new KeyValuePair<string, object>("id", "my_id"),
};
Given()
.QueryParams(queryParams)
.When()
.Get("http://localhost:9876/multiple-query-params")
.Then()
.StatusCode(200);
}
You can also set query parameters as part of a RequestSpecification.
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 a placeholder name that matches the one in the PathParam()
definition and place it between square brackets ([
and ]
). 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);
}
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.
NTLM authentication
Given()
.NtlmAuth()
This will set NTLM authentication to the value of System.Net.CredentialCache.DefaultNetworkCredentials
. An overload is available that lets you set the username, password and domain yourself. All arguments default to an empty string.
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.
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.
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.
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.
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 DisableSslCertificateValidation()
:
[Test]
public void HttpsConnectionsCanBeUsed()
{
Given()
.DisableSslCertificateValidation()
.When()
.Get("https://localhost:8443/ssl-endpoint")
.Then()
.StatusCode(200);
}
You can also disable SSL certificate validation as part of a RequestSpecification, or globally through the static RestAssuredConfiguration.
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\"}")
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);
}
and with records:
private record BlogPostRecord(int id, string title, string body);
[Test]
public void RecordCanBeSerializedToJson()
{
var post = new BlogPostRecord(this.blogPost.Id, this.blogPost.Title, this.blogPost.Body);
Given()
.Body(post)
.When()
.Post("http://localhost:9876/object-serialization")
.Then()
.StatusCode(201);
}
As of version 3.0.0, you can also pass in custom Newtonsoft.Json.JsonSerializerSettings
when serializing an object to JSON:
[Test]
public void CustomJsonSerializerSettingsCanBeSuppliedInTestBody()
{
JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.DateFormatString = "yyyy-MM-dd";
var post = new
{
Id = 1,
Title = "My post title",
Date = new DateTime(1999, 12, 31, 23, 59, 59, 0), // date will now show up as '1999-12-31' in the request
};
Given()
.JsonSerializerSettings(jsonSerializerSettings)
.Body(post)
.When()
.Post("http://localhost:9876/object-serialization-custom-settings")
.Then()
.StatusCode(201);
}
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);
}
You can also serialize a C# record to XML. Please note that the record requires a parameterless constructor for this to work.
public record BlogPostRecord(int id, string title, string body)
{
public BlogPostRecord()
: this(0, string.Empty, string.Empty)
{
}
}
[Test]
public void RecordCanBeSerializedToXml()
{
var post = new BlogPostRecord(123, "My blog post title", "My blog post body");
Given()
.ContentType("application/xml")
.Body(post)
.When()
.Post("http://localhost:9876/xml-serialization-from-record")
.Then()
.StatusCode(201);
}
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
.
If you need to upload multipart form data, 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.
For other multipart data types, you can use the MultiPart(string name, HttpContent content)
method for single data entities, or MultiPart(Dictionary<string, HttpContent> content)
to add multiple entities in a single call. Again, the tests contain working examples.
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"));
}
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()
.WithBaseUri("http://localhost")
.WithScheme("http") // deprecated
.WithHostName("localhost") // deprecated
.WithBasePath("api")
.WithPort(9876)
.WithQueryParam("param_name", "param_value")
.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")
.WithContentType("application/json")
.WithContentEncoding(Encoding.ASCII)
.WithDisabledSslCertificateValidation()
.WithJsonSerializerSettings(new JsonSerializerSettings())
.WithHttpCompletionOption(HttpCompletionOption.ResponseHeadersRead)
.WithLogConfiguration(new LogConfiguration())
.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 tolocalhost
, port defaults to-1
(to ensure that the default for the set scheme will be used), base path defaults to an empty string.
For debugging and exploratory testing purposes, you can log request and response details to the console:
[Test]
public void RequestDetailsCanBeWrittenToStandardOutputForJson()
{
var logConfig = new LogConfiguration {
RequestLogLevel = RequestLogLevel.All,
ResponseLogLevel = ResponseLogLevel.All,
SensitiveRequestHeadersAndCookies = new List<string>() { "SensitiveRequestHeader" },
}
Given()
.Log(logConfig)
.When()
.Get("http://localhost:9876/log-json-response")
.Then()
.StatusCode(200);
}
For the request, 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
.
This prints the response status code, response headers (including cookies), 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 (including cookies) -
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 asResponseLogLevel.All
, but only if the response status code is 4xx or 5xx -
ResponseLogLevel.OnVerificationFailure
- logs the same asResponseLogLevel.All
, but only if a verification fails, i.e., when RestAssured.Net throws aResponseVerificationException
You can also configure the log configuration as part of a RequestSpecification, or globally through the static RestAssuredConfiguration.
If you want to prevent certain sensitive header or cookie values from turning up in the logging, you can pass these as a List<string>
property in the LogConfiguration
object for the request or response and their value will be replaced with *****
in the logging:
[Test]
public void SingleSensitiveRequestHeaderIsMasked()
{
var logConfig = new LogConfiguration {
RequestLogLevel = RequestLogLevel.All,
ResponseLogLevel = ResponseLogLevel.All,
SensitiveRequestHeadersAndCookies = new List<string>() { "SensitiveRequestHeader" },
}
Given()
.Log(logConfig)
.And()
.Header("NonsensitiveRequestHeader", "This one is printed")
.Header("SensitiveRequestHeader", "This one is masked")
.When()
.Get("http://localhost:9876/masking-sensitive-data")
.Then()
.StatusCode(200);
}
You can also specify a list of sensitive request headers and cookies in a RequestSpecification
.
For specifying which headers and cookies to mask when logging response details, use the SensitiveResponseHeadersAndCookies
property.
In some cases, such as when working with APIs that use server-sent events, it might be useful not to have to wait until the API provider closes the connection. You can tell RestAssured .Net to not wait for this by setting the HttpCompletionOption
to ResponseHeadersRead
(default is ResponseContentRead
):
Given()
.HttpCompletionOption(HttpCompletionOption.ResponseHeadersRead)
In some cases, for example when writing tests for ASP.NET Core Web APIs, you'll need to use the System.Net.Http.HttpClient
provided by the WebApplicationFactory
. From RestAssured.Net 4.1.0 onwards, you can inject that HttpClient
using an optional argument to the Given()
method like this:
public void UsingACustomHttpClient()
{
var webAppFactory = new WebApplicationFactory<Program>();
var httpClient = webAppFactory.CreateDefaultClient();
Given(httpClient)
.When()
.Get("http://localhost:5154/weatherforecast")
.Then()
.StatusCode(HttpStatusCode.OK);
}
The repository contains an example service and corresponding tests to demonstrate the use of this feature.
More background information about this feature can also be found in this blog post.
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;
}
[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"));
}
}
public class ConsoleWriter : StringWriter
{
private ITestOutputHelper output;
public ConsoleWriter(ITestOutputHelper output)
{
this.output = output;
}
public override void WriteLine(string? m)
{
output.WriteLine(m);
}
}