Question
How to Assert Exceptions in MSTest (C#)
Question
How can I use Assert or other testing features in MSTest (Microsoft.VisualStudio.TestTools.UnitTesting) to verify that a specific exception is thrown?
For example, I want to test code that should fail under certain conditions and confirm that the expected exception is raised during the test.
Short Answer
By the end of this page, you will understand how to test exceptions in MSTest using modern and older approaches. You will learn when to use Assert.ThrowsException<T>(), how it works, why it is usually preferred over attribute-based exception testing, and how to write clear, reliable exception tests in C#.
Concept
In unit testing, verifying exceptions is how you confirm that code fails correctly.
Sometimes a method is supposed to throw an exception when it receives invalid input, missing data, or an impossible state. A good test should check not only successful behavior, but also failure behavior.
In MSTest, the most common way to verify this is:
Assert.ThrowsException<ArgumentException>(() => SomeMethod());
This means:
- Run the code inside the lambda
- Expect an exception of type
ArgumentException - Fail the test if no exception is thrown
- Fail the test if a different exception type is thrown
This matters because exception tests help you:
- validate input rules
- protect against silent failures
- document intended behavior
- catch regressions when code changes
For example, if a method should reject a negative price, your test should prove that it throws an exception instead of returning bad data.
In modern MSTest, Assert.ThrowsException<T>() is usually the clearest and most precise choice. Older code may also use the [ExpectedException] attribute, but that approach is less flexible and usually less explicit.
Mental Model
Think of an exception test like a fire alarm drill.
You are not hoping the alarm goes off randomly. You deliberately trigger a known condition and verify that the correct alarm responds.
- Your code under test = the building system
- Invalid input or bad state = the smoke trigger
- Expected exception = the correct alarm
- The test = the inspector checking that the right thing happened
If no alarm goes off, something is wrong. If the wrong alarm goes off, something is also wrong.
That is exactly what exception assertions do in a unit test.
Syntax and Examples
The most common MSTest syntax is:
Assert.ThrowsException<ExceptionType>(() =>
{
// code that should throw
});
Basic example
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class CalculatorTests
{
[TestMethod]
public void Divide_ByZero_ThrowsDivideByZeroException()
{
var calculator = new Calculator();
Assert.ThrowsException<DivideByZeroException>(() =>
{
calculator.Divide(10, 0);
});
}
}
public class Calculator
{
public int Divide(int a, int b)
{
if (b == 0)
{
throw new DivideByZeroException();
}
return a / b;
}
}
Why this works
Assert.ThrowsException<DivideByZeroException>()expects that exact exception type
Step by Step Execution
Consider this example:
Assert.ThrowsException<ArgumentNullException>(() =>
{
SaveFile(null);
});
And the method:
public void SaveFile(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
// save logic
}
Here is what happens step by step:
- The test starts running.
Assert.ThrowsException<ArgumentNullException>prepares to execute the lambda.- The lambda calls
SaveFile(null). - Inside
SaveFile, the conditionpath == nullis true. - The method throws
ArgumentNullException. - MSTest catches that exception.
- MSTest checks whether the caught exception is exactly the expected type.
- Because it matches, the assertion passes.
- The test succeeds.
If SaveFile(null) did not throw anything, the test would fail.
If it threw instead of , the test would also fail.
Real World Use Cases
Exception assertions are common in real software because many methods must reject invalid states.
Common scenarios
- Input validation
- A registration method throws
ArgumentExceptionfor invalid email input
- A registration method throws
- Null checks
- A service throws
ArgumentNullExceptionwhen a required dependency or parameter is missing
- A service throws
- Business rules
- An order service throws
InvalidOperationExceptionif checkout is attempted with an empty cart
- An order service throws
- File and I/O handling
- A file loader throws
FileNotFoundExceptionfor missing files
- A file loader throws
- Parsing and conversion
- A parser throws
FormatExceptionwhen input is not in the correct format
- A parser throws
- Security and authorization
- A method throws
UnauthorizedAccessExceptionwhen a user lacks permission
- A method throws
Example from an API-related service
public void SetPageSize(int pageSize)
{
(pageSize <= )
{
ArgumentOutOfRangeException((pageSize));
}
}
Real Codebase Usage
In real projects, developers usually use exception assertions together with a few common patterns.
Guard clauses
Guard clauses fail early when input is invalid.
public void SendEmail(string address)
{
if (string.IsNullOrWhiteSpace(address))
throw new ArgumentException("Email address is required.", nameof(address));
// continue
}
Test:
Assert.ThrowsException<ArgumentException>(() => service.SendEmail(""));
Early validation
Many services validate arguments at the start of the method.
public void UpdatePrice(decimal price)
{
if (price < 0)
throw new ArgumentOutOfRangeException(nameof(price));
}
Verifying exception details
Real code often checks more than just the exception type.
Common Mistakes
1. Putting too much code inside the exception assertion
If you wrap too much code, the test may pass for the wrong reason.
Less clear
Assert.ThrowsException<ArgumentException>(() =>
{
var service = new UserService();
service.Initialize();
service.CreateUser("");
});
If Initialize() throws, the test may still pass incorrectly depending on the expected type.
Better
var service = new UserService();
service.Initialize();
Assert.ThrowsException<ArgumentException>(() =>
{
service.CreateUser("");
});
2. Using the wrong exception type
Assert.ThrowsException<Exception>(() => service.CreateUser(""));
This is usually too broad. Prefer the most specific type.
Better
Assert.ThrowsException<ArgumentException>(() => service.CreateUser(""));
3. Using [ExpectedException] for complex tests
Older MSTest code often uses:
[]
[]
{
service = UserService();
service.CreateUser();
}
Comparisons
Common ways to test exceptions in MSTest
| Approach | Example | Pros | Cons | Recommended? |
|---|---|---|---|---|
Assert.ThrowsException<T>() | Assert.ThrowsException<ArgumentException>(() => DoWork()); | Clear, precise, can inspect returned exception | Slightly more verbose than an attribute | Yes |
[ExpectedException] | [ExpectedException(typeof(ArgumentException))] | Simple for very small tests | Less precise, exception can come from any line | Usually no |
Manual try/catch | try { ... } catch (...) { ... } | Full control | More boilerplate, easier to get wrong |
Cheat Sheet
// Preferred MSTest pattern
Assert.ThrowsException<ArgumentException>(() => SomeMethod());
// Capture exception for more checks
var ex = Assert.ThrowsException<ArgumentNullException>(() => SomeMethod(null));
Assert.AreEqual("paramName", ex.ParamName);
Quick rules
- Use
Assert.ThrowsException<T>()for most exception tests - Keep only the failing call inside the lambda when possible
- Prefer specific exception types over
Exception - Capture the returned exception if you need to inspect it
- Use
[ExpectedException]mainly when maintaining older tests
Older MSTest pattern
[ExpectedException(typeof(ArgumentException))]
[TestMethod]
public void TestMethod()
{
SomeMethod();
}
Good exception types to know
ArgumentExceptionArgumentNullExceptionArgumentOutOfRangeExceptionInvalidOperationException
FAQ
How do I assert that an exception was thrown in MSTest?
Use Assert.ThrowsException<T>() and place the code that should fail inside the lambda.
Assert.ThrowsException<ArgumentException>(() => DoWork());
Is Assert.ThrowsException<T>() better than [ExpectedException]?
Usually yes. It is more precise, easier to read, and lets you inspect the exception object.
Can I check the exception message in MSTest?
Yes. Store the returned exception and assert its properties.
var ex = Assert.ThrowsException<ArgumentException>(() => DoWork());
Assert.AreEqual("Invalid input.", ex.Message);
What happens if no exception is thrown?
The test fails because MSTest expected an exception and did not receive one.
What happens if the wrong exception type is thrown?
The test fails. Assert.ThrowsException<T>() expects the specific exception type you provide.
Should I test exact exception messages?
Only when necessary. Messages may change. Stable properties like ParamName are often better for argument-related exceptions.
Can I still use try/catch in MSTest?
Yes, but usually only when you need more control than provides.
Mini Project
Description
Build a small validation-focused service and test it with MSTest. The project demonstrates how to verify that methods throw the correct exceptions when invalid input is passed. This mirrors real application code where services reject bad data before doing work.
Goal
Create a ProductService and write MSTest unit tests that confirm invalid product data throws the correct exceptions.
Requirements
- Create a
ProductServiceclass with anAddProduct(string name, decimal price)method. - Throw
ArgumentExceptionwhen the name is empty or whitespace. - Throw
ArgumentOutOfRangeExceptionwhen the price is negative. - Return a success message when the input is valid.
- Write MSTest methods that verify both exception cases and one successful case.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Array Initialization Syntaxes Explained
Learn all common C# array initialization syntaxes with examples, rules, comparisons, and mistakes beginners often make.