Skip to content

Commit 44adef3

Browse files
committed
wip
1 parent be515ad commit 44adef3

14 files changed

Lines changed: 380 additions & 102 deletions

src/MrKWatkins.Assertions.Tests/AsyncActionAssertionsTests.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ public sealed class AsyncActionAssertionsTests
55
[Test]
66
public async Task ThrowAsync()
77
{
8-
Func<Task> doesNotThrow = () => Task.CompletedTask;
9-
Func<Task> doesNotThrowAsync = async () => await Task.Yield();
8+
var doesNotThrow = () => Task.CompletedTask;
9+
var doesNotThrowAsync = async () => await Task.Yield();
1010

1111
var exception = new InvalidOperationException("Test");
1212
Func<Task> throws = () => throw exception;
13-
Func<Task> throwsAsync = async () => { await Task.Yield(); throw exception; };
13+
var throwsAsync = async () => { await Task.Yield(); throw exception; };
1414

1515
Func<Task> throwsWrongException = () => throw new NotSupportedException("Wrong");
16-
Func<Task> throwsWrongExceptionAsync = async () => { await Task.Yield(); throw new NotSupportedException("Wrong"); };
16+
var throwsWrongExceptionAsync = async () => { await Task.Yield(); throw new NotSupportedException("Wrong"); };
1717

1818
await Assert.That(() => throws.Should().ThrowAsync<InvalidOperationException>()).ThrowsNothing();
1919
await Assert.That(() => throwsAsync.Should().ThrowAsync<InvalidOperationException>()).ThrowsNothing();
@@ -34,7 +34,7 @@ public async Task ThrowAsync_Chain()
3434
{
3535
var exception = new InvalidOperationException("Test");
3636
Func<Task> throws = () => throw exception;
37-
Func<Task> throwsAsync = async () => { await Task.Yield(); throw exception; };
37+
var throwsAsync = async () => { await Task.Yield(); throw exception; };
3838

3939
var chain = await throws.Should().ThrowAsync<InvalidOperationException>().ConfigureAwait(false);
4040
await Assert.That(chain.Exception).IsSameReferenceAs(exception);
@@ -48,15 +48,15 @@ public async Task ThrowAsync_Chain()
4848
[Test]
4949
public async Task ThrowAsync_String()
5050
{
51-
Func<Task> doesNotThrow = () => Task.CompletedTask;
52-
Func<Task> doesNotThrowAsync = async () => await Task.Yield();
51+
var doesNotThrow = () => Task.CompletedTask;
52+
var doesNotThrowAsync = async () => await Task.Yield();
5353

5454
var exception = new InvalidOperationException("Test");
5555
Func<Task> throws = () => throw exception;
56-
Func<Task> throwsAsync = async () => { await Task.Yield(); throw exception; };
56+
var throwsAsync = async () => { await Task.Yield(); throw exception; };
5757

5858
Func<Task> throwsWrongException = () => throw new NotSupportedException("Wrong");
59-
Func<Task> throwsWrongExceptionAsync = async () => { await Task.Yield(); throw new NotSupportedException("Wrong"); };
59+
var throwsWrongExceptionAsync = async () => { await Task.Yield(); throw new NotSupportedException("Wrong"); };
6060

6161
await Assert.That(() => throws.Should().ThrowAsync<InvalidOperationException>("Test")).ThrowsNothing();
6262
await Assert.That(() => throwsAsync.Should().ThrowAsync<InvalidOperationException>("Test")).ThrowsNothing();
@@ -82,7 +82,7 @@ public async Task ThrowAsync_String_Chain()
8282
{
8383
var exception = new InvalidOperationException("Test");
8484
Func<Task> throws = () => throw exception;
85-
Func<Task> throwsAsync = async () => { await Task.Yield(); throw exception; };
85+
var throwsAsync = async () => { await Task.Yield(); throw exception; };
8686

8787
var chain = await throws.Should().ThrowAsync<InvalidOperationException>("Test").ConfigureAwait(false);
8888
await Assert.That(chain.Exception).IsSameReferenceAs(exception);
@@ -96,12 +96,12 @@ public async Task ThrowAsync_String_Chain()
9696
[Test]
9797
public async Task NotThrowAsync()
9898
{
99-
Func<Task> doesNotThrow = () => Task.CompletedTask;
100-
Func<Task> doesNotThrowAsync = async () => await Task.Yield();
99+
var doesNotThrow = () => Task.CompletedTask;
100+
var doesNotThrowAsync = async () => await Task.Yield();
101101

102102
var exception = new InvalidOperationException("Test");
103103
Func<Task> throws = () => throw exception;
104-
Func<Task> throwsAsync = async () => { await Task.Yield(); throw exception; };
104+
var throwsAsync = async () => { await Task.Yield(); throw exception; };
105105

106106
await Assert.That(() => doesNotThrow.Should().NotThrowAsync()).ThrowsNothing();
107107
await Assert.That(() => doesNotThrowAsync.Should().NotThrowAsync()).ThrowsNothing();

src/MrKWatkins.Assertions.Tests/EnumerableAssertionsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ await Assert.That(() => nullEnumerable.Should().SequenceEqual(1)).Throws<Asserti
7777

7878
[Test]
7979
[Arguments(new int[0], true, new[] { 1, 2, 3 }, true, "Value [] should sequence equal [1, 2, 3] but it has 0 elements rather than the expected 3.")]
80-
[Arguments(new int[0], true, new[] { 1, 2, 3 }, false, "Value [] should sequence equal [1, 2, ...] but it has less elements (0) than expected.")]
80+
[Arguments(new int[0], true, new[] { 1, 2, 3 }, false, "Value [] should sequence equal [1, 2, ...] but it has fewer elements (0) than expected.")]
8181
[Arguments(new int[0], false, new[] { 1, 2, 3 }, true, "Value [] should sequence equal [1, 2, 3] but it has 0 elements rather than the expected 3.")]
82-
[Arguments(new int[0], false, new[] { 1, 2, 3 }, false, "Value [] should sequence equal [1, 2, ...] but it has less elements (0) than expected.")]
82+
[Arguments(new int[0], false, new[] { 1, 2, 3 }, false, "Value [] should sequence equal [1, 2, ...] but it has fewer elements (0) than expected.")]
8383
[Arguments(new[] { 1 }, true, new int[0], true, "Value [1] should sequence equal [] but it has 1 element rather than the expected 0.")]
8484
[Arguments(new[] { 1, 2, 3 }, true, new int[0], false, "Value [1, 2, 3] should sequence equal [] but it has 3 elements rather than the expected 0.")]
8585
[Arguments(new[] { 1, 2, 3 }, false, new int[0], true, "Value [1, 2, ...] should sequence equal [] but it has more elements than the expected 0.")]

src/MrKWatkins.Assertions.Tests/ReadOnlySpanAssertionsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ await Assert.That(() =>
172172
ReadOnlySpan<byte> value = [];
173173
var differentLength = CreateEnumerable(1);
174174
value.Should().SequenceEqual(differentLength);
175-
}).Throws<AssertionException>().WithMessage("Value [] should sequence equal [1, ...] but it has less elements (0) than expected.");
175+
}).Throws<AssertionException>().WithMessage("Value [] should sequence equal [1, ...] but it has fewer elements (0) than expected.");
176176

177177
await Assert.That(() =>
178178
{
@@ -186,7 +186,7 @@ await Assert.That(() =>
186186
ReadOnlySpan<byte> value = [1, 2, 3];
187187
var differentLength = CreateEnumerable(1, 2, 3, 4);
188188
value.Should().SequenceEqual(differentLength);
189-
}).Throws<AssertionException>().WithMessage("Value [1, 2, 3] should sequence equal [1, 2, ...] but it has less elements (3) than expected.");
189+
}).Throws<AssertionException>().WithMessage("Value [1, 2, 3] should sequence equal [1, 2, ...] but it has fewer elements (3) than expected.");
190190

191191
await Assert.That(() =>
192192
{

src/MrKWatkins.Assertions/AsyncActionAssertions.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,78 @@ public async Task<ActionAssertionsChain<TException>> ThrowAsync<TException>(stri
5252
return chain;
5353
}
5454

55+
/// <summary>
56+
/// Asserts that the async action throws an exception of the specified type with the specified message and inner exception.
57+
/// </summary>
58+
/// <typeparam name="TException">The expected exception type.</typeparam>
59+
/// <param name="expectedMessage">The expected exception message.</param>
60+
/// <param name="expectedInnerException">The expected inner exception, or <see langword="null" /> to assert no inner exception.</param>
61+
/// <returns>A <see cref="Task{TResult}"/> that resolves to an <see cref="ActionAssertionsChain{TException}" /> containing the thrown exception.</returns>
62+
public async Task<ActionAssertionsChain<TException>> ThrowAsync<TException>(string expectedMessage, Exception? expectedInnerException)
63+
where TException : Exception
64+
{
65+
var chain = await ThrowAsync<TException>().ConfigureAwait(false);
66+
67+
chain.That.Should().HaveMessage(expectedMessage);
68+
if (expectedInnerException != null)
69+
{
70+
chain.That.Should().HaveInnerException(expectedInnerException);
71+
}
72+
else
73+
{
74+
chain.That.Should().NotHaveInnerException();
75+
}
76+
77+
return chain;
78+
}
79+
80+
/// <summary>
81+
/// Asserts that the async action throws the exact specified exception instance.
82+
/// </summary>
83+
/// <typeparam name="TException">The expected exception type.</typeparam>
84+
/// <param name="expected">The expected exception instance.</param>
85+
/// <returns>A <see cref="Task{TResult}"/> that resolves to an <see cref="ActionAssertionsChain{TException}" /> containing the thrown exception.</returns>
86+
public async Task<ActionAssertionsChain<TException>> ThrowAsync<TException>(TException expected)
87+
where TException : Exception
88+
{
89+
var chain = await ThrowAsync<TException>().ConfigureAwait(false);
90+
91+
chain.Exception.Should().BeTheSameInstanceAs(expected);
92+
93+
return chain;
94+
}
95+
96+
/// <summary>
97+
/// Asserts that the async action throws an <see cref="ArgumentException" /> with the specified message and parameter name.
98+
/// </summary>
99+
/// <param name="expectedMessage">The expected exception message.</param>
100+
/// <param name="expectedParamName">The expected parameter name.</param>
101+
/// <returns>A <see cref="Task{TResult}"/> that resolves to an <see cref="ActionAssertionsChain{TException}" /> containing the thrown exception.</returns>
102+
public async Task<ActionAssertionsChain<ArgumentException>> ThrowArgumentExceptionAsync(string expectedMessage, string expectedParamName)
103+
{
104+
var chain = await ThrowAsync<ArgumentException>().ConfigureAwait(false);
105+
106+
chain.Exception.Should().HaveMessageStartingWith(expectedMessage).And.HaveParamName(expectedParamName);
107+
108+
return chain;
109+
}
110+
111+
/// <summary>
112+
/// Asserts that the async action throws an <see cref="ArgumentOutOfRangeException" /> with the specified message, parameter name and actual value.
113+
/// </summary>
114+
/// <param name="expectedMessage">The expected exception message.</param>
115+
/// <param name="expectedParamName">The expected parameter name.</param>
116+
/// <param name="expectedActualValue">The expected actual value.</param>
117+
/// <returns>A <see cref="Task{TResult}"/> that resolves to an <see cref="ActionAssertionsChain{TException}" /> containing the thrown exception.</returns>
118+
public async Task<ActionAssertionsChain<ArgumentOutOfRangeException>> ThrowArgumentOutOfRangeExceptionAsync(string expectedMessage, string expectedParamName, object expectedActualValue)
119+
{
120+
var chain = await ThrowAsync<ArgumentOutOfRangeException>().ConfigureAwait(false);
121+
122+
chain.Exception.Should().HaveMessageStartingWith(expectedMessage).And.HaveParamName(expectedParamName).And.HaveActualValue(expectedActualValue);
123+
124+
return chain;
125+
}
126+
55127
/// <summary>
56128
/// Asserts that the async action does not throw any exception.
57129
/// </summary>

src/MrKWatkins.Assertions/EnumerableAssertions.cs

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,31 @@ public EnumerableAssertionsChain<TEnumerable, T> OnlyContain([InstantHandle] Fun
3232
return new EnumerableAssertionsChain<TEnumerable, T>(this);
3333
}
3434

35+
/// <summary>
36+
/// Asserts that the enumerable contains exactly one item.
37+
/// </summary>
38+
/// <returns>An <see cref="ObjectAssertionsChain{T}" /> for the single item.</returns>
39+
public ObjectAssertionsChain<T> ContainSingle()
40+
{
41+
NotBeNull();
42+
43+
using var enumerator = Value.GetEnumerator();
44+
45+
if (!enumerator.MoveNext())
46+
{
47+
throw Verify.CreateException("Value should contain a single item but was empty.");
48+
}
49+
50+
var item = enumerator.Current;
51+
52+
if (enumerator.MoveNext())
53+
{
54+
throw Verify.CreateException("Value should contain a single item but contains more than one item.");
55+
}
56+
57+
return new ObjectAssertionsChain<T>(new ObjectAssertions<T>(item));
58+
}
59+
3560
/// <summary>
3661
/// Asserts that the enumerable contains exactly one item that satisfies the specified predicate.
3762
/// </summary>
@@ -128,7 +153,7 @@ private EnumerableAssertionsChain<TEnumerable, T> SequenceEqualCore(IEnumerable<
128153
if (expectedEnumerator.MoveNext())
129154
{
130155
throw Verify.CreateException(
131-
$"Value {Format.Enumerable(Value)} should sequence equal {Format.Enumerable(expected)} but it has less elements ({actualList.Count}) than expected.");
156+
$"Value {Format.Enumerable(Value)} should sequence equal {Format.Enumerable(expected)} but it has fewer elements ({actualList.Count}) than expected.");
132157
}
133158

134159
return new EnumerableAssertionsChain<TEnumerable, T>(this);
@@ -222,4 +247,78 @@ public EnumerableAssertionsChain<TEnumerable, T> Contain(T expected)
222247

223248
return new EnumerableAssertionsChain<TEnumerable, T>(this);
224249
}
250+
251+
/// <summary>
252+
/// Asserts that the enumerable contains the specified item using the specified equality comparer.
253+
/// </summary>
254+
/// <param name="expected">The item that should be present in the enumerable.</param>
255+
/// <param name="comparer">The equality comparer to use for element comparison.</param>
256+
/// <returns>An <see cref="EnumerableAssertionsChain{TEnumerable, T}" /> for chaining further assertions.</returns>
257+
public EnumerableAssertionsChain<TEnumerable, T> Contain(T expected, IEqualityComparer<T> comparer)
258+
{
259+
NotBeNull();
260+
261+
Verify.That(Value.Contains(expected, comparer), $"Value should contain {expected} but did not.");
262+
263+
return new EnumerableAssertionsChain<TEnumerable, T>(this);
264+
}
265+
266+
/// <summary>
267+
/// Asserts that the enumerable contains the specified item using the specified predicate.
268+
/// </summary>
269+
/// <param name="expected">The item that should be present in the enumerable.</param>
270+
/// <param name="predicate">The predicate to use for element comparison, taking the actual item and expected item as arguments.</param>
271+
/// <returns>An <see cref="EnumerableAssertionsChain{TEnumerable, T}" /> for chaining further assertions.</returns>
272+
public EnumerableAssertionsChain<TEnumerable, T> Contain(T expected, Func<T?, T?, bool> predicate)
273+
{
274+
NotBeNull();
275+
276+
Verify.That(Value.Any(item => predicate(item, expected)), $"Value should contain {expected} but did not.");
277+
278+
return new EnumerableAssertionsChain<TEnumerable, T>(this);
279+
}
280+
281+
/// <summary>
282+
/// Asserts that the enumerable does not contain the specified item.
283+
/// </summary>
284+
/// <param name="expected">The item that should not be present in the enumerable.</param>
285+
/// <returns>An <see cref="EnumerableAssertionsChain{TEnumerable, T}" /> for chaining further assertions.</returns>
286+
public EnumerableAssertionsChain<TEnumerable, T> NotContain(T expected)
287+
{
288+
NotBeNull();
289+
290+
Verify.That(!Value.Contains(expected), $"Value should not contain {expected}.");
291+
292+
return new EnumerableAssertionsChain<TEnumerable, T>(this);
293+
}
294+
295+
/// <summary>
296+
/// Asserts that the enumerable does not contain the specified item using the specified equality comparer.
297+
/// </summary>
298+
/// <param name="expected">The item that should not be present in the enumerable.</param>
299+
/// <param name="comparer">The equality comparer to use for element comparison.</param>
300+
/// <returns>An <see cref="EnumerableAssertionsChain{TEnumerable, T}" /> for chaining further assertions.</returns>
301+
public EnumerableAssertionsChain<TEnumerable, T> NotContain(T expected, IEqualityComparer<T> comparer)
302+
{
303+
NotBeNull();
304+
305+
Verify.That(!Value.Contains(expected, comparer), $"Value should not contain {expected}.");
306+
307+
return new EnumerableAssertionsChain<TEnumerable, T>(this);
308+
}
309+
310+
/// <summary>
311+
/// Asserts that the enumerable does not contain the specified item using the specified predicate.
312+
/// </summary>
313+
/// <param name="expected">The item that should not be present in the enumerable.</param>
314+
/// <param name="predicate">The predicate to use for element comparison, taking the actual item and expected item as arguments.</param>
315+
/// <returns>An <see cref="EnumerableAssertionsChain{TEnumerable, T}" /> for chaining further assertions.</returns>
316+
public EnumerableAssertionsChain<TEnumerable, T> NotContain(T expected, Func<T?, T?, bool> predicate)
317+
{
318+
NotBeNull();
319+
320+
Verify.That(!Value.Any(item => predicate(item, expected)), $"Value should not contain {expected}.");
321+
322+
return new EnumerableAssertionsChain<TEnumerable, T>(this);
323+
}
225324
}

0 commit comments

Comments
 (0)