Because life's too short for hard-to-read assertions! 🔍
Shouldly.FromAssert is a Roslyn analyzer that helps you migrate from traditional NUnit assertions to the more human-readable Shouldly library. It's like having a friendly code review buddy who's really into making tests more readable (but doesn't drink your coffee).
- Automatically detects NUnit assertions and suggests Shouldly alternatives
- Provides code fixes to transform assertions with a single click
- Supports a wide range of assertion types:
- Basic assertions (
Assert.AreEqual→ShouldBe) - String assertions (
StringAssert.Contains→ShouldContain) - Collection assertions (
CollectionAssert.Contains→ShouldContain) - Type assertions (
Assert.IsInstanceOf→ShouldBeOfType;Is.TypeOf<T>()→ShouldBeOfType<T>(),Is.InstanceOf<T>()→ShouldBeAssignableTo<T>(), withIs.Notandtypeof(T)forms) - And many more!
- Basic assertions (
dotnet add package Shouldly.FromAssert- Install the package
- Write your tests as usual with NUnit
- Look for the analyzer suggestions (they'll appear as warnings)
- Click the lightbulb 💡 or press (Alt+Enter or Ctrl+. depending on the IDE religeon you practice)
- Select "Convert to Shouldly"
- Watch your assertions transform into beautiful, readable Shouldly statements
// Before: 😕
Assert.That(contestant, Is.EqualTo(1337));
CollectionAssert.Contains(contestants, winner);
StringAssert.StartsWith("Hello", greeting);
// After: 😊
contestant.ShouldBe(1337);
contestants.ShouldContain(winner);
greeting.ShouldStartWith("Hello");Here are some examples of the transformations this analyzer can perform:
| NUnit | Shouldly |
|---|---|
Assert.That(x, Is.EqualTo(y)) |
x.ShouldBe(y) |
Assert.IsTrue(x > 10) |
(x > 10).ShouldBeTrue() |
Assert.IsNull(x) |
x.ShouldBeNull() |
CollectionAssert.Contains(list, item) |
list.ShouldContain(item) |
StringAssert.StartsWith("Hi", str) |
str.ShouldStartWith("Hi") |
Assert.Throws<ArgumentException>(() => method()) |
Should.Throw<ArgumentException>(() => method()) |
Assert.Multiple(() => { Assert.That(a, Is.EqualTo(1)); Assert.IsNotNull(b); }) |
this.ShouldSatisfyAllConditions(() => a.ShouldBe(1), () => b.ShouldNotBeNull()) |
Assert.That(x, Is.EqualTo(y), "why") |
x.ShouldBe(y, "why") |
Assert.That(x, Is.Null) |
x.ShouldBeNull() |
Assert.That(list, Has.Count.EqualTo(3)) |
list.Count.ShouldBe(3) |
Assert.That(list, Is.EquivalentTo(other)) |
list.ShouldBe(other, ignoreOrder: true) |
Assert.That(s, Does.StartWith("/").And.EndWith(".png")) |
s.ShouldStartWith("/"); s.ShouldEndWith(".png"); |
Assert.That(x, Is.EqualTo(0.8).Within(0.001)) |
x.ShouldBe(0.8, 0.001) |
Assert.ThrowsAsync<ArgumentException>(() => MethodAsync()) |
Should.Throw<ArgumentException>(() => MethodAsync()) |
Assert.Fail("why") |
throw new ShouldAssertException("why"); |
Assert.That(s, Does.Contain("a").Or.Contain("b")) |
new[] { "a", "b" }.ShouldContain(item => s.Contains(item)) |
And many more! Check out the tests for a complete list of supported conversions.
Assert.Multiple is only converted (and only reported) when its lambda contains nothing but expression statements and sits in an instance member, since ShouldSatisfyAllConditions needs this and takes one Action per condition. Hoist locals out of the block first. An async lambda in an async method has each awaited value moved into a local above the call (var readAsByteArray = await response.ReadAsByteArrayAsync();), unless an await is only evaluated conditionally (?., ??, &&, ||, ?:) or is a whole statement. With nullable enabled, a x!. on the first statement no longer carries into the later lambdas; add x.ShouldNotBeNull(); above the block instead. The asserts inside an Assert.Multiple are converted with it, so one fix-all (or one dotnet format analyzers --diagnostics SHU001 run) does the whole file.
Assert.Ignore, Assert.Pass, Assert.Inconclusive and Assert.Warn are runner control rather than assertions, so they aren't reported.
With nullable enabled, a Does.Contain, Does.StartWith, Does.EndWith or Does.Match on a string? that may be null becomes x.ShouldNotBeNull().ShouldContain(...). NUnit fails these on null, and Shouldly's string overloads take a non-nullable string, so the plain call would raise CS8604. Does.Not.Contain passes on null in NUnit, so it gets no ShouldNotBeNull().
Found a bug? Have a suggestion? Want to add support for more assertions? We'd love your help! Please feel free to:
- Open an issue
- Submit a pull request
- Start a discussion
- Share your success stories
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
- The awesome Shouldly team for making assertions more human
- The Roslyn team for making this possible
- Coffee ☕, for making developers possible
Made with 💚 by Agoda