Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce assertions for StatusCode of HttpResponseMessage #1737

Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions Src/FluentAssertions/AssertionExtensions.cs
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics;
using System.IO;
using System.Linq.Expressions;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using System.Xml.Linq;
Expand Down Expand Up @@ -302,6 +303,16 @@ public static NullableBooleanAssertions Should(this bool? actualValue)
return new NullableBooleanAssertions(actualValue);
}

/// <summary>
/// Returns an <see cref="HttpResponseMessageAssertions"/> object that can be used to assert the
/// current <see cref="HttpResponseMessage"/>.
/// </summary>
[Pure]
public static HttpResponseMessageAssertions Should(this HttpResponseMessage actualValue)
{
return new HttpResponseMessageAssertions(actualValue);
}

/// <summary>
/// Returns an <see cref="GuidAssertions"/> object that can be used to assert the
/// current <see cref="Guid"/>.
Expand Down
234 changes: 234 additions & 0 deletions Src/FluentAssertions/Primitives/HttpResponseMessageAssertions.cs
@@ -0,0 +1,234 @@
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using FluentAssertions.Execution;

namespace FluentAssertions.Primitives
{
/// <summary>
/// Contains a number of methods to assert that a <see cref="HttpResponseMessage"/> is in the expected state.
/// </summary>
[DebuggerNonUserCode]
public class HttpResponseMessageAssertions : HttpResponseMessageAssertions<HttpResponseMessageAssertions>
{
public HttpResponseMessageAssertions(HttpResponseMessage value)
: base(value)
{
}
}

/// <summary>
/// Contains a number of methods to assert that a <see cref="HttpResponseMessage" /> is in the expected state.
/// </summary>
[DebuggerNonUserCode]
public class HttpResponseMessageAssertions<TAssertions> : ObjectAssertions<HttpResponseMessage, TAssertions>
where TAssertions : HttpResponseMessageAssertions<TAssertions>
{
protected HttpResponseMessageAssertions(HttpResponseMessage value)
: base(value)
{
}

/// <summary>
/// Asserts that the <see cref="HttpStatusCode"/> is successful (2xx).
/// </summary>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public AndConstraint<TAssertions> BeSuccessful(string because = "", params object[] becauseArgs)
{
var success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be successful (2xx){reason}, but HttpResponseMessage was <null>.");

if (success)
{
Execute.Assertion
.ForCondition(Subject!.IsSuccessStatusCode)
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be successful (2xx){reason}, but found {0}.", Subject.StatusCode);
}

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that the <see cref="HttpStatusCode"/> is redirection (3xx).
/// </summary>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public AndConstraint<TAssertions> BeRedirection(string because = "", params object[] becauseArgs)
{
var success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be redirection (3xx){reason}, but HttpResponseMessage was <null>.");

if (success)
{
Execute.Assertion
.ForCondition((int)Subject!.StatusCode is >= 300 and <= 399)
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be redirection (3xx){reason}, but found {0}.", Subject.StatusCode);
}

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that the <see cref="HttpStatusCode"/> is either client (4xx) or server error (5xx).
/// </summary>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public AndConstraint<TAssertions> HaveError(string because = "", params object[] becauseArgs)
{
var success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be an error{reason}, but HttpResponseMessage was <null>.");

if (success)
{
Execute.Assertion
.ForCondition(IsClientError() || IsServerError())
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be an error{reason}, but found {0}.", Subject.StatusCode);
}

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that the <see cref="HttpStatusCode"/> is client error (4xx).
/// </summary>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public AndConstraint<TAssertions> HaveClientError(string because = "", params object[] becauseArgs)
{
var success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be client error (4xx){reason}, but HttpResponseMessage was <null>.");

if (success)
{
Execute.Assertion
.ForCondition(IsClientError())
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be client error (4xx){reason}, but found {0}.", Subject.StatusCode);
}

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that the <see cref="HttpStatusCode"/> is server error (5xx).
/// </summary>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public AndConstraint<TAssertions> HaveServerError(string because = "", params object[] becauseArgs)
{
var success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be server error (5xx){reason}, but HttpResponseMessage was <null>.");

if (success)
mu88 marked this conversation as resolved.
Show resolved Hide resolved
{
Execute.Assertion
.ForCondition(IsServerError())
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be server error (5xx){reason}, but found {0}.", Subject.StatusCode);
}

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that the value is equal to the specified <paramref name="expected"/> value.
mu88 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="expected">The expected value</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public AndConstraint<TAssertions> HaveStatusCode(HttpStatusCode expected, string because = "", params object[] becauseArgs)
{
var success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be {0}{reason}, but HttpResponseMessage was <null>.", expected);

if (success)
mu88 marked this conversation as resolved.
Show resolved Hide resolved
{
Execute.Assertion
.ForCondition(Subject!.StatusCode == expected)
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode to be {0}{reason}, but found {1}.", expected, Subject.StatusCode);
}

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that the value is not equal to the specified <paramref name="unexpected"/> value.
mu88 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="unexpected">The unexpected value</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because"/>.
/// </param>
public AndConstraint<TAssertions> NotHaveStatusCode(HttpStatusCode unexpected, string because = "", params object[] becauseArgs)
{
var success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode not to be {0}{reason}, but HttpResponseMessage was <null>.", unexpected);

if (success)
{
Execute.Assertion
.ForCondition(Subject!.StatusCode != unexpected)
.BecauseOf(because, becauseArgs)
.FailWith("Expected HttpStatusCode not to be {0}{reason}, but found {1}.", unexpected, Subject.StatusCode);
}

return new AndConstraint<TAssertions>((TAssertions)this);
}

private bool IsServerError() => (int)Subject.StatusCode is >= 500 and <= 599;

private bool IsClientError() => (int)Subject.StatusCode is >= 400 and <= 499;
}
}
Expand Up @@ -66,6 +66,7 @@ namespace FluentAssertions
public static FluentAssertions.Primitives.NullableGuidAssertions Should(this System.Guid? actualValue) { }
public static FluentAssertions.Streams.BufferedStreamAssertions Should(this System.IO.BufferedStream actualValue) { }
public static FluentAssertions.Streams.StreamAssertions Should(this System.IO.Stream actualValue) { }
public static FluentAssertions.Primitives.HttpResponseMessageAssertions Should(this System.Net.Http.HttpResponseMessage actualValue) { }
public static FluentAssertions.Reflection.AssemblyAssertions Should(this System.Reflection.Assembly assembly) { }
public static FluentAssertions.Types.ConstructorInfoAssertions Should(this System.Reflection.ConstructorInfo constructorInfo) { }
public static FluentAssertions.Types.MethodInfoAssertions Should(this System.Reflection.MethodInfo methodInfo) { }
Expand Down Expand Up @@ -1901,6 +1902,22 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint<TAssertions> NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEmpty(string because = "", params object[] becauseArgs) { }
}
public class HttpResponseMessageAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions<FluentAssertions.Primitives.HttpResponseMessageAssertions>
{
public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
}
public class HttpResponseMessageAssertions<TAssertions> : FluentAssertions.Primitives.ObjectAssertions<System.Net.Http.HttpResponseMessage, TAssertions>
where TAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions<TAssertions>
{
protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
public FluentAssertions.AndConstraint<TAssertions> BeRedirection(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> BeSuccessful(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> HaveClientError(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> HaveError(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> HaveServerError(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> HaveStatusCode(System.Net.HttpStatusCode expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotHaveStatusCode(System.Net.HttpStatusCode unexpected, string because = "", params object[] becauseArgs) { }
}
public class NullableBooleanAssertions : FluentAssertions.Primitives.NullableBooleanAssertions<FluentAssertions.Primitives.NullableBooleanAssertions>
{
public NullableBooleanAssertions(bool? value) { }
Expand Down
Expand Up @@ -66,6 +66,7 @@ namespace FluentAssertions
public static FluentAssertions.Primitives.NullableGuidAssertions Should(this System.Guid? actualValue) { }
public static FluentAssertions.Streams.BufferedStreamAssertions Should(this System.IO.BufferedStream actualValue) { }
public static FluentAssertions.Streams.StreamAssertions Should(this System.IO.Stream actualValue) { }
public static FluentAssertions.Primitives.HttpResponseMessageAssertions Should(this System.Net.Http.HttpResponseMessage actualValue) { }
public static FluentAssertions.Reflection.AssemblyAssertions Should(this System.Reflection.Assembly assembly) { }
public static FluentAssertions.Types.ConstructorInfoAssertions Should(this System.Reflection.ConstructorInfo constructorInfo) { }
public static FluentAssertions.Types.MethodInfoAssertions Should(this System.Reflection.MethodInfo methodInfo) { }
Expand Down Expand Up @@ -1901,6 +1902,22 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint<TAssertions> NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEmpty(string because = "", params object[] becauseArgs) { }
}
public class HttpResponseMessageAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions<FluentAssertions.Primitives.HttpResponseMessageAssertions>
{
public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
}
public class HttpResponseMessageAssertions<TAssertions> : FluentAssertions.Primitives.ObjectAssertions<System.Net.Http.HttpResponseMessage, TAssertions>
where TAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions<TAssertions>
{
protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
public FluentAssertions.AndConstraint<TAssertions> BeRedirection(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> BeSuccessful(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> HaveClientError(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> HaveError(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> HaveServerError(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> HaveStatusCode(System.Net.HttpStatusCode expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotHaveStatusCode(System.Net.HttpStatusCode unexpected, string because = "", params object[] becauseArgs) { }
}
public class NullableBooleanAssertions : FluentAssertions.Primitives.NullableBooleanAssertions<FluentAssertions.Primitives.NullableBooleanAssertions>
{
public NullableBooleanAssertions(bool? value) { }
Expand Down