Last active
October 20, 2016 11:13
-
-
Save JulianMay/ef3d367a22829181e42a179fa97ce51f to your computer and use it in GitHub Desktop.
Assertion DSL for Rest Responses
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using NUnit.Framework; | |
using RestSharp; | |
namespace ProjectName.Tests.System | |
{ | |
public class CompositeResponseAssert : IResponseAssert | |
{ | |
private readonly ResponseAssert[] _responses; | |
public CompositeResponseAssert(params IRestResponse[] responses) | |
{ | |
if (responses == null) throw new ArgumentNullException("responses"); | |
if (responses.Length < 1) throw new ArgumentException("At least 1 is needed for assertions to make sense"); | |
_responses = responses.Select(x=>new ResponseAssert(x)).ToArray(); | |
} | |
private IResponseAssert MakeAssertionForResponses(Action<ResponseAssert> assertion) | |
{ | |
for(int i = 1; i <=_responses.Length;i++) | |
{ | |
try | |
{ | |
assertion(_responses[i - 1]); | |
} | |
catch(AssertionException assertFail) | |
{ | |
throw new WrappedAssertionException( | |
string.Format("Assertion of response no. {0} failed:", i), | |
assertFail, string.Empty); | |
} | |
} | |
return this; | |
} | |
public IResponseAssert HasContentThatContains(string someContent) | |
{ | |
return MakeAssertionForResponses(x => x.HasContentThatContains(someContent)); | |
} | |
public IResponseAssert HasFollowingJsonBody(string expected, bool allowUnexpectedProperties = true, bool allowUndefinedArrayMembers = false) | |
{ | |
return MakeAssertionForResponses(x => x.HasFollowingJsonBody(expected,allowUnexpectedProperties)); | |
} | |
public IResponseAssert Is200Ok() | |
{ | |
return MakeAssertionForResponses(x => x.Is200Ok()); | |
} | |
public IResponseAssert Is400BadRequest() | |
{ | |
return MakeAssertionForResponses(x => x.Is400BadRequest()); | |
} | |
public IResponseAssert Is404NotFoundError() | |
{ | |
return MakeAssertionForResponses(x => x.Is404NotFoundError()); | |
} | |
public IResponseAssert Is405MethodNotAllowedError() | |
{ | |
return MakeAssertionForResponses(x => x.Is405MethodNotAllowedError()); | |
} | |
public IResponseAssert Is422UnprocessableEntity() | |
{ | |
return MakeAssertionForResponses(x => x.Is422UnprocessableEntity()); | |
} | |
public IResponseAssert IsNot4xxError() | |
{ | |
return MakeAssertionForResponses(x => x.IsNot4xxError()); | |
} | |
public IResponseAssert HasNoContentBody() | |
{ | |
return MakeAssertionForResponses(x => x.HasNoContentBody()); | |
} | |
public IResponseAssert HasRawContent(byte[] binary) | |
{ | |
return MakeAssertionForResponses(x => x.HasRawContent(binary)); | |
} | |
public IResponseAssert Is201Created() | |
{ | |
return MakeAssertionForResponses(x => x.Is201Created()); | |
} | |
public IResponseAssert HasExpectedHeader(string headerName, Func<string, bool> expectation) | |
{ | |
return MakeAssertionForResponses(x => x.HasExpectedHeader(headerName, expectation)); | |
} | |
public IResponseAssert HasContentType(string contentType) | |
{ | |
return MakeAssertionForResponses(x => x.HasContentType(contentType)); | |
} | |
public class WrappedAssertionException : AssertionException | |
{ | |
private readonly AssertionException _ae; | |
public WrappedAssertionException(string messageHead, AssertionException ae, string messageTail) | |
: base(string.Format("{0}\r\n{1}\r\n{2}", messageHead, ae.Message, messageTail)) | |
{ | |
if (ae == null) throw new ArgumentNullException("ae"); | |
_ae = ae; | |
} | |
public override string StackTrace | |
{ | |
get | |
{ | |
return _ae.StackTrace; | |
} | |
} | |
public override string Source | |
{ | |
get | |
{ | |
return _ae.Source; | |
} | |
set | |
{ | |
_ae.Source = value; | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I recommend writing a baseclass with factory methods for asserters, so you can do something like this: | |
var result = --Get IRestResponse Here-- | |
AssertResponse(result) | |
.Is200Ok() | |
.HasFollowingJsonBody(@" | |
{ | |
"ItWorks": true | |
} | |
"); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace ProjectName.Tests.System | |
{ | |
public interface IResponseAssert | |
{ | |
IResponseAssert HasContentThatContains(string someContent); | |
IResponseAssert HasRawContent(byte[] binary); | |
IResponseAssert HasFollowingJsonBody(string expected, bool allowUnexpectedProperties = true, bool allowUndefinedArrayMembers = false); | |
IResponseAssert Is200Ok(); | |
IResponseAssert Is201Created(); | |
IResponseAssert Is400BadRequest(); | |
IResponseAssert Is404NotFoundError(); | |
IResponseAssert Is405MethodNotAllowedError(); | |
IResponseAssert Is422UnprocessableEntity(); | |
IResponseAssert IsNot4xxError(); | |
IResponseAssert HasNoContentBody(); | |
IResponseAssert HasContentType(string contentType); | |
IResponseAssert HasExpectedHeader(string headerName, Func<string, bool> expectation); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Net; | |
using NUnit.Framework; | |
using RestSharp; | |
using Spectra.Hc.Tests.System.json; | |
using System.Collections; | |
using System.Linq; | |
namespace ProjectName.Tests.System | |
{ | |
public class ResponseAssert : IResponseAssert | |
{ | |
private JsonComparer _jsonComparer = new JsonComparer(); //See "JsonComparer.cs" gist | |
private readonly IRestResponse _response; | |
public ResponseAssert(IRestResponse response) | |
{ | |
if (response == null) | |
throw new ArgumentNullException("response"); | |
_response = response; | |
} | |
public IResponseAssert Is404NotFoundError() | |
{ | |
Assert.That(_response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); | |
return this; | |
} | |
public IResponseAssert Is405MethodNotAllowedError() | |
{ | |
Assert.That(_response.StatusCode, Is.EqualTo(HttpStatusCode.MethodNotAllowed)); | |
return this; | |
} | |
public IResponseAssert Is400BadRequest() | |
{ | |
Assert.That(_response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); | |
return this; | |
} | |
public IResponseAssert HasContentThatContains(string someContent) | |
{ | |
StringAssert.Contains(someContent, _response.Content); | |
return this; | |
} | |
public IResponseAssert HasNoContentBody() | |
{ | |
Assert.That(string.IsNullOrWhiteSpace(_response.Content), string.Format("Content was expected to be blank, but was \"{0}\"", _response.Content) ); | |
return this; | |
} | |
public IResponseAssert Is422UnprocessableEntity() | |
{ | |
var responseCode = (int)_response.StatusCode; | |
Assert.That(responseCode, Is.EqualTo(422)); | |
return this; | |
} | |
public IResponseAssert IsNot4xxError() | |
{ | |
var responseCode = (int)_response.StatusCode; | |
Assert.That(responseCode < 400, "Returned content is:\r\n {0}", _response.Content); | |
return this; | |
} | |
public IResponseAssert Is200Ok() | |
{ | |
Assert.That(_response.StatusCode, Is.EqualTo(HttpStatusCode.OK), | |
string.Format("The response's StatusCode was expected to be '{0}', but was '{1}'.\r\n The content of the response was: {2}", | |
(int)HttpStatusCode.OK, (int)_response.StatusCode, _response.Content)); | |
return this; | |
} | |
public IResponseAssert Is201Created() | |
{ | |
Assert.That(_response.StatusCode, Is.EqualTo(HttpStatusCode.Created), | |
string.Format("The response's StatusCode was expected to be '{0}', but was '{1}'.\r\n The content of the response was: {2}", | |
(int)HttpStatusCode.Created, (int)_response.StatusCode, _response.Content)); | |
return this; | |
} | |
/// <summary> | |
/// Asserts comparison between json-elements | |
/// Use *somestring* as wildcard (not the json-string "*somestring*", but actual invalid json-value *somestring* ) | |
/// </summary> | |
/// <param name="expected"></param> | |
/// <param name="allowUnexpectedProperties"></param> | |
/// <returns></returns> | |
public IResponseAssert HasFollowingJsonBody(string expected, bool allowUnexpectedProperties = true, bool allowUndefinedArrayMembers = false) | |
{ | |
var result = _jsonComparer.JsonDifferenceReport("content body", expected, _response.Content, allowUnexpectedProperties, allowUndefinedArrayMembers); | |
if (result.Report.HasUnexpectedDifferences) | |
{ | |
Assert.Fail(string.Format("\r\n{0}\r\n##EXPECTED##\r\n{1}\r\n\r\n##ACTUAL##\r\n{2}", result.Report.Report(new string[0]), result.ExpectedPretty, result.ActualPretty)); | |
} | |
return this; | |
} | |
public IResponseAssert HasContentType(string contentType) | |
{ | |
StringAssert.AreEqualIgnoringCase(contentType, _response.ContentType); | |
return this; | |
} | |
public IResponseAssert HasRawContent(byte[] binary) | |
{ | |
Assert.That(((IStructuralEquatable)binary).Equals(_response.RawBytes, StructuralComparisons.StructuralEqualityComparer), "The actual binary content is different from the expected"); | |
return this; | |
} | |
public IResponseAssert HasExpectedHeader(string headerName, Func<string,bool> expectation) | |
{ | |
var n = headerName.ToLowerInvariant(); | |
var h = _response.Headers.FirstOrDefault(x => x.Name.ToLowerInvariant().Equals(n)); | |
Assert.IsNotNull(h, string.Format("The response has no '{0}' header", n)); | |
Assert.That(expectation(h.Value.ToString()), "The '{0}'-headers value '{1}' did not match expectation"); | |
return this; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment