If Theow Up Once Will Do It Again? Glucose Test
Sometimes in that location are cases where we desire to throw a specific exception in our code. When yous are writing your tests, how do you account for this? In this commodity I will work through examples of how to unit exam C# code that's expected to throw exceptions.
Testing Series
I plan on making this article just one of many articles that are all to do with testing your C#/.NET code. This article is the 2d in the series. My previous article was an introduction to unit of measurement testing C#/.NET lawmaking with the assistance of the xUnit.Cyberspace testing library. Here is the C#/.NET testing series thus far.
- Unit Testing Your C# Code with xUnit
- Unit Testing Exceptions in C#
For this article, I volition outset with the code I wrote in my previous article. If you'd like to see that code, I've posted it on my Github, and y'all can see information technology here. If yous see something wrong or something that could be improved, feel free to submit a pull request! In that commodity, I wrote a SpeedConversionService whose sole purpose was to catechumen incoming speed units expressed as kilometers per hour into miles per 60 minutes. Using a examination driven development (TDD) Red-Green-Refactor arroyo with the assist of xUnit, I did non touch on the codebase without first writing a failing exam. For this article, I will proceed using a TDD arroyo.
The Code for this Article
If you would similar to see the total source, including all the code and the test project, I've posted the code specifically for testing exceptions on my GitHub. Every bit always, if you have a suggestion or feel y'all could make it ameliorate, feel gratuitous to submit a pull request!
Exam for Exceptions using xUnit's Assert.Throws<T>
xUnit kindly provides a nice way of capturing exceptions inside our tests with Affirm.Throws<T>
. All nosotros need to do is supply Assert.Throws<T>
with an exception blazon, and an Action that is supposed to throw an exception. Since we're following Red-Green-Refactor, nosotros're going to start with a failing examination. Nosotros're going to test the example where we call SpeedConversionService
's ConvertToMilesPerHour
method and pass it -1 equally the inputted kilometers per hr.
Since speed, in the math and physics world, is considered a scalar quantity with no representation of direction, a "negative" speed isn't possible. In our code, we need to add together a rule where we cannot convert negative values of kilometers per hr. We want to throw an exception, specifically an ArgumentOutOfRangeException
, if the ConvertToMilesPerHour
method is passed a negative input. Here'south how we'll do it with xUnit.
[Fact] public void ConvertToMilesPerHour_InputNegative1_ThrowsArgumentOutOfRangeException() { Assert.Throws<ArgumentOutOfRangeException>(() => speedConverter.ConvertToMilesPerHour(-i)); }
Start, we decorated the examination method with [Fact]
. [Fact]
, as I mentioned in my previous commodity on unit testing C#/.Internet code with xUnit, is used to tell the examination runner to really run the test. If the exam runner completes the test without throwing an exception or failing an Assert, the examination passes.
Next, nosotros provide the blazon argument, which needs to be a blazon of Exception
, the type of exception nosotros wait our lawmaking to throw, ArgumentOutOfRangeException
.
Finally, we laissez passer in an Action every bit an empty lambda expression that simply calls our class nether test SpeedConversionService
'south ConvertToMilesPerHour
method with -1
equally the input parameter.
If nosotros run our exam, it fails. Since nosotros're post-obit TDD, we'll easily beginning with a failing exam since we don't have whatever such code that throws an ArgumentOutOfRangeException
. Here's the output from my Visual Studio 2019 Test Explorer.
Now, we need to write the lawmaking to make our examination laissez passer.
public int ConvertToMilesPerHour(int kilometersPerHour) { if (kilometersPerHour == -1) { throw new ArgumentOutOfRangeException($"{nameof(kilometersPerHour)} must be positive."); } return (int)Math.Round(kilometersPerHour * 0.62137); }
All I've washed is added a new guard clause that checks if kilometersPerHour
is -1
. If it is, it volition throw the exception. Our code should now pass the examination because we throw the expected ArgumentOutOfRangeException
. I've besides used C#'south string interpolation and the nameof
operator to specify the exception bulletin. The nameof
operator will just enforce the name of kilometersPerHour
is consistent with what we place in the exception message via compilation.
Great! Our test is at present passing, merely nosotros nevertheless have a problem. What if we input -2? Let's write a test.
[Theory] [InlineData(-1)] [InlineData(-2)] public void ConvertToMilesPerHour_InputNegative_ThrowsArgumentOutOfRangeException(int input) { Assert.Throws(() => speedConverter.ConvertToMilesPerHour(input)); }
I've changed our test to use the [Theory]
and [InlineData]
attributes instead of the [Fact]
attribute. Equally I demonstrated in my previous article, this will allow usa to write less code to perform more tests. Now, let's run across what happens when we run all of the tests.
We need to modify the lawmaking to throw an ArgumentOutOfRangeException
for all negative input.
if (kilometersPerHour <= -1) { throw new ArgumentOutOfRangeException($"{nameof(kilometersPerHour)} must be positive."); }
Asserting Exception Messages
So far so proficient, our lawmaking now throws an ArgumentOutOfRangeException
when inputting a negative integer, and our tests comprehend that. But what would we practice if nosotros added more requirements to our code, and information technology could throw ArgumentOutOfRangeExceptions
for different reasons? For this, we tin actually ensure we've thrown the correct exception past inspecting the exception message of the return value of Assert.Throws<T>
. A neat feature of Assert.Throws<T>
is that it really returns the exception that was thrown within the passed Activeness.
Let's say nosotros desire our electric current ArgumentOutOfRangeException
's to throw with the exception bulletin: "The input kilometersPerHour
must be greater than or equal to zero." We'll need to alter our tests to account for this.
[Theory] [InlineData(-ane)] [InlineData(-two)] public void ConvertToMilesPerHour_InputNegative_ThrowsArgumentOutOfRangeException(int input) { var ex = Assert.Throws<ArgumentOutOfRangeException>(() => speedConverter.ConvertToMilesPerHour(input)); Affirm.Contains("must exist greater than or equal to zero.", ex.Message); }
I've changed the test method to store the event of Assert.Throws<T>
into a variable, ex
. Then I utilise Assert.Contains
to ensure my ex
, the ArgumentOutOfRangeException
thrown past my code, contains the cord "must exist greater than or equal to nothing." I could have used Assert.Equals
hither to ensure they exactly match, but I decided to use Affirm.Contains
in example I wanted to change the starting time part of the exception message in the name of easier maintenance.
Our tests are now going to fail since the exception doesn't match what nosotros're expecting in the test.
Now that we take our failing tests, let's write the code needed to make the tests pass. We'll demand to modify the exception message when we throw the ArgumentOutOfRangeException
in our code.
if (kilometersPerHour <= -one) { throw new ArgumentOutOfRangeException($"{nameof(kilometersPerHour)} must be greater than or equal to naught."); }
And once again, our tests all pass!
Testing Exceptions Regardless of Test Framework
Okay, so testing for the exceptions our code throws is great and all, but what if we don't use xUnit in our test project? Nosotros can examination our exceptions using whatever testing framework such as MSTest, a still-popular testing framework developed past Microsoft, or NUnit, another wildly popular testing framework for .NET applications.
The way to do this is using good ole' fashioned C# try/take hold of blocks. Similar xUnit's way of testing exceptions with Assert.Throws<T>
, it's unproblematic to test exceptions, but nosotros must exist mindful of the flow of the try/catch logic within our test methods.
If we wanted to ensure that our code simply throws the ArgumentOutOfRangeException
given a negative input, we'd write our exam like this.
[Theory] [InlineData(-1)] [InlineData(-2)] public void FrameworkAgnostic_ConvertToMilesPerHour_InputNegative_ThrowsArgumentOutOfRangeException(int input) { endeavour { speedConverter.ConvertToMilesPerHour(input); } take hold of (ArgumentOutOfRangeException) { render; } throw new InvalidOperationException($"Expected {nameof(ArgumentOutOfRangeException)} but no exception was thrown."); }
While I used the [Theory]
and [InlineData]
attributes which are specific to xUnit, you lot could use attributes from whichever season of testing framework you choose such as [Test]
or [TestMethod]
. I've wrapped my call to ConvertToMilesPerHour
inside a endeavour cake to give our examination method a chance to catch the exception within the catch block. If nosotros arrive at the catch block, which indicates our lawmaking threw the ArgumentOutOfRangeException
as expected, we can simply return, as our work is done hither, and our test volition pass. Otherwise, if our code continues executing afterwards the telephone call to ConvertToMilesPerHour
, we know our lawmaking didn't throw the ArgumentOutOfRangeException
as expected, thus I throw an InvalidOperationException
here with an appropriate bulletin to bespeak that something went incorrect, and the exam volition neglect.
Similarly, if nosotros wanted to check for a specific message after the exception is thrown, we demand to modify the grab block to audit the exception message.
[Theory] [InlineData(-1)] [InlineData(-2)] public void FrameworkAgnostic_ConvertToMilesPerHour_InputNegative_ThrowsArgumentOutOfRangeException(int input) { try { speedConverter.ConvertToMilesPerHour(input); } grab (ArgumentOutOfRangeException ex) { Affirm.Contains("must be greater than or equal to zero.", ex.Message); return; } throw new InvalidOperationException($"Expected {nameof(ArgumentOutOfRangeException)} only no exception was thrown."); }
Our test must now satisfy an additional condition in that the exception message, ex.Bulletin
, must comprise the string, "must be greater than or equal to zero." In one case once again I've used Assert.Contains
, only any assertion appropriate for the situation in other frameworks will piece of work just as well. After we do the Assert.Contains
, nosotros need to return from the method, otherwise the flow of execution will reach the bottom and throw the InvalidOperationException
.
Wrapping Up
And there you have it! In this article nosotros've gone over how to unit exam our code that volition throw exceptions in a deterministic way. We can either use xUnit'south Assert.Throws<T>
, which makes life while testing for exceptions pretty easy, or nosotros could practise the old fashioned test agnostic mode of using try/take hold of blocks. While xUnit does requite us some nice syntactic sugar for testing exceptions, we tin make the try/catch approach work equally well.
I've posted the lawmaking and testing projection on my GitHub if you'd like to check it out.
I hope y'all observe this commodity useful, and as e'er, happy coding!
Unit Exam Your C# Lawmaking Hands with xUnit and TDD prototype
Jan 12, 2020 by Republic of chad
Unit Examination Your C# Lawmaking Easily with xUnit and TDD
Unit of measurement testing your C# code has truly never been easier. Today I will innovate how to go up and running and unit of measurement testing your C# code in only a matter of seconds with one of the latest testing technologies, xUnit, using a Test Driven Evolution (TDD) arroyo.
Read Article
Multi-Tenanted Entity Framework Core Migration Deployment paradigm
April xi, 2021 by Chad
Multi-Tenanted Entity Framework Cadre Migration Deployment
There'due south many means to deploy pending Entity Framework Core (EF Core) migrations, especially for multi-tenanted scenarios. In this postal service, I'll demonstrate a strategy to efficiently utilize awaiting EF Core 5 migrations using a .NET 5 console app.
Read Article
Multi-Tenanted Entity Framework vi Migration Deployment image
April 10, 2021 by Chad
Multi-Tenanted Entity Framework 6 Migration Deployment
There's many ways to deploy pending Entity Framework 6 (EF6) migrations, especially for multi-tenanted product scenarios. In this post, I'll demonstrate a strategy to efficiently apply awaiting migrations using a .NET 5 console app.
Read Article
Source: https://chadgolden.com/blog/unit-testing-exceptions-in-c-sharp
0 Response to "If Theow Up Once Will Do It Again? Glucose Test"
Enviar um comentário