-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(vnext): add Testify.Assertions project
Add the Testify.Assertions project and the associated Testify.Assertions.Tests test project to the solution. Implement enough to prove the new design.
- Loading branch information
Showing
60 changed files
with
4,048 additions
and
70 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# EditorConfig is awesome: | ||
http://EditorConfig.org | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
# (Please don't specify an indent_size here; that has too many unintended consequences.) | ||
|
||
[*.cs,*.csx,*.vb,*.vbx] | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
max_line_length = 120 | ||
spaces_around_operators = true | ||
curly_bracket_next_line = true | ||
spaces_around_brackets = both | ||
indent_brace_style = Allman | ||
indent_size = 4 | ||
|
||
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] | ||
indent_size = 2 | ||
|
||
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] | ||
indent_size = 2 | ||
|
||
[*.json] | ||
indent_size = 2 | ||
|
||
[*.{cs, vb}] | ||
dotnet_style_qualification_for_field = false:suggestion | ||
dotnet_style_qualification_for_property = false:suggestion | ||
dotnet_style_qualification_for_method = false:suggestion | ||
dotnet_style_qualification_for_event = false:suggestion | ||
dotnet_style_predefined_type_for_locals_parameters_members = true:error | ||
dotnet_style_predefined_type_for_member_access = true:error | ||
dotnet_style_object_initializer = true:suggestion | ||
dotnet_style_collection_initializer = true:suggestion | ||
dotnet_style_coalesce_expression = true:suggestion | ||
dotnet_style_null_propagation = true:suggestion | ||
dotnet_style_explicit_tuple_names = true:suggestion | ||
|
||
[*.cs] | ||
csharp_style_var_for_built_in_types = true:suggestion | ||
csharp_style_var_when_type_is_apparent = true:suggestion | ||
csharp_style_var_elsewhere = true:suggestion | ||
csharp_style_expression_bodied_methods = true:suggestion | ||
csharp_style_expression_bodied_constructors = true:suggestion | ||
csharp_style_expression_bodied_operators = true:suggestion | ||
csharp_style_expression_bodied_properties = true:suggestion | ||
csharp_style_expression_bodied_indexers = true:suggestion | ||
csharp_style_expression_bodied_accessors = true:suggestion | ||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion | ||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion | ||
csharp_style_inlined_variable_declaration = true:suggestion | ||
csharp_style_throw_expression = true:suggestion | ||
csharp_style_conditional_delegate_call = true:suggestion |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,16 @@ | ||
<Project> | ||
<ItemGroup> | ||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.2" /> | ||
<PackageVersion Include="Microsoft.VisualStudio.Validation" Version="17.6.11" /> | ||
<PackageVersion Include="System.ValueTuple" Version="4.5.0" /> | ||
<PackageVersion Include="xunit" Version="2.5.1" /> | ||
<PackageVersion Include="xunit.extensibility.core" Version="2.5.1" /> | ||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.1" /> | ||
<PackageVersion Include="StyleCop.Analyzers" Version="1.1.0-beta006" /> | ||
<PackageVersion Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.0" /> | ||
<PackageVersion Include="Roslynator.Analyzers" Version="1.7.0" /> | ||
<PackageVersion Include="xunit.analyzers" Version="0.8.0" PrivateAssets="all" /> | ||
<PackageVersion Include="xunit.analyzers" Version="0.8.0" /> | ||
<PackageVersion Include="Moq" Version="4.7.99" /> | ||
<PackageVersion Include="coverlet.collector" Version="3.2.0" /> | ||
</ItemGroup> | ||
</Project> |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
namespace Testify; | ||
|
||
using System; | ||
|
||
/// <summary> | ||
/// Represents a record that holds actual value for an assertion and the | ||
/// expression for it. | ||
/// </summary> | ||
/// <typeparam name="T">The type of the actual value.</typeparam> | ||
public record ActualValue<T>(T? Value, string Expression) : IFormattable | ||
{ | ||
/// <inheritdoc/> | ||
public override string ToString() | ||
{ | ||
return ToString(null, null); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public string ToString(string? format, IFormatProvider? formatProvider) | ||
=> format switch | ||
{ | ||
"e" => FormatExpression(Expression), | ||
"v" => Format(Value), | ||
_ => Format(Value, Expression) | ||
}; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
namespace Testify; | ||
|
||
using System.Text.RegularExpressions; | ||
|
||
using Testify.Internal; | ||
|
||
/// <summary> | ||
/// Provides methods used to start an assertion or for use within an assertion | ||
/// implementation. | ||
/// </summary> | ||
public static partial class Assertion | ||
{ | ||
[GeneratedRegex("{because}", RegexOptions.Compiled)] | ||
private static partial Regex BecauseHole(); | ||
|
||
/// <summary> | ||
/// Begins a fluent assertion by providing the actual value being asserted | ||
/// on. | ||
/// </summary> | ||
/// <typeparam name="T">The type of the actual value.</typeparam> | ||
/// <param name="value">The actual value.</param> | ||
/// <param name="expression">The expression used when providing the actual | ||
/// value.</param> | ||
/// <returns>An <see cref="ActualValue{T}"/> instance representing the | ||
/// actual value being asserted on.</returns> | ||
public static ActualValue<T> Assert<T>( | ||
T? value, | ||
[Expression("value")] string? expression = null) | ||
=> new(value, Guard.Against.Null(expression)); | ||
|
||
/// <summary> | ||
/// Begins a fluent assertion by providing an <see cref="Action"/> as the | ||
/// value being asserted on. | ||
/// </summary> | ||
/// <param name="action">The <see cref="Action"/> for the actual value. | ||
/// </param> | ||
/// <param name="expression">The expression used when providing the action. | ||
/// </param> | ||
/// <returns>An <see cref="ActualValue{Action}"/> instance representing the | ||
/// actual value being asserted on.</returns> | ||
public static ActualValue<Action> Assert( | ||
Action action, | ||
[Expression("action")] string? expression = null) | ||
=> Assert<Action>(action, expression); | ||
|
||
/// <summary> | ||
/// Makes a "compound assertion" that fails with the specified message if any | ||
/// wrapped assertions fail. | ||
/// </summary> | ||
/// <param name="message">The assertion failure message to report if any | ||
/// wrapped assertions fail.</param> | ||
/// <param name="assertions">The <see cref="Action"/> to invoke which makes | ||
/// assertions to be wrapped in the "compound assertions".</param> | ||
/// <remarks> | ||
/// This is a very low level assertion generally used in the implementation | ||
/// of other "compound assertions" and not made directly within tests. The | ||
/// behavior of assertions are temporarily changed within the scope of the | ||
/// invoked <paramref name="assertions"/> to combine assertion failures, | ||
/// rather than to immediately throw them. This allows multiple assertions | ||
/// to be combined into a single assertion with a meaningful failure | ||
/// message. Note that only assertion methods within <b>Testify</b> will be | ||
/// combined this way, and any other exceptions thrown within the | ||
/// <paramref name="assertions"/> will cause an immediate test failure. | ||
/// </remarks> | ||
public static void Assert(string message, Action assertions) | ||
{ | ||
AssertionScope.Push(message); | ||
try | ||
{ | ||
assertions.Invoke(); | ||
} | ||
catch | ||
{ | ||
AssertionScope.Pop(false); | ||
throw; | ||
} | ||
|
||
AssertionScope.Pop(); | ||
} | ||
|
||
/// <summary> | ||
/// Asserts that the <paramref name="actual"/> value should satisfy all of | ||
/// the assertions made when invoking <paramref name="assertions"/>. | ||
/// </summary> | ||
/// <typeparam name="T">The type of the actual value being asserted on. | ||
/// </typeparam> | ||
/// <param name="actual">The actual value being asserted on.</param> | ||
/// <param name="assertions">An <see cref="Action{T}"/> that makes multiple | ||
/// assertions on the <paramref name="actual"/> value.</param> | ||
public static void ShouldSatisfy<T>(this ActualValue<T> actual, Action<ActualValue<T>> assertions) | ||
=> Assert("One or more assertions were not satisfied.", () => assertions.Invoke(actual)); | ||
|
||
/// <summary> | ||
/// Generates a test platform specific failure exception. If the test | ||
/// platform cannot be determined then raises the non-platform specific | ||
/// <see cref="AssertionException"/>. | ||
/// </summary> | ||
/// <param name="message">The assertion message, including the "{because}" | ||
/// hole used to format the user specified reason for the failure. | ||
/// </param> | ||
/// <param name="because">The user specified reason for the failure.</param> | ||
/// <exception cref="Exception"></exception> | ||
public static void Fail(string message, string? because = null) | ||
{ | ||
Guard.Against.NullOrWhiteSpace(message); | ||
|
||
if (!string.IsNullOrWhiteSpace(because)) | ||
{ | ||
because = because.Trim(); | ||
if (!because.StartsWith("because")) | ||
{ | ||
because = $"because {because}"; | ||
} | ||
|
||
message = BecauseHole().Replace(message, " " + because); | ||
} | ||
|
||
AssertionScope.Fail(message); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
namespace Testify; | ||
|
||
using System; | ||
using System.Runtime.Serialization; | ||
|
||
/// <summary> | ||
/// Represents assertion failures that occur during test execution. | ||
/// </summary> | ||
/// <remarks> | ||
/// This is the exception type thrown by assertions in the <b>Testify</b> | ||
/// framework when no unit test framework can be detected. | ||
/// </remarks> | ||
[Serializable] | ||
public class AssertionException : Exception | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="AssertionException"/> | ||
/// class. | ||
/// </summary> | ||
public AssertionException() { } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="AssertionException"/> class | ||
/// with a specified failure message. | ||
/// </summary> | ||
/// <param name="message">The message that describes the reason for an | ||
/// assertion failure.</param> | ||
public AssertionException(string message) : base(message) { } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="AssertionException"/> class | ||
/// with a specified failure message and a reference to the inner exception | ||
/// that is the cause of this exception. | ||
/// </summary> | ||
/// <param name="message">The message that describes the reason for an | ||
/// assertion failure.</param> | ||
/// <param name="inner">The exception that is the cause of the current | ||
/// exception, or a <see langword="null"/> reference if no inner | ||
/// exception is specified.</param> | ||
public AssertionException(string message, Exception inner) : base(message, inner) { } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="AssertionException"/> class | ||
/// with serialized data. | ||
/// </summary> | ||
/// <param name="info">The | ||
/// <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that | ||
/// holds the serialized object data about the exception being thrown. | ||
/// </param> | ||
/// <param name="context">The | ||
/// <see cref="T:System.Runtime.Serialization.StreamingContext"/> that | ||
/// contains contextual information about the source or destination. | ||
/// </param> | ||
/// <exception cref="ArgumentNullException"><paramref name="info"/> is | ||
/// <see langword="null"/>.</exception> | ||
/// <exception cref="SerializationException">The class name is | ||
/// <see langword="null"/> or <see cref="Exception.HResult"/> is zero | ||
/// (0).</exception> | ||
protected AssertionException( | ||
SerializationInfo info, | ||
StreamingContext context) : base(info, context) { } | ||
} |
Oops, something went wrong.