- Home
- .NET tutorials
- xUnit advanced Assert methods: Throws, IsType + more
xUnit advanced Assert methods: Throws, IsType + more
Published: Monday 6 October 2025
xUnit assert methods like Assert.Equal
and Assert.True
are commonly used by developers. But did you know there are advanced Assert
methods that can make your test cleaner and more reliable?
This article will show you how use advanced assert methods like Assert.Throws
, Assert.Raises
and Assert.IsType
so you can provide more coverage within your unit tests.
The methods we are going to test
Throughout this article, we are going use these methods to write unit tests:
// Product.cs
public record Product(int Id, string Name);
// RatingEnum.cs
public enum RatingEnum
{
Poor,
Satisfactory,
Good,
Great
}
// ProductHelper.cs
public static class ProductHelper
{
public static void AddProduct(this IList<Product> products, int id, string name)
{
if (products == null)
{
products = new List<Product>();
}
products.Add(new Product(id, name));
}
}
// RatingHelper.cs
public static class RatingHelper
{
public static RatingEnum GetRating(this int number)
{
return number switch
{
>= 1 and < 2 => RatingEnum.Poor,
>= 2 and < 4 => RatingEnum.Satisfactory,
>= 4 and < 7 => RatingEnum.Good,
>= 7 and <= 10 => RatingEnum.Great,
_ => throw new ArgumentOutOfRangeException("Unable to get rating")
};
}
}
Type assertions
This is where we verify whether the object returned is a particular type. There is the Assert.IsType
and Assert.IsNotType
methods. If a type inherits another type, you can use the Assert.IsAssignableFrom
method and specify one of the parent types of the object being returned. The opposite to that is Assert.IsNotAssignableFrom
.
// RatingHelperTypeTests.cs
public class RatingHelperTypeTests
{
[Fact]
public void GetRating_WhenCalled_ChecksItsARatingEnumType()
{
var act = RatingHelper.GetRating(9);
Assert.IsType<RatingEnum>(act);
}
[Fact]
public void GetRating_WhenCalled_ChecksItsNotAnIntType()
{
var act = RatingHelper.GetRating(9);
Assert.IsNotType<int>(act);
}
[Fact]
public void GetRating_WhenCalled_CheckItsAssignableFromObject()
{
var act = RatingHelper.GetRating(8);
Assert.IsAssignableFrom<object>(act);
}
[Fact]
public void GetRating_WhenCalled_NotAssignableFromInt()
{
var act = RatingHelper.GetRating(8);
Assert.IsNotAssignableFrom<int>(act);
}
}
Collection assertions
There are a wide range of collection assertions that you can use.
The Assert.Empty
method checks to see if a collection has no elements with the Assert.NotEmpty
method doing the opposite. Then there's the Assert.Distinct
method which verifies that the elements in a collection are all unique.
// ProductHelperCollectionTests.cs
public class ProductHelperCollectionTests
{
[Fact]
public void AddProduct_NotCalled_ReturnsEmptyCollection()
{
var products = new List<Product>();
Assert.Empty(products);
}
[Fact]
public void AddProduct_WhenCalled_ReturnsNotEmptyCollection()
{
var products = new List<Product>();
products.AddProduct(1, "Watch");
Assert.NotEmpty(products);
}
[Fact]
public void AddProduct_WhenCalledWithDifferentProperties_ReturnsDistinctCollection()
{
var products = new List<Product>();
products.AddProduct(1, "Watch");
products.AddProduct(2, "Necklace");
Assert.Distinct(products);
}
}
The Assert.All
method executes an assertion against every element in the collection. The Assert.Collection
method verifies both the order and the contents of a collection.
// ProductHelperCollectionTests.cs
public class ProductHelperCollectionTests
{
[Fact]
public void AddProduct_LessThanThreeProducts_ReturnsTrue()
{
var products = new List<Product>();
products.AddProduct(1, "Watch");
products.AddProduct(2, "Necklace");
Assert.All(products, p => Assert.True(p.Id < 3));
}
[Fact]
public void AddProduct_WhenCalled_CheckOrder()
{
var products = new List<Product>();
products.AddProduct(1, "Watch");
products.AddProduct(2, "Necklace");
Assert.Collection(products,
p => Assert.Equivalent(new Product(1, "Watch"), p),
p => Assert.Equivalent(new Product(2, "Necklace"), p)
);
}
}
Then there's the Assert.Single
method which checks if there is only one element and returns it so you can verify the element.
// ProductHelperCollectionTests.cs
public class ProductHelperCollectionTests
{
[Fact]
public void AddProduct_WhenCalledOnce_VerifyOnlyOneRecord()
{
var products = new List<Product>();
products.AddProduct(1, "Watch");
Assert.Single(products);
Assert.Equivalent(new Product(1, "Watch"), single);
}
}
The Assert.Subset
method ensures that all elements in the expected subset are contained in the actual superset. The Assert.ProperSet
is like Assert.Subset
, but the superset must contain additional elements beyond the subset. The Assert.Superset
method ensures that actual subset is contained in the expected superset.
// ProductHelperCollectionTests.cs
public class ProductHelperCollectionTests
{
[Fact]
public void AddProduct_WhenCalled_IsSubsetOfExpected()
{
var expectedProducts = new HashSet<Product>();
expectedProducts.UnionWith([
new Product(1, "Watch"),
new Product(2, "Necklace")
]);
var actualProducts = new List<Product>();
actualProducts.AddProduct(1, "Watch");
Assert.Subset(expectedProducts, actualProducts.ToHashSet());
}
[Fact]
public void AddProduct_WhenCalled_IsProperSubsetOfExpected()
{
var expectedProducts = new HashSet<Product>();
expectedProducts.UnionWith([
new Product(1, "Watch"),
new Product(2, "Necklace")
]);
var actualProducts = new List<Product>();
actualProducts.AddProduct(1, "Watch");
Assert.ProperSubset(expectedProducts, actualProducts.ToHashSet());
}
[Fact]
public void AddProduct_WhenCalled_IsSupersetOfExpected()
{
var expectedProducts = new HashSet<Product>();
expectedProducts.UnionWith([
new Product(1, "Watch"),
]);
var actualProducts = new List<Product>();
actualProducts.AddProduct(1, "Watch");
actualProducts.AddProduct(1, "Necklace");
Assert.Superset(expectedProducts, actualProducts.ToHashSet());
}
}
Event assertions
In a class, you can implement the INotifyPropertyChanged
interface to send an event when a property is changed.
In this Category
class when setting the Name
property, if the value changes it will invoke the PropertyChanged
event handler which is required when implementing the INotifyPropertyChanged
interface.
// Category.cs
public class Category : INotifyPropertyChanged
{
private string _name = string.Empty;
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
In xUnit, there is a Assert.PropertyChanged
assert method. This ensures that the PropertyChanged
event handler is invoked.
In this test, we've created a new Category
instance with an empty Name
property. When calling the Assert.PropertyChanged
method, we are setting the Name
property to Electronics
which will invoke the PropertyChanged
event handler and pass the test.
// CategoryPropertyChangedTests.cs
public class CategoryPropertyChangedTests
{
[Fact]
public void Category_WhenNameIsChanged_CallsOnPropertyChanged()
{
var category = new Category();
Assert.PropertyChanged(
category,
nameof(category.Name),
() => category.Name = "Electronics"
);
}
}
There is also the Assert.Raises
event that asserts that an event was raised during the test execution. The Assert.RaisesAny
is like Assert.Raises
but will match any event argument type that derives from the generic type specified in the assert method.
In this Job
class, we are invoking the CompletedEventHandler
when MarkAsComplete
is called. With the Delete
method, we are invoking the DeletedEventHandler
handler.
// Job.cs
public class Job
{
public class CompletedEventArgs : EventArgs { }
public class DeletedEventArgs : EventArgs { }
public event EventHandler<CompletedEventArgs>? CompletedEventHandler;
public event EventHandler<EventArgs>? DeletedEventHandler;
public void MarkAsComplete()
{
CompletedEventHandler?.Invoke(this, new CompletedEventArgs());
}
public void Delete()
{
DeletedEventHandler?.Invoke(this, new DeletedEventArgs());
}
}
In these tests we are checking to see if the events are invoked. Although the DeletedEventHandler
property is invoked with a DeletedEventArgs
type, as it derives from the EventArgs
type it means that it will pass if we call Assert.RaisesAny<EventArgs>
in the unit test.
// JobRaisesTests.cs
public class JobRaisesTests
{
[Fact]
public void Job_MarkAsComplete_RaisesACompletedEventArgs()
{
var job = new Job();
var evt = Assert.Raises<CompletedEventArgs>(
h => job.CompletedEventHandler += h,
h => job.CompletedEventHandler -= h,
job.MarkAsComplete);
Assert.NotNull(evt);
Assert.Equal(job, evt.Sender);
Assert.Equivalent(new CompletedEventArgs(), evt.Arguments);
}
[Fact]
public void Job_Delete_RaisesAnyEventArgs()
{
var job = new Job();
var evt = Assert.RaisesAny<EventArgs>(
h => job.DeletedEventHandler += h,
h => job.DeletedEventHandler -= h,
job.Delete);
Assert.NotNull(evt);
Assert.Equal(job, evt.Sender);
Assert.Equivalent(new DeletedEventArgs(), evt.Arguments);
}
}
Exception assertions
Exception assertions allows you to capture any exception that gets thrown. The Assert.Throws
method ensures that the provided actions throws the specified exception type. You can also use Assert.ThrowsAsync
for asynchronous methods. There's also Assert.ThrowsAny
and Assert.ThrowsAnyAsync
. This checks that the action throws any exception assignable from the exception type specified in the generic type.
// RatingHelperExceptionTests.cs
public class RatingHelperExceptionTests
{
[Fact]
public void GetRating_OutOfRangeRating_ThrowsArgumentOutOfRangeException()
{
Assert.Throws<ArgumentOutOfRangeException>(() => RatingHelper.GetRating(11));
}
[Fact]
public void GetRating_OutOfRangeRating_ThrowsAnyException()
{
Assert.ThrowsAny<Exception>(() => RatingHelper.GetRating(11));
}
}
Multiple assertions
The Assert.Multiple
method allows multiple assertions to be grouped together, reporting all failures at once. This is useful when multiple outcomes must be verified together.
// ProductHelperMultipleTests.cs
public class ProductHelperMultipleTests
{
[Fact]
public void AddProduct_WhenCalled_CheckValues()
{
var products = new List<Product>();
products.AddProduct(1, "Watch");
products.AddProduct(2, "Necklace");
Assert.Multiple(() =>
{
Assert.Equal(2, products.Count);
Assert.Equal("Watch", products[0].Name);
Assert.Equal("Necklace", products[1].Name);
});
}
}
Watch the video
When you watch the video, you will learn how to use these advanced assert methods and get a better understanding on how each one works:
And when you download the code example, you can try each assert method mentioned in this article for yourself.
Final thoughts
xUnit's advanced assertion methods make your tests more precise, and robust. You can leverage these specialised assertions to improve test readability and intent.
Using the right assertion for the right situation not only makes failures easier to diagnose but will also allow for greater code coverage.
Latest tutorials

