Skip to content

Commit

Permalink
Add Should.Throw() overloads for Func (shouldly#530)
Browse files Browse the repository at this point in the history
* Delete UpgradeLog.htm

Delete UpgradeLog.htm from some previous incarnation that made its way into Git. Update .gitignore so it doesn't happen again.

* Add Func-based Should.Throw methods

Inspired by xunit's Assert.Throws(), add Func-based Should.Throw() methods to remove the need to create dummy variables for actions and to remove the need to suppress warnings from tools like FxCop for unused objects.

* Update examples

Update the examples as instructed in the assert failure.
  • Loading branch information
martincostello authored and josephwoodward committed Oct 17, 2018
1 parent b3acc89 commit 148a37e
Show file tree
Hide file tree
Showing 15 changed files with 295 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ src/*.Tests/report.xml
.vscode
src/.idea
src/.idea.*
UpgradeLog*.htm
UpgradeLog*.XML
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
var homer = new Person() { Name = "Homer", Salary = 30000 };
var denominator = 0;
Should.NotThrow(() =>
{
var task = Task.Factory.StartNew(() => { var y = homer.Salary / denominator; });
return task;
});
string name = null;
Should.NotThrow(() => new Person(name));
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
`var task = Task.Factory.StartNew(() => { var y = homer.Salary / denominator; }); return task;`
`new Person(name)`
should not throw but threw
System.DivideByZeroException
System.ArgumentNullException
with message
"Attempted to divide by zero."
"Value cannot be null.
Parameter name: name"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var homer = new Person() { Name = "Homer", Salary = 30000 };
var denominator = 0;
Should.NotThrow(() =>
{
var task = Task.Factory.StartNew(() => { var y = homer.Salary / denominator; });
return task;
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
`var task = Task.Factory.StartNew(() => { var y = homer.Salary / denominator; }); return task;`
should not throw but threw
System.DivideByZeroException
with message
"Attempted to divide by zero."
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Should.Throw<ArgumentNullException>(() => new Person("Homer"));
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
`new Person("Homer")`
should throw
System.ArgumentNullException
but did not
13 changes: 12 additions & 1 deletion src/DocumentationExamples/ExampleClasses.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Simpsons
using System;

namespace Simpsons
{
public abstract class Pet
{
Expand All @@ -22,6 +24,15 @@ public class Dog : Pet

public class Person
{
public Person()
{
}

public Person(string name)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
}

public string Name { get; set; }
public int Salary { get; set; }

Expand Down
10 changes: 10 additions & 0 deletions src/DocumentationExamples/ShouldNotThrowExamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ public void ShouldNotThrowAction()

[Fact]
public void ShouldNotThrowFunc()
{
DocExampleWriter.Document(() =>
{
string name = null;
Should.NotThrow(() => new Person(name));
}, _testOutputHelper);
}

[Fact]
public void ShouldNotThrowFuncOfTask()
{
DocExampleWriter.Document(() =>
{
Expand Down
9 changes: 9 additions & 0 deletions src/DocumentationExamples/ShouldThrowExamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ public void ShouldThrowAction()
}, _testOutputHelper);
}

[Fact]
public void ShouldThrowFunc()
{
DocExampleWriter.Document(() =>
{
Should.Throw<ArgumentNullException>(() => new Person("Homer"));
}, _testOutputHelper);
}

[Fact]
public void ShouldThrowFuncOfTask()
{
Expand Down
81 changes: 81 additions & 0 deletions src/Shouldly.Tests/ShouldThrow/FuncOfObjectScenario.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using Shouldly.Tests.Strings;
using Xunit;

namespace Shouldly.Tests.ShouldThrow
{
public class FuncOfObjectScenario
{
[Fact]
public void FuncOfObjectScenarioShouldFail()
{
Func<object> action = () => 1;
Verify.ShouldFail(() =>
action.ShouldThrow<NotImplementedException>("Some additional context"),

errorWithSource:
@"`action()`
should throw
System.NotImplementedException
but did not
Additional Info:
Some additional context",

errorWithoutSource:
@"delegate
should throw
System.NotImplementedException
but did not
Additional Info:
Some additional context");
}

[Fact]
public void FuncOfObjectScenarioShouldFail_ExceptionTypePassedIn()
{
Func<object> action = () => 1;
Verify.ShouldFail(() =>
action.ShouldThrow("Some additional context", typeof(NotImplementedException)),

errorWithSource:
@"`action()`
should throw
System.NotImplementedException
but did not
Additional Info:
Some additional context",

errorWithoutSource:
@"delegate
should throw
System.NotImplementedException
but did not
Additional Info:
Some additional context");
}

[Fact]
public void ShouldPass()
{
var action = new Func<object>(() => { throw new NotImplementedException(); });

var ex = action.ShouldThrow<NotImplementedException>();
ex.ShouldBeOfType<NotImplementedException>();
ex.ShouldNotBe(null);
}

[Fact]
public void ShouldPass_ExceptionTypePassedIn()
{
var action = new Func<object>(() => { throw new NotImplementedException(); });

var ex = action.ShouldThrow(typeof(NotImplementedException));
ex.ShouldBeOfType<NotImplementedException>();
ex.ShouldNotBe(null);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using Shouldly.Tests.Strings;
using Xunit;

namespace Shouldly.Tests.ShouldThrow
{
public class FuncOfObjectThrowsDifferentExceptionScenario
{
[Fact]
public void NestedBlockLambdaWithoutAdditionalInformationsScenarioShouldFail()
{
Func<object> action = () => { throw new RankException(); };
Verify.ShouldFail(() =>
action.ShouldThrow<InvalidOperationException>("Some additional context"),

errorWithSource:
@"`action()`
should throw
System.InvalidOperationException
but threw
System.RankException
Additional Info:
Some additional context",

errorWithoutSource:
@"delegate
should throw
System.InvalidOperationException
but threw
System.RankException
Additional Info:
Some additional context");
}

[Fact]
public void NestedBlockLambdaWithoutAdditionalInformationsScenarioShouldFail_ExceptionTypePassedIn()
{
Func<object> action = () => { throw new RankException(); };
Verify.ShouldFail(() =>
action.ShouldThrow("Some additional context", typeof(InvalidOperationException)),

errorWithSource:
@"`action()`
should throw
System.InvalidOperationException
but threw
System.RankException
Additional Info:
Some additional context",

errorWithoutSource:
@"delegate
should throw
System.InvalidOperationException
but threw
System.RankException
Additional Info:
Some additional context");
}
}
}
65 changes: 65 additions & 0 deletions src/Shouldly/ShouldStaticClasses/ShouldThrow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,71 @@ internal static Exception ThrowInternal([InstantHandle] Action actual, [InstantH
throw new ShouldAssertException(new ShouldlyThrowMessage(exceptionType, customMessage, shouldlyMethod).ToString());
}

/*** Should.Throw(Func<T>) ***/
public static TException Throw<TException>([InstantHandle] Func<object> actual) where TException : Exception
{
return Throw<TException>(actual, () => null);
}
public static TException Throw<TException>([InstantHandle] Func<object> actual, string customMessage) where TException : Exception
{
return Throw<TException>(actual, () => customMessage);
}
public static TException Throw<TException>([InstantHandle] Func<object> actual, [InstantHandle] Func<string> customMessage) where TException : Exception
{
return ThrowInternal<TException>(actual, customMessage);
}
internal static TException ThrowInternal<TException>([InstantHandle] Func<object> actual, [InstantHandle] Func<string> customMessage,
[CallerMemberName] string shouldlyMethod = null) where TException : Exception
{
try
{
_ = actual();
}
catch (TException e)
{
return e;
}
catch (Exception e)
{
throw new ShouldAssertException(new ShouldlyThrowMessage(typeof(TException), e.GetType(), customMessage, shouldlyMethod).ToString(), e);
}

throw new ShouldAssertException(new ShouldlyThrowMessage(typeof(TException), customMessage, shouldlyMethod).ToString());
}

/*** Should.Throw(Func<T>) ***/
public static Exception Throw([InstantHandle] Func<object> actual, Type exceptionType)
{
return Throw(actual, () => null, exceptionType);
}
public static Exception Throw([InstantHandle] Func<object> actual, string customMessage, Type exceptionType)
{
return Throw(actual, () => customMessage, exceptionType);
}
public static Exception Throw([InstantHandle] Func<object> actual, [InstantHandle] Func<string> customMessage, Type exceptionType)
{
return ThrowInternal(actual, customMessage, exceptionType);
}
internal static Exception ThrowInternal([InstantHandle] Func<object> actual, [InstantHandle] Func<string> customMessage, Type exceptionType,
[CallerMemberName] string shouldlyMethod = null)
{
try
{
_ = actual();
}
catch (Exception e)
{
if (e.GetType() == exceptionType)
{
return e;
}

throw new ShouldAssertException(new ShouldlyThrowMessage(exceptionType, e.GetType(), customMessage, shouldlyMethod).ToString(), e);
}

throw new ShouldAssertException(new ShouldlyThrowMessage(exceptionType, customMessage, shouldlyMethod).ToString());
}

/*** Should.NotThrow(Action) ***/
public static void NotThrow([InstantHandle] Action action)
{
Expand Down
28 changes: 28 additions & 0 deletions src/Shouldly/ShouldlyExtensionMethods/ShouldThrowExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ public static TException ShouldThrow<TException>(this Action actual, [InstantHan
return Should.ThrowInternal<TException>(actual, customMessage);
}

/*** ShouldThrow(Func<T>) ***/
public static TException ShouldThrow<TException>(this Func<object> actual) where TException : Exception
{
return ShouldThrow<TException>(actual, () => null);
}
public static TException ShouldThrow<TException>(this Func<object> actual, string customMessage) where TException : Exception
{
return ShouldThrow<TException>(actual, () => customMessage);
}
public static TException ShouldThrow<TException>(this Func<object> actual, [InstantHandle] Func<string> customMessage) where TException : Exception
{
return Should.ThrowInternal<TException>(actual, customMessage);
}

/*** ShouldThrow(Action) ***/
public static Exception ShouldThrow(this Action actual, Type exceptionType)
{
Expand All @@ -36,6 +50,20 @@ public static Exception ShouldThrow(this Action actual, [InstantHandle] Func<str
return Should.ThrowInternal(actual, customMessage, exceptionType);
}

/*** ShouldThrow(Func<T>) ***/
public static Exception ShouldThrow(this Func<object> actual, Type exceptionType)
{
return ShouldThrow(actual, () => null, exceptionType);
}
public static Exception ShouldThrow(this Func<object> actual, string customMessage, Type exceptionType)
{
return ShouldThrow(actual, () => customMessage, exceptionType);
}
public static Exception ShouldThrow(this Func<object> actual, [InstantHandle] Func<string> customMessage, Type exceptionType)
{
return Should.ThrowInternal(actual, customMessage, exceptionType);
}

/*** ShouldNotThrow(Action) ***/
public static void ShouldNotThrow(this Action action)
{
Expand Down
Binary file removed src/UpgradeLog.htm
Binary file not shown.

0 comments on commit 148a37e

Please sign in to comment.