From c7fe3e6eb438d9cecba5d24e270d4f31a61f2faf Mon Sep 17 00:00:00 2001
From: mu88 <4560672+mu88@users.noreply.github.com>
Date: Mon, 10 Jan 2022 10:20:03 +0100
Subject: [PATCH] Introduce assertions for StatusCode of HttpResponseMessage
(#1737)
---
Src/FluentAssertions/AssertionExtensions.cs | 11 +
Src/FluentAssertions/FluentAssertions.csproj | 1 +
.../HttpResponseMessageAssertions.cs | 234 +++++++++++++++
.../FluentAssertions/net47.verified.txt | 17 ++
.../netcoreapp2.1.verified.txt | 17 ++
.../netcoreapp3.0.verified.txt | 17 ++
.../netstandard2.0.verified.txt | 17 ++
.../netstandard2.1.verified.txt | 17 ++
Tests/Benchmarks/Benchmarks.csproj | 2 +-
.../HttpResponseMessageAssertionSpecs.cs | 271 ++++++++++++++++++
docs/_data/navigation.yml | 2 +
docs/_pages/httpresponsemessages.md | 28 ++
docs/_pages/releases.md | 1 +
13 files changed, 634 insertions(+), 1 deletion(-)
create mode 100644 Src/FluentAssertions/Primitives/HttpResponseMessageAssertions.cs
create mode 100644 Tests/FluentAssertions.Specs/Primitives/HttpResponseMessageAssertionSpecs.cs
create mode 100644 docs/_pages/httpresponsemessages.md
diff --git a/Src/FluentAssertions/AssertionExtensions.cs b/Src/FluentAssertions/AssertionExtensions.cs
index 9eb3e85dfc..4940cffe48 100644
--- a/Src/FluentAssertions/AssertionExtensions.cs
+++ b/Src/FluentAssertions/AssertionExtensions.cs
@@ -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;
@@ -302,6 +303,16 @@ public static NullableBooleanAssertions Should(this bool? actualValue)
return new NullableBooleanAssertions(actualValue);
}
+ ///
+ /// Returns an object that can be used to assert the
+ /// current .
+ ///
+ [Pure]
+ public static HttpResponseMessageAssertions Should(this HttpResponseMessage actualValue)
+ {
+ return new HttpResponseMessageAssertions(actualValue);
+ }
+
///
/// Returns an object that can be used to assert the
/// current .
diff --git a/Src/FluentAssertions/FluentAssertions.csproj b/Src/FluentAssertions/FluentAssertions.csproj
index 2204810ff1..330b2da149 100644
--- a/Src/FluentAssertions/FluentAssertions.csproj
+++ b/Src/FluentAssertions/FluentAssertions.csproj
@@ -75,6 +75,7 @@
+
diff --git a/Src/FluentAssertions/Primitives/HttpResponseMessageAssertions.cs b/Src/FluentAssertions/Primitives/HttpResponseMessageAssertions.cs
new file mode 100644
index 0000000000..45010cb0ce
--- /dev/null
+++ b/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
+{
+ ///
+ /// Contains a number of methods to assert that a is in the expected state.
+ ///
+ [DebuggerNonUserCode]
+ public class HttpResponseMessageAssertions : HttpResponseMessageAssertions
+ {
+ public HttpResponseMessageAssertions(HttpResponseMessage value)
+ : base(value)
+ {
+ }
+ }
+
+ ///
+ /// Contains a number of methods to assert that a is in the expected state.
+ ///
+ [DebuggerNonUserCode]
+ public class HttpResponseMessageAssertions : ObjectAssertions
+ where TAssertions : HttpResponseMessageAssertions
+ {
+ protected HttpResponseMessageAssertions(HttpResponseMessage value)
+ : base(value)
+ {
+ }
+
+ ///
+ /// Asserts that the is successful (2xx).
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndConstraint 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 .");
+
+ 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)this);
+ }
+
+ ///
+ /// Asserts that the is redirection (3xx).
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndConstraint 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 .");
+
+ 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)this);
+ }
+
+ ///
+ /// Asserts that the is either client (4xx) or server error (5xx).
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndConstraint 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 .");
+
+ 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)this);
+ }
+
+ ///
+ /// Asserts that the is client error (4xx).
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndConstraint 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 .");
+
+ 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)this);
+ }
+
+ ///
+ /// Asserts that the is server error (5xx).
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndConstraint 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 .");
+
+ if (success)
+ {
+ 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)this);
+ }
+
+ ///
+ /// Asserts that the is equal to the specified value.
+ ///
+ /// The expected value
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndConstraint 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 .", expected);
+
+ if (success)
+ {
+ 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)this);
+ }
+
+ ///
+ /// Asserts that the is not equal to the specified value.
+ ///
+ /// The unexpected value
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndConstraint 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 .", 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)this);
+ }
+
+ private bool IsServerError() => (int)Subject.StatusCode is >= 500 and <= 599;
+
+ private bool IsClientError() => (int)Subject.StatusCode is >= 400 and <= 499;
+ }
+}
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
index 0f7328406c..8f749545dd 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
@@ -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) { }
@@ -1909,6 +1910,22 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBeEmpty(string because = "", params object[] becauseArgs) { }
}
+ public class HttpResponseMessageAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions
+ {
+ public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
+ }
+ public class HttpResponseMessageAssertions : FluentAssertions.Primitives.ObjectAssertions
+ where TAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions
+ {
+ protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
+ public FluentAssertions.AndConstraint BeRedirection(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint BeSuccessful(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveClientError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveServerError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveStatusCode(System.Net.HttpStatusCode expected, string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint NotHaveStatusCode(System.Net.HttpStatusCode unexpected, string because = "", params object[] becauseArgs) { }
+ }
public class NullableBooleanAssertions : FluentAssertions.Primitives.NullableBooleanAssertions
{
public NullableBooleanAssertions(bool? value) { }
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt
index 8427581cf0..74366f32aa 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt
@@ -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) { }
@@ -1909,6 +1910,22 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBeEmpty(string because = "", params object[] becauseArgs) { }
}
+ public class HttpResponseMessageAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions
+ {
+ public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
+ }
+ public class HttpResponseMessageAssertions : FluentAssertions.Primitives.ObjectAssertions
+ where TAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions
+ {
+ protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
+ public FluentAssertions.AndConstraint BeRedirection(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint BeSuccessful(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveClientError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveServerError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveStatusCode(System.Net.HttpStatusCode expected, string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint NotHaveStatusCode(System.Net.HttpStatusCode unexpected, string because = "", params object[] becauseArgs) { }
+ }
public class NullableBooleanAssertions : FluentAssertions.Primitives.NullableBooleanAssertions
{
public NullableBooleanAssertions(bool? value) { }
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt
index d0c715644a..742058b3f4 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt
@@ -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) { }
@@ -1909,6 +1910,22 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBeEmpty(string because = "", params object[] becauseArgs) { }
}
+ public class HttpResponseMessageAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions
+ {
+ public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
+ }
+ public class HttpResponseMessageAssertions : FluentAssertions.Primitives.ObjectAssertions
+ where TAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions
+ {
+ protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
+ public FluentAssertions.AndConstraint BeRedirection(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint BeSuccessful(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveClientError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveServerError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveStatusCode(System.Net.HttpStatusCode expected, string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint NotHaveStatusCode(System.Net.HttpStatusCode unexpected, string because = "", params object[] becauseArgs) { }
+ }
public class NullableBooleanAssertions : FluentAssertions.Primitives.NullableBooleanAssertions
{
public NullableBooleanAssertions(bool? value) { }
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt
index 9b13b251b7..9a7e925e43 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt
@@ -65,6 +65,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) { }
@@ -1862,6 +1863,22 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBeEmpty(string because = "", params object[] becauseArgs) { }
}
+ public class HttpResponseMessageAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions
+ {
+ public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
+ }
+ public class HttpResponseMessageAssertions : FluentAssertions.Primitives.ObjectAssertions
+ where TAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions
+ {
+ protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
+ public FluentAssertions.AndConstraint BeRedirection(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint BeSuccessful(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveClientError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveServerError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveStatusCode(System.Net.HttpStatusCode expected, string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint NotHaveStatusCode(System.Net.HttpStatusCode unexpected, string because = "", params object[] becauseArgs) { }
+ }
public class NullableBooleanAssertions : FluentAssertions.Primitives.NullableBooleanAssertions
{
public NullableBooleanAssertions(bool? value) { }
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt
index ea94174c5a..27d7e7a226 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt
@@ -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) { }
@@ -1909,6 +1910,22 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint NotBe(string unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBeEmpty(string because = "", params object[] becauseArgs) { }
}
+ public class HttpResponseMessageAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions
+ {
+ public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
+ }
+ public class HttpResponseMessageAssertions : FluentAssertions.Primitives.ObjectAssertions
+ where TAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions
+ {
+ protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { }
+ public FluentAssertions.AndConstraint BeRedirection(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint BeSuccessful(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveClientError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveServerError(string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint HaveStatusCode(System.Net.HttpStatusCode expected, string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint NotHaveStatusCode(System.Net.HttpStatusCode unexpected, string because = "", params object[] becauseArgs) { }
+ }
public class NullableBooleanAssertions : FluentAssertions.Primitives.NullableBooleanAssertions
{
public NullableBooleanAssertions(bool? value) { }
diff --git a/Tests/Benchmarks/Benchmarks.csproj b/Tests/Benchmarks/Benchmarks.csproj
index 51b8040720..3e61198905 100644
--- a/Tests/Benchmarks/Benchmarks.csproj
+++ b/Tests/Benchmarks/Benchmarks.csproj
@@ -17,4 +17,4 @@
-
\ No newline at end of file
+
diff --git a/Tests/FluentAssertions.Specs/Primitives/HttpResponseMessageAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Primitives/HttpResponseMessageAssertionSpecs.cs
new file mode 100644
index 0000000000..d0513cacc7
--- /dev/null
+++ b/Tests/FluentAssertions.Specs/Primitives/HttpResponseMessageAssertionSpecs.cs
@@ -0,0 +1,271 @@
+using System;
+using System.Net;
+using System.Net.Http;
+using Xunit;
+using Xunit.Sdk;
+
+namespace FluentAssertions.Specs.Primitives
+{
+ public class HttpResponseMessageAssertionSpecs
+ {
+ [Theory]
+ [InlineData(HttpStatusCode.OK)]
+ [InlineData(HttpStatusCode.Accepted)]
+ public void Should_succeed_when_status_code_is_successful(HttpStatusCode statusCodeOfResponse)
+ {
+ // Arrange
+ var testee = new HttpResponseMessage(statusCodeOfResponse);
+
+ // Act / Assert
+ testee.Should().BeSuccessful();
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_status_code_error_is_successful()
+ {
+ // Arrange
+ Action action = () => new HttpResponseMessage(HttpStatusCode.Gone).Should()
+ .BeSuccessful("because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be successful (2xx) because we want to test the failure message, but found HttpStatusCode.Gone {value: 410}.");
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_asserting_success_but_response_is_null()
+ {
+ // Arrange
+ Action action = () =>
+ ((HttpResponseMessage)null).Should().BeSuccessful("because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be successful (2xx) because we want to test the failure message, but HttpResponseMessage was .");
+ }
+
+ [Theory]
+ [InlineData(HttpStatusCode.Moved)]
+ public void Should_succeed_when_status_code_is_redirect(HttpStatusCode statusCodeOfResponse)
+ {
+ // Arrange
+ var testee = new HttpResponseMessage(statusCodeOfResponse);
+
+ // Act / Assert
+ testee.Should().BeRedirection();
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_status_code_error_is_redirection()
+ {
+ // Arrange
+ Action action = () => new HttpResponseMessage(HttpStatusCode.Gone).Should()
+ .BeRedirection("because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be redirection (3xx) because we want to test the failure message, but found HttpStatusCode.Gone {value: 410}.");
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_asserting_redirect_but_response_is_null()
+ {
+ // Arrange
+ Action action = () =>
+ ((HttpResponseMessage)null).Should().BeRedirection("because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be redirection (3xx) because we want to test the failure message, but HttpResponseMessage was .");
+ }
+
+ [Theory]
+ [InlineData(HttpStatusCode.Gone)]
+ [InlineData(HttpStatusCode.BadRequest)]
+ public void Should_succeed_when_status_code_is_client_error(HttpStatusCode statusCodeOfResponse)
+ {
+ // Arrange
+ var testee = new HttpResponseMessage(statusCodeOfResponse);
+
+ // Act / Assert
+ testee.Should().HaveClientError();
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_status_code_success_is_client_error()
+ {
+ // Arrange
+ Action action = () => new HttpResponseMessage(HttpStatusCode.OK).Should()
+ .HaveClientError("because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be client error (4xx) because we want to test the failure message, but found HttpStatusCode.OK {value: 200}.");
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_asserting_client_error_but_response_is_null()
+ {
+ // Arrange
+ Action action = () =>
+ ((HttpResponseMessage)null).Should().HaveClientError("because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be client error (4xx) because we want to test the failure message, but HttpResponseMessage was .");
+ }
+
+ [Theory]
+ [InlineData(HttpStatusCode.InternalServerError)]
+ public void Should_succeed_when_status_code_is_server_error(HttpStatusCode statusCodeOfResponse)
+ {
+ // Arrange
+ var testee = new HttpResponseMessage(statusCodeOfResponse);
+
+ // Act / Assert
+ testee.Should().HaveServerError();
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_status_code_success_is_server_error()
+ {
+ // Arrange
+ Action action = () => new HttpResponseMessage(HttpStatusCode.OK).Should()
+ .HaveServerError("because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be server error (5xx) because we want to test the failure message, but found HttpStatusCode.OK {value: 200}.");
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_asserting_server_error_but_response_is_null()
+ {
+ // Arrange
+ Action action = () =>
+ ((HttpResponseMessage)null).Should().HaveServerError("because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be server error (5xx) because we want to test the failure message, but HttpResponseMessage was .");
+ }
+
+ [Theory]
+ [InlineData(HttpStatusCode.BadRequest)]
+ [InlineData(HttpStatusCode.InternalServerError)]
+ public void Should_succeed_when_status_code_is_error(HttpStatusCode statusCodeOfResponse)
+ {
+ // Arrange
+ var testee = new HttpResponseMessage(statusCodeOfResponse);
+
+ // Act / Assert
+ testee.Should().HaveError();
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_status_code_success_is_error()
+ {
+ // Arrange
+ Action action = () => new HttpResponseMessage(HttpStatusCode.OK).Should()
+ .HaveError("because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be an error because we want to test the failure message, but found HttpStatusCode.OK {value: 200}.");
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_asserting_error_but_response_is_null()
+ {
+ // Arrange
+ Action action = () =>
+ ((HttpResponseMessage)null).Should().HaveError("because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be an error because we want to test the failure message, but HttpResponseMessage was .");
+ }
+
+ [Fact]
+ public void Should_succeed_when_status_code_to_be_equal_to_the_same_value()
+ {
+ // Arrange
+ Action action = () => new HttpResponseMessage(HttpStatusCode.OK).Should().HaveStatusCode(HttpStatusCode.OK);
+
+ // Act / Assert
+ action.Should().NotThrow();
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_status_code_value_to_be_equal_to_a_different_value()
+ {
+ // Arrange
+ Action action = () => new HttpResponseMessage(HttpStatusCode.OK).Should().HaveStatusCode(HttpStatusCode.Gone,
+ "because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be HttpStatusCode.Gone {value: 410} because we want to test the failure message, but found HttpStatusCode.OK {value: 200}.*");
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_asserting_certain_status_code_but_response_is_null()
+ {
+ // Arrange
+ Action action = () => ((HttpResponseMessage)null).Should()
+ .HaveStatusCode(HttpStatusCode.Gone, "because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode to be HttpStatusCode.Gone {value: 410} because we want to test the failure message, but HttpResponseMessage was .");
+ }
+
+ [Fact]
+ public void Should_succeed_when_status_code_value_not_to_be_equal_to_the_same_value()
+ {
+ // Arrange
+ Action action = () => new HttpResponseMessage(HttpStatusCode.OK).Should().NotHaveStatusCode(HttpStatusCode.Gone);
+
+ // Act / Assert
+ action.Should().NotThrow();
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_status_code_value_not_to_be_equal_to_a_different_value()
+ {
+ // Arrange
+ Action action = () => new HttpResponseMessage(HttpStatusCode.OK).Should().NotHaveStatusCode(HttpStatusCode.OK,
+ "because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode not to be HttpStatusCode.OK {value: 200} because we want to test the failure message, but found HttpStatusCode.OK {value: 200}.*");
+ }
+
+ [Fact]
+ public void Should_fail_with_descriptive_message_when_asserting_against_certain_status_code_but_response_is_null()
+ {
+ // Arrange
+ Action action = () => ((HttpResponseMessage)null).Should()
+ .NotHaveStatusCode(HttpStatusCode.Gone, "because we want to test the failure {0}", "message");
+
+ // Act / Assert
+ action.Should().Throw()
+ .WithMessage(
+ "Expected HttpStatusCode not to be HttpStatusCode.Gone {value: 410} because we want to test the failure message, but HttpResponseMessage was .");
+ }
+ }
+}
diff --git a/docs/_data/navigation.yml b/docs/_data/navigation.yml
index 51c26a1224..0ec09d306d 100644
--- a/docs/_data/navigation.yml
+++ b/docs/_data/navigation.yml
@@ -73,6 +73,8 @@ sidebar:
url: /xml
- title: Execution Time
url: /executiontime
+ - title: HTTP response messages
+ url: /httpresponsemessages
- title: Extensibility
url: /extensibility
diff --git a/docs/_pages/httpresponsemessages.md b/docs/_pages/httpresponsemessages.md
new file mode 100644
index 0000000000..96575c1087
--- /dev/null
+++ b/docs/_pages/httpresponsemessages.md
@@ -0,0 +1,28 @@
+---
+title: HttpResponseMessages
+permalink: /httpresponsemessages/
+layout: single
+classes: wide
+sidebar:
+nav: "sidebar"
+---
+
+```csharp
+var successfulResponse = new HttpResponseMessage(HttpStatusCode.OK);
+successfulResponse.Should().BeSuccessful("it's set to OK"); // (HttpStatusCode = 2xx)
+
+var redirectResponse = new HttpResponseMessage(HttpStatusCode.Moved);
+redirectResponse.Should().BeRedirection("it's set to Moved"); // (HttpStatusCode = 3xx)
+
+var clientErrorResponse = new HttpResponseMessage(HttpStatusCode.BadRequest);
+clientErrorResponse.Should().HaveClientError("it's set to BadRequest"); // (HttpStatusCode = 4xx)
+clientErrorResponse.Should().HaveError("it's set to BadRequest"); // (HttpStatusCode = 4xx or 5xx)
+
+var serverErrorResponse = new HttpResponseMessage(HttpStatusCode.InternalServerError);
+serverErrorResponse.Should().HaveServerError("it's set to InternalServerError"); // (HttpStatusCode = 5xx)
+serverErrorResponse.Should().HaveError("it's set to InternalServerError"); // (HttpStatusCode = 4xx or 5xx)
+
+var anotherResponse = new HttpResponseMessage(HttpStatusCode.Moved);
+anotherResponse.Should().HaveStatusCode(HttpStatusCode.Moved);
+anotherResponse.Should().NotHaveStatusCode(HttpStatusCode.OK);
+```
diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md
index eb5b94e687..094c140bc6 100644
--- a/docs/_pages/releases.md
+++ b/docs/_pages/releases.md
@@ -11,6 +11,7 @@ sidebar:
### What's New
* Adding `ThatAreStatic()` and `ThatAreNotStatic()` for filtering in method assertions - [#1740](https://github.com/fluentassertions/fluentassertions/pull/1740)
+* Adding new assertions for the `HttpStatusCode` of an `HttpResponseMessage` - [#1737](https://github.com/fluentassertions/fluentassertions/pull/1737)
### Fixes
* Querying methods on classes, e.g. `typeof(MyController).Methods()`, now also includes static methods - [#1740](https://github.com/fluentassertions/fluentassertions/pull/1740)