Skip to content

Commit 762ce50

Browse files
committed
Move non-generic NotThrow[After] to ActionAssertions
Similar to what we did in fluentassertions#2359
1 parent 39dfa49 commit 762ce50

File tree

7 files changed

+104
-103
lines changed

7 files changed

+104
-103
lines changed

Src/FluentAssertions/Specialized/ActionAssertions.cs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Diagnostics;
33
using FluentAssertions.Common;
4+
using FluentAssertions.Execution;
45

56
namespace FluentAssertions.Specialized;
67

@@ -20,6 +21,97 @@ public ActionAssertions(Action subject, IExtractExceptions extractor, IClock clo
2021
{
2122
}
2223

24+
/// <summary>
25+
/// Asserts that the current <see cref="Delegate" /> does not throw any exception.
26+
/// </summary>
27+
/// <param name="because">
28+
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
29+
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
30+
/// </param>
31+
/// <param name="becauseArgs">
32+
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
33+
/// </param>
34+
public AndConstraint<ActionAssertions> NotThrow(string because = "", params object[] becauseArgs)
35+
{
36+
bool success = Execute.Assertion
37+
.ForCondition(Subject is not null)
38+
.BecauseOf(because, becauseArgs)
39+
.FailWith("Expected {context} not to throw{reason}, but found <null>.");
40+
41+
if (success)
42+
{
43+
FailIfSubjectIsAsyncVoid();
44+
Exception exception = InvokeSubjectWithInterception();
45+
return NotThrowInternal(exception, because, becauseArgs);
46+
}
47+
48+
return new AndConstraint<ActionAssertions>(this);
49+
}
50+
51+
/// <summary>
52+
/// Asserts that the current <see cref="Delegate"/> stops throwing any exception
53+
/// after a specified amount of time.
54+
/// </summary>
55+
/// <remarks>
56+
/// The delegate is invoked. If it raises an exception,
57+
/// the invocation is repeated until it either stops raising any exceptions
58+
/// or the specified wait time is exceeded.
59+
/// </remarks>
60+
/// <param name="waitTime">
61+
/// The time after which the delegate should have stopped throwing any exception.
62+
/// </param>
63+
/// <param name="pollInterval">
64+
/// The time between subsequent invocations of the delegate.
65+
/// </param>
66+
/// <param name="because">
67+
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
68+
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
69+
/// </param>
70+
/// <param name="becauseArgs">
71+
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
72+
/// </param>
73+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="waitTime"/> or <paramref name="pollInterval"/> are negative.</exception>
74+
public AndConstraint<ActionAssertions> NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval, string because = "",
75+
params object[] becauseArgs)
76+
{
77+
Guard.ThrowIfArgumentIsNegative(waitTime);
78+
Guard.ThrowIfArgumentIsNegative(pollInterval);
79+
80+
bool success = Execute.Assertion
81+
.ForCondition(Subject is not null)
82+
.BecauseOf(because, becauseArgs)
83+
.FailWith("Expected {context} not to throw after {0}{reason}, but found <null>.", waitTime);
84+
85+
if (success)
86+
{
87+
FailIfSubjectIsAsyncVoid();
88+
89+
TimeSpan? invocationEndTime = null;
90+
Exception exception = null;
91+
ITimer timer = Clock.StartTimer();
92+
93+
while (invocationEndTime is null || invocationEndTime < waitTime)
94+
{
95+
exception = InvokeSubjectWithInterception();
96+
97+
if (exception is null)
98+
{
99+
break;
100+
}
101+
102+
Clock.Delay(pollInterval);
103+
invocationEndTime = timer.Elapsed;
104+
}
105+
106+
Execute.Assertion
107+
.BecauseOf(because, becauseArgs)
108+
.ForCondition(exception is null)
109+
.FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception);
110+
}
111+
112+
return new AndConstraint<ActionAssertions>(this);
113+
}
114+
23115
protected override void InvokeSubject()
24116
{
25117
Subject();

Src/FluentAssertions/Specialized/DelegateAssertions.cs

Lines changed: 2 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -81,33 +81,6 @@ public AndConstraint<TAssertions> NotThrow<TException>(string because = "", para
8181
return new AndConstraint<TAssertions>((TAssertions)this);
8282
}
8383

84-
/// <summary>
85-
/// Asserts that the current <see cref="Delegate" /> does not throw any exception.
86-
/// </summary>
87-
/// <param name="because">
88-
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
89-
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
90-
/// </param>
91-
/// <param name="becauseArgs">
92-
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
93-
/// </param>
94-
public AndConstraint<TAssertions> NotThrow(string because = "", params object[] becauseArgs)
95-
{
96-
bool success = Execute.Assertion
97-
.ForCondition(Subject is not null)
98-
.BecauseOf(because, becauseArgs)
99-
.FailWith("Expected {context} not to throw{reason}, but found <null>.");
100-
101-
if (success)
102-
{
103-
FailIfSubjectIsAsyncVoid();
104-
Exception exception = InvokeSubjectWithInterception();
105-
return NotThrowInternal(exception, because, becauseArgs);
106-
}
107-
108-
return new AndConstraint<TAssertions>((TAssertions)this);
109-
}
110-
11184
/// <summary>
11285
/// Asserts that the current <see cref="Delegate"/> throws an exception of the exact type <typeparamref name="TException"/> (and not a derived exception type).
11386
/// </summary>
@@ -153,73 +126,9 @@ public ExceptionAssertions<TException> ThrowExactly<TException>(string because =
153126
return new ExceptionAssertions<TException>(Array.Empty<TException>());
154127
}
155128

156-
/// <summary>
157-
/// Asserts that the current <see cref="Delegate"/> stops throwing any exception
158-
/// after a specified amount of time.
159-
/// </summary>
160-
/// <remarks>
161-
/// The delegate is invoked. If it raises an exception,
162-
/// the invocation is repeated until it either stops raising any exceptions
163-
/// or the specified wait time is exceeded.
164-
/// </remarks>
165-
/// <param name="waitTime">
166-
/// The time after which the delegate should have stopped throwing any exception.
167-
/// </param>
168-
/// <param name="pollInterval">
169-
/// The time between subsequent invocations of the delegate.
170-
/// </param>
171-
/// <param name="because">
172-
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
173-
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
174-
/// </param>
175-
/// <param name="becauseArgs">
176-
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
177-
/// </param>
178-
/// <exception cref="ArgumentOutOfRangeException"><paramref name="waitTime"/> or <paramref name="pollInterval"/> are negative.</exception>
179-
public AndConstraint<TAssertions> NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval, string because = "",
180-
params object[] becauseArgs)
181-
{
182-
Guard.ThrowIfArgumentIsNegative(waitTime);
183-
Guard.ThrowIfArgumentIsNegative(pollInterval);
184-
185-
bool success = Execute.Assertion
186-
.ForCondition(Subject is not null)
187-
.BecauseOf(because, becauseArgs)
188-
.FailWith("Expected {context} not to throw after {0}{reason}, but found <null>.", waitTime);
189-
190-
if (success)
191-
{
192-
FailIfSubjectIsAsyncVoid();
193-
194-
TimeSpan? invocationEndTime = null;
195-
Exception exception = null;
196-
ITimer timer = Clock.StartTimer();
197-
198-
while (invocationEndTime is null || invocationEndTime < waitTime)
199-
{
200-
exception = InvokeSubjectWithInterception();
201-
202-
if (exception is null)
203-
{
204-
break;
205-
}
206-
207-
Clock.Delay(pollInterval);
208-
invocationEndTime = timer.Elapsed;
209-
}
210-
211-
Execute.Assertion
212-
.BecauseOf(because, becauseArgs)
213-
.ForCondition(exception is null)
214-
.FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception);
215-
}
216-
217-
return new AndConstraint<TAssertions>((TAssertions)this);
218-
}
219-
220129
protected abstract void InvokeSubject();
221130

