Unit Testing with xUnit
Unit tests are the base of the pyramid: fast checks of small pieces of logic. xUnit is the most popular .NET framework.
On this page · 5 min · Intermediate
On this page
- Learning objectives
- Explanation
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Intermediate
- Updated
- Oct 29, 2026
Related tutorials
Learning objectives#
- Write [Fact] and [Theory] tests
- Structure tests with Arrange-Act-Assert
- Test edge cases and exceptions
- Run tests and read results
Explanation#
[Fact] is a single test; [Theory] with [InlineData] runs the same test with several inputs.
Arrange the inputs, Act by calling the method, Assert the outcome — one behaviour per test.
Test edges and errors: Assert.Throws<ArgumentException>(() => …).
Name tests by behaviour: Method_Scenario_ExpectedResult.
Code example: Testing a price calculator#
public static class PriceCalculator
{
public static decimal Total(decimal subtotal, string? coupon)
{
if (subtotal < 0) throw new ArgumentOutOfRangeException(nameof(subtotal));
var discount = coupon == "SAVE10" ? subtotal * 0.10m : 0m;
var delivery = subtotal - discount >= 999 ? 0m : 49m;
return subtotal - discount + delivery;
}
}
public class PriceCalculatorTests
{
[Theory]
[InlineData(500, null, 549)] // below free-delivery threshold
[InlineData(999, null, 999)] // exactly at threshold
[InlineData(1000, "SAVE10", 949)] // 900 after discount → delivery charged
[InlineData(2000, "SAVE10", 1800)]
public void Total_applies_coupon_and_delivery(decimal subtotal, string? coupon, decimal expected)
=> Assert.Equal(expected, PriceCalculator.Total(subtotal, coupon));
[Fact]
public void Total_negative_subtotal_throws()
=> Assert.Throws<ArgumentOutOfRangeException>(() => PriceCalculator.Total(-1, null));
}
Passed! - Failed: 0, Passed: 5, Skipped: 0, Total: 5, Duration: 21 ms
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- The Theory runs four data rows, including boundaries.
- Each row asserts one expected total.
- The Fact checks invalid input throws.
- Five tests run in about 20 ms.
Real-world use case#
Pricing rules are fully covered by fast unit tests, so UI tests only check one purchase journey.
Common mistakes#
Failures become hard to diagnose. One behaviour per test.
Those are integration tests. Keep unit tests in memory.
Best practices#
- Arrange-Act-Assert
- Theories for data variations
- Cover boundaries and errors
- Descriptive test names
Remember this#
[Fact] = one case. [Theory] = many cases.
Arrange · Act · Assert.
Fast, isolated, deterministic.
Practice exercise#
Your task: Add a test row proving an unknown coupon gives no discount.
Show solution
[InlineData(2000, "BOGUS", 2000)]
Mini quiz#
Which attribute runs a test with several data rows?
Want more? Practise all Automation Testing MCQs · Automation Testing interview questions
My notes#
Saved in this browser.