222-
private Exception InvokeSubjectWithInterception()
131+
private protected Exception InvokeSubjectWithInterception()
223132
{
224133
Exception actualException = null;
225134

@@ -248,7 +157,7 @@ private Exception InvokeSubjectWithInterception()
248157
return actualException;
249158
}
250159

251-
private void FailIfSubjectIsAsyncVoid()
160+
private protected void FailIfSubjectIsAsyncVoid()
252161
{
253162
if (Subject.GetMethodInfo().IsDecoratedWithOrInherit<AsyncStateMachineAttribute>())
254163
{

Src/FluentAssertions/Specialized/FunctionAssertions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ protected override void InvokeSubject()
3838
/// <param name="becauseArgs">
3939
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
4040
/// </param>
41-
public new AndWhichConstraint<FunctionAssertions<T>, T> NotThrow(string because = "", params object[] becauseArgs)
41+
public AndWhichConstraint<FunctionAssertions<T>, T> NotThrow(string because = "", params object[] becauseArgs)
4242
{
4343
bool success = Execute.Assertion
4444
.ForCondition(Subject is not null)
@@ -78,7 +78,7 @@ protected override void InvokeSubject()
7878
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
7979
/// </param>
8080
/// <exception cref="ArgumentOutOfRangeException"><paramref name="waitTime"/> or <paramref name="pollInterval"/> are negative.</exception>
81-
public new AndWhichConstraint<FunctionAssertions<T>, T> NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval,
81+
public AndWhichConstraint<FunctionAssertions<T>, T> NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval,
8282
string because = "", params object[] becauseArgs)
8383
{
8484
bool success = Execute.Assertion

Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,6 +2088,8 @@ namespace FluentAssertions.Specialized
20882088
public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { }
20892089
protected override string Identifier { get; }
20902090
protected override void InvokeSubject() { }
2091+
public FluentAssertions.AndConstraint<FluentAssertions.Specialized.ActionAssertions> NotThrow(string because = "", params object[] becauseArgs) { }
2092+
public FluentAssertions.AndConstraint<FluentAssertions.Specialized.ActionAssertions> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { }
20912093
}
20922094
public class AsyncFunctionAssertions<TTask, TAssertions> : FluentAssertions.Specialized.DelegateAssertionsBase<System.Func<TTask>, TAssertions>
20932095
where TTask : System.Threading.Tasks.Task
@@ -2121,10 +2123,8 @@ namespace FluentAssertions.Specialized
21212123
{
21222124
protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { }
21232125
protected abstract void InvokeSubject();
2124-
public FluentAssertions.AndConstraint<TAssertions> NotThrow(string because = "", params object[] becauseArgs) { }
21252126
public FluentAssertions.AndConstraint<TAssertions> NotThrow<TException>(string because = "", params object[] becauseArgs)
21262127
where TException : System.Exception { }
2127-
public FluentAssertions.AndConstraint<TAssertions> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { }
21282128
public FluentAssertions.Specialized.ExceptionAssertions<TException> Throw<TException>(string because = "", params object[] becauseArgs)
21292129
where TException : System.Exception { }
21302130
public FluentAssertions.Specialized.ExceptionAssertions<TException> ThrowExactly<TException>(string because = "", params object[] becauseArgs)

Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,6 +2209,8 @@ namespace FluentAssertions.Specialized
22092209
public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { }
22102210
protected override string Identifier { get; }
22112211
protected override void InvokeSubject() { }
2212+
public FluentAssertions.AndConstraint<FluentAssertions.Specialized.ActionAssertions> NotThrow(string because = "", params object[] becauseArgs) { }
2213+
public FluentAssertions.AndConstraint<FluentAssertions.Specialized.ActionAssertions> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { }
22122214
}
22132215
public class AsyncFunctionAssertions<TTask, TAssertions> : FluentAssertions.Specialized.DelegateAssertionsBase<System.Func<TTask>, TAssertions>
22142216
where TTask : System.Threading.Tasks.Task
@@ -2242,10 +2244,8 @@ namespace FluentAssertions.Specialized
22422244
{
22432245
protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { }
22442246
protected abstract void InvokeSubject();
2245-
public FluentAssertions.AndConstraint<TAssertions> NotThrow(string because = "", params object[] becauseArgs) { }
22462247
public FluentAssertions.AndConstraint<TAssertions> NotThrow<TException>(string because = "", params object[] becauseArgs)
22472248
where TException : System.Exception { }
2248-
public FluentAssertions.AndConstraint<TAssertions> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { }
22492249
public FluentAssertions.Specialized.ExceptionAssertions<TException> Throw<TException>(string because = "", params object[] becauseArgs)
22502250
where TException : System.Exception { }
22512251
public FluentAssertions.Specialized.ExceptionAssertions<TException> ThrowExactly<TException>(string because = "", params object[] becauseArgs)

Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2039,6 +2039,8 @@ namespace FluentAssertions.Specialized
20392039
public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { }
20402040
protected override string Identifier { get; }
20412041
protected override void InvokeSubject() { }
2042+
public FluentAssertions.AndConstraint<FluentAssertions.Specialized.ActionAssertions> NotThrow(string because = "", params object[] becauseArgs) { }
2043+
public FluentAssertions.AndConstraint<FluentAssertions.Specialized.ActionAssertions> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { }
20422044
}
20432045
public class AsyncFunctionAssertions<TTask, TAssertions> : FluentAssertions.Specialized.DelegateAssertionsBase<System.Func<TTask>, TAssertions>
20442046
where TTask : System.Threading.Tasks.Task
@@ -2072,10 +2074,8 @@ namespace FluentAssertions.Specialized
20722074
{
20732075
protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { }
20742076
protected abstract void InvokeSubject();
2075-
public FluentAssertions.AndConstraint<TAssertions> NotThrow(string because = "", params object[] becauseArgs) { }
20762077
public FluentAssertions.AndConstraint<TAssertions> NotThrow<TException>(string because = "", params object[] becauseArgs)
20772078
where TException : System.Exception { }
2078-
public FluentAssertions.AndConstraint<TAssertions> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { }
20792079
public FluentAssertions.Specialized.ExceptionAssertions<TException> Throw<TException>(string because = "", params object[] becauseArgs)
20802080
where TException : System.Exception { }
20812081
public FluentAssertions.Specialized.ExceptionAssertions<TException> ThrowExactly<TException>(string because = "", params object[] becauseArgs)

Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,6 +2088,8 @@ namespace FluentAssertions.Specialized
20882088
public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { }
20892089
protected override string Identifier { get; }
20902090
protected override void InvokeSubject() { }
2091+
public FluentAssertions.AndConstraint<FluentAssertions.Specialized.ActionAssertions> NotThrow(string because = "", params object[] becauseArgs) { }
2092+
public FluentAssertions.AndConstraint<FluentAssertions.Specialized.ActionAssertions> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { }
20912093
}
20922094
public class AsyncFunctionAssertions<TTask, TAssertions> : FluentAssertions.Specialized.DelegateAssertionsBase<System.Func<TTask>, TAssertions>
20932095
where TTask : System.Threading.Tasks.Task
@@ -2121,10 +2123,8 @@ namespace FluentAssertions.Specialized
21212123
{
21222124
protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { }
21232125
protected abstract void InvokeSubject();
2124-
public FluentAssertions.AndConstraint<TAssertions> NotThrow(string because = "", params object[] becauseArgs) { }
21252126
public FluentAssertions.AndConstraint<TAssertions> NotThrow<TException>(string because = "", params object[] becauseArgs)
21262127
where TException : System.Exception { }
2127-
public FluentAssertions.AndConstraint<TAssertions> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { }
21282128
public FluentAssertions.Specialized.ExceptionAssertions<TException> Throw<TException>(string because = "", params object[] becauseArgs)
21292129
where TException : System.Exception { }
21302130
public FluentAssertions.Specialized.ExceptionAssertions<TException> ThrowExactly<TException>(string because = "", params object[] becauseArgs)

0 commit comments

Comments
 (0)