# Overview

[![CI](https://github.com/shouldly/shouldly/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/shouldly/shouldly/actions/workflows/CI.yml) [![NuGet](https://img.shields.io/nuget/dt/shouldly.svg)](https://www.nuget.org/packages/Shouldly) [![NuGet](https://img.shields.io/nuget/vpre/shouldly.svg)](https://www.nuget.org/packages/Shouldly) [![Join the chat at https://gitter.im/shouldly/shouldly](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/shouldly/shouldly?utm_source=badge\&utm_medium=badge\&utm_campaign=pr-badge\&utm_content=badge)

Shouldly is an assertion framework which focuses on giving great error messages when the assertion fails while being simple and terse.

This is the old *Assert* way:

```csharp
Assert.That(contestant.Points, Is.EqualTo(1337));
```

For your troubles, you get this message, when it fails:

```
Expected 1337 but was 0
```

How it **Should** be:

```csharp
contestant.Points.ShouldBe(1337);
```

Which is just syntax, so far, but check out the message when it fails:

```
contestant.Points should be 1337 but was 0
```

It might be easy to underestimate how useful this is. Another example, side by side:

```csharp
Assert.That(map.IndexOfValue("boo"), Is.EqualTo(2));
// -> Expected 2 but was -1

map.IndexOfValue("boo").ShouldBe(2);
// -> map.IndexOfValue("boo") should be 2 but was -1
```

**Shouldly** uses the code before the *ShouldBe* statement to report on errors, which makes diagnosing easier.

## Installation

Shouldly can be [found here on NuGet](https://www.nuget.org/packages/Shouldly/) and can be installed by copying and pasting the following command into your [Package Manager Console within Visual Studio](https://docs.microsoft.com/en-us/nuget/consume-packages/install-use-packages-powershell).

```bash
Install-Package Shouldly
```

Alternatively if you're using .NET Core then you can install Shouldly via the command line interface with the following command:

```bash
dotnet add package Shouldly
```

## Contributing

Contributions to Shouldly are very welcome. For guidance, please see [CONTRIBUTING.md](https://github.com/shouldly/shouldly/blob/master/CONTRIBUTING.md)

## Prerequisites for running on build server

Shouldly uses the source code to make its error messages better. Hence, on the build server you will need to have the "full" pdb files available where the tests are being run.

What is meant by "full" is that when you set up your "release" configuration in Visual Studio and you go to Project Properties > Build > Advanced > Debug, you should set it to "full" rather than "pdb-only".

## Currently maintained by

* [Stuart Lang](https://github.com/slang25)
* [Simon Cropp](https://github.com/SimonCropp)
* [Joseph Musser](https://github.com/jnm2)

If you are interested in helping out, jump on [Gitter](https://gitter.im/shouldly/shouldly) and have a chat.

## Brought to you by

* Joseph Woodward
* Dave Newman
* Xerxes Battiwalla
* Anthony Egerton
* Peter van der Woude
* Jake Ginnivan


# Contributing


# Getting Started

Here are some guides to setup a Shouldly project.

## Setup a project with Dotnet CLI

In this guide, we will set up a project with Shouldly unit tests.

We won't use Visual Studio, we will build our project bare bones with Dotnet CLI and a text editor.

## Prerequisites

* Install [Dotnet CLI Tools](https://dotnet.microsoft.com/learn/dotnet/hello-world-tutorial/install).
* You need to install a text editor (Notepad++, VSCode, etc...).

## Creating the main program

The project structure will be as follows:

```csharp
/project-name
    project-name.sln
    /program
        ...
    /test
```

1. Create your root directory, name it your project name.
2. Open a terminal in your root directory.
3. Run `dotnet new sln`.
4. Go into program folder and run `dotnet new classlib`.
5. Rename *Class1.cs* to *Program.cs*.
6. Add the following code to *Program.cs*:

```csharp
using System;

public static class Program
{
    public static string TestWorks()
    {
        return "Works";
    }
}
```

Go to the root folder and run `dotnet sln add program/program.csproj`, in order to add *program* to your solution.

## Creating unit tests

In this part we will create the unit tests for the main program.

1. Move to test folder and run `dotnet new nunit` *(you can use others unit tests frameworks but in this guide we will stick to nunit)*.
2. Add a reference to the main program by running `dotnet add reference ../program/program.csproj`
3. In the root folder add the tests to the solution by running `dotnet sln add test/test.csproj`.

## Adding Shouldly

Now comes the important part, when we actually add Shouldly.

1. Go to the test folder and run `dotnet add package Shouldly`, to add Shouldly as a NuGet Package.
2. Add the following code to *UnitTest1.cs*:

```csharp
using NUnit.Framework;
using Shouldly;

public class Tests
{
    [Test]
    public void Test1()
    {
        Program.TestWorks().ShouldBe("Works");
    }
}
```

## Testing

To test your project, in your root folder run `dotnet test`.

And there you go, you have a basic project with unit tests using Shouldly.

## Summary

We created a classlib project using Dotnet CLI Tools, then added unit tests and added Shouldly as a NuGet package to be able to use Shouldly in our tests.


# Migrating from FluentAssertions

This guide is for teams moving from [FluentAssertions](https://fluentassertions.com/) to Shouldly, and targets Shouldly 5.x.

Most of a migration is mechanical: `value.Should().Be(x)` becomes `value.ShouldBe(x)`. The value is in the handful of places where the two libraries behave differently. Those are the cases that make a green FA test fail, or silently pass, after a rename. This guide focuses on them.

## The mental model

|              | FluentAssertions                                                           | Shouldly                                             |
| ------------ | -------------------------------------------------------------------------- | ---------------------------------------------------- |
| Import       | `using FluentAssertions;`                                                  | `using Shouldly;`                                    |
| Entry point  | `value.Should().Be(x)`                                                     | `value.ShouldBe(x)`                                  |
| Failure type | the host test framework's assert exception (or `AssertionFailedException`) | always `ShouldAssertException`                       |
| Chaining     | `.And` / `.Which` fluent chain                                             | separate statements (some assertions return a value) |

There is no `.Should()` gateway. Every assertion is an extension method directly on the value, named `Should…`. Most return `void`. A few return something useful for a follow-up assertion:

```csharp
InvalidOperationException ex = action.ShouldThrow<InvalidOperationException>();
Cat cat = animal.ShouldBeOfType<Cat>();          // also ShouldBeAssignableTo<T>
string name = maybeNull.ShouldNotBeNull();       // returns the non-null value
Order only = orders.ShouldHaveSingleItem();      // returns the single element
```

Shouldly puts the code you asserted on into the failure message. In v5 that expression is captured by the compiler (`CallerArgumentExpression`), so you get the source text in the message without any runtime source lookup:

```csharp
var result = Add(2, 2);
result.ShouldBe(5);
// -> result should be 5 but was 4
```

## Quick reference

Direct name mappings. Where a mapping is not a plain rename, a note in parentheses says what changed.

### Equality and identity

| FluentAssertions                 | Shouldly                                                                              |
| -------------------------------- | ------------------------------------------------------------------------------------- |
| `x.Should().Be(y)`               | `x.ShouldBe(y)`                                                                       |
| `x.Should().NotBe(y)`            | `x.ShouldNotBe(y)`                                                                    |
| `x.Should().BeNull()`            | `x.ShouldBeNull()`                                                                    |
| `x.Should().NotBeNull()`         | `x.ShouldNotBeNull()`                                                                 |
| `x.Should().BeTrue()`            | `x.ShouldBeTrue()`                                                                    |
| `x.Should().BeFalse()`           | `x.ShouldBeFalse()`                                                                   |
| `x.Should().BeSameAs(y)`         | `x.ShouldBeSameAs(y)`                                                                 |
| `x.Should().NotBeSameAs(y)`      | `x.ShouldNotBeSameAs(y)`                                                              |
| `x.Should().BeOfType<T>()`       | `x.ShouldBeOfType<T>()`                                                               |
| `x.Should().NotBeOfType<T>()`    | `x.ShouldNotBeOfType<T>()`                                                            |
| `x.Should().BeAssignableTo<T>()` | `x.ShouldBeAssignableTo<T>()`                                                         |
| `x.Should().BeOneOf(a, b)`       | `x.ShouldBeOneOf([a, b])` (takes an array, not `params`)                              |
| `x.Should().BeEquivalentTo(y)`   | `x.ShouldBeEquivalentTo(y)` (see [caveats](#object-equivalence-shouldbeequivalentto)) |

### Comparisons and ranges

| FluentAssertions                        | Shouldly                            |
| --------------------------------------- | ----------------------------------- |
| `x.Should().BeGreaterThan(y)`           | `x.ShouldBeGreaterThan(y)`          |
| `x.Should().BeGreaterThanOrEqualTo(y)`  | `x.ShouldBeGreaterThanOrEqualTo(y)` |
| `x.Should().BeLessThan(y)`              | `x.ShouldBeLessThan(y)`             |
| `x.Should().BeLessThanOrEqualTo(y)`     | `x.ShouldBeLessThanOrEqualTo(y)`    |
| `x.Should().BePositive()`               | `x.ShouldBePositive()`              |
| `x.Should().BeNegative()`               | `x.ShouldBeNegative()`              |
| `x.Should().BeInRange(lo, hi)`          | `x.ShouldBeInRange(lo, hi)`         |
| `x.Should().NotBeInRange(lo, hi)`       | `x.ShouldNotBeInRange(lo, hi)`      |
| `date.Should().BeCloseTo(y, precision)` | `date.ShouldBe(y, tolerance)`       |
| `date.Should().BeAfter(y)`              | `date.ShouldBeGreaterThan(y)`       |
| `date.Should().BeBefore(y)`             | `date.ShouldBeLessThan(y)`          |
| `num.Should().BeApproximately(y, tol)`  | `num.ShouldBe(y, tol)`              |

### Collections

| FluentAssertions                                  | Shouldly                                                                                                                                                      |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `c.Should().Contain(item)`                        | `c.ShouldContain(item)`                                                                                                                                       |
| `c.Should().NotContain(item)`                     | `c.ShouldNotContain(item)`                                                                                                                                    |
| `c.Should().Contain(x => …)`                      | `c.ShouldContain(x => …)`                                                                                                                                     |
| `c.Should().BeEmpty()`                            | `c.ShouldBeEmpty()`                                                                                                                                           |
| `c.Should().NotBeEmpty()`                         | `c.ShouldNotBeEmpty()`                                                                                                                                        |
| `c.Should().HaveCount(n)`                         | `c.ShouldHaveCount(n)`                                                                                                                                        |
| `c.Should().HaveCountGreaterThan(n)`              | `c.Count.ShouldBeGreaterThan(n)` (no `ShouldHaveCountGreaterThan`)                                                                                            |
| `c.Should().Equal(a, b, c)`                       | `c.ShouldBe([a, b, c])` (order-sensitive)                                                                                                                     |
| `c.Should().BeEquivalentTo(other)`                | `c.ShouldBeEquivalentTo(other, new EquivalencyOptions { IgnoreOrder = true })` (structural + unordered; [see below](#collections-order-and-element-equality)) |
| `c.Should().OnlyHaveUniqueItems()`                | `c.ShouldBeUnique()` (not `ShouldAllBeUnique`)                                                                                                                |
| `c.Should().OnlyContain(x => …)`                  | `c.ShouldAllBe(x => …)`                                                                                                                                       |
| `c.Should().AllBeAssignableTo<T>()`               | `c.ShouldAllBe(x => x is T)` (no `ShouldAllBeAssignableTo`)                                                                                                   |
| `c.Should().AllSatisfy(x => x.Should()…)`         | `Should.Satisfy([.. c.Select(x => new Action(() => …))])` (assertion per element, all failures aggregated; [see below](#per-element-assertions-allsatisfy))   |
| `c.Should().ContainSingle()`                      | `c.ShouldHaveSingleItem()`                                                                                                                                    |
| `c.Should().ContainSingle().Which.Should().Be(v)` | `c.ShouldHaveSingleItem().ShouldBe(v)`                                                                                                                        |
| `c.Should().BeSubsetOf(other)`                    | `c.ShouldBeSubsetOf(other)`                                                                                                                                   |
| `c.Should().BeInAscendingOrder()`                 | `c.ShouldBeInOrder()`                                                                                                                                         |
| `c.Should().BeInDescendingOrder()`                | `c.ShouldBeInOrder(SortDirection.Descending)`                                                                                                                 |
| `c.Should().ContainInOrder(a, b)`                 | no equivalent; [see below](#no-drop-in-for-containinorder-or-containequivalentof)                                                                             |
| `c.Should().ContainEquivalentOf(item)`            | no equivalent for collections; [see below](#no-drop-in-for-containinorder-or-containequivalentof)                                                             |

### Dictionaries

| FluentAssertions                | Shouldly                                                    |
| ------------------------------- | ----------------------------------------------------------- |
| `d.Should().ContainKey(k)`      | `d.ShouldContainKey(k)`                                     |
| `d.Should().NotContainKey(k)`   | `d.ShouldNotContainKey(k)`                                  |
| `d.Should().Contain(k, v)`      | `d.ShouldContainKeyAndValue(k, v)`                          |
| `d.Should().ContainValue(v)`    | `d.Values.ShouldContain(v)` (no `ShouldContainValue`)       |
| `d.Should().NotContainValue(v)` | `d.Values.ShouldNotContain(v)` (no `ShouldNotContainValue`) |

### Strings

| FluentAssertions                      | Shouldly                                                                                                 |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `s.Should().Be("x")`                  | `s.ShouldBe("x")` (exact, case-sensitive)                                                                |
| `s.Should().Contain("x")`             | `s.ShouldContain("x")` ([case-sensitive by default](#strings-are-case-sensitive-by-default), matches FA) |
| `s.Should().ContainEquivalentOf("x")` | `s.ShouldContain("x", Case.Insensitive)` (FA ignores case, so pass `Case.Insensitive`)                   |
| `s.Should().StartWith("x")`           | `s.ShouldStartWith("x")` (case-sensitive by default, matches FA)                                         |
| `s.Should().EndWith("x")`             | `s.ShouldEndWith("x")` (case-sensitive by default, matches FA)                                           |
| `s.Should().Match("re*ex")`           | `s.ShouldMatch(regex)` (Shouldly takes a regex, FA `Match` takes a wildcard)                             |
| `s.Should().MatchRegex("re.ex")`      | `s.ShouldMatch("re.ex")`                                                                                 |
| `s.Should().BeNullOrEmpty()`          | `s.ShouldBeNullOrEmpty()`                                                                                |
| `s.Should().BeNullOrWhiteSpace()`     | `s.ShouldBeNullOrWhiteSpace()`                                                                           |

### Exceptions

| FluentAssertions                            | Shouldly                                                                       |
| ------------------------------------------- | ------------------------------------------------------------------------------ |
| `act.Should().Throw<T>()`                   | `act.ShouldThrow<T>()`                                                         |
| `act.Should().ThrowExactly<T>()`            | `act.ShouldThrow<T>()` then `ex.ShouldBeOfType<T>()` (no `ShouldThrowExactly`) |
| `act.Should().NotThrow()`                   | `act.ShouldNotThrow()`                                                         |
| `await act.Should().NotThrowAsync()`        | `await act.ShouldNotThrowAsync()`                                              |
| `(await act.Should().ThrowAsync<T>())`      | `await act.ShouldThrowAsync<T>()`                                              |
| `act.Should().Throw<T>().WithMessage("x*")` | `act.ShouldThrow<T>().Message.ShouldContain("x")` (no `WithMessage`)           |

## Behavioral differences to watch for

These are the traps. Read them before you trust a bulk find-and-replace.

### Strings are case-sensitive by default

Shouldly's string `ShouldContain`, `ShouldStartWith`, `ShouldEndWith` (and their `Not…` forms) are case-sensitive by default, matching FluentAssertions' `Contain`, `StartWith`, and `EndWith`, so a straight rename preserves the behavior:

```csharp
"Hello".ShouldContain("hello");     // fails, case matters
"Hello".ShouldStartWith("HELLO");   // fails, case matters
```

```
"Hello"
    should contain
"hello"
    but did not
```

When you do want a case-insensitive comparison — for example FA's `ContainEquivalentOf`, which ignores case — pass `Case.Insensitive`:

```csharp
"Hello".Should().ContainEquivalentOf("hello");    // FA, ignores case
"Hello".ShouldContain("hello", Case.Insensitive); // Shouldly equivalent
```

### `ShouldBe` is strongly typed

FluentAssertions compares through `object`, so it happily accepts mismatched types and converts them. Shouldly's `ShouldBe<T>` requires the actual and expected values to be the same type, so mismatches are caught by the compiler, not at runtime:

```csharp
int i = 1;
i.ShouldBe(1L);            // does NOT compile: int vs long

decimal d = 1m;
decimal? n = 1m;
d.ShouldBe(n);             // does NOT compile: decimal vs decimal?
```

This is usually a good thing, since it turns sloppy tests into compile errors, but it means some FA assertions that compiled will not. Fix the types (cast, or make both sides `T?`) rather than fighting it.

### Collections: order and element equality

`ShouldBe` on a collection is order-sensitive and compares elements with their normal equality:

```csharp
new[] { 1, 2, 3 }.ShouldBe([3, 2, 1]);                    // fails: order differs
new[] { 1, 2, 3 }.ShouldBe([3, 2, 1], ignoreOrder: true); // passes
```

So the FluentAssertions collection methods map like this:

* `Should().Equal(…)` (ordered, element equality) becomes `ShouldBe(…)`
* `Should().BeEquivalentTo(…)` (unordered, structural) becomes `ShouldBeEquivalentTo(…, new EquivalencyOptions { IgnoreOrder = true })`

`ShouldBe(…, ignoreOrder: true)` also compares order-insensitively, but it matches elements by their `Equals`, so it only lines up with FA's collection `BeEquivalentTo` when the elements are values (or override equality). For collections of reference types that should be compared *structurally*, use `ShouldBeEquivalentTo` with `IgnoreOrder` (see the [object equivalence](#object-equivalence-shouldbeequivalentto) section).

Because `ShouldBe` uses each element's `Equals`, a collection of reference types that don't override equality is compared by reference:

```csharp
var a = new List<Dog> { new Dog { Name = "Rex" } };
var b = new List<Dog> { new Dog { Name = "Rex" } };
a.ShouldBe(b);              // fails: different instances
a.ShouldBeEquivalentTo(b); // passes: compares member by member
```

### Chaining: `.And` and `.Which`

FluentAssertions chains with `.And` and drills in with `.Which`. Shouldly has neither. Split a `.And` chain into separate statements, and for `.Which` use the value that some assertions return.

```csharp
// FA
markup.Should().Contain("mud-card").And.Contain("mud-elevation-1");
result.Should().ContainSingle().Which.Should().Be(5);

// Shouldly
markup.ShouldContain("mud-card");
markup.ShouldContain("mud-elevation-1");
result.ShouldHaveSingleItem().ShouldBe(5);
```

(`ShouldContain` on a string is case-sensitive, matching FA's `.Contain`; add `Case.Insensitive` if you want to ignore case.)

### Custom messages (`because`)

Every FluentAssertions assertion accepts a reason with format arguments, which FA weaves into the failure sentence. The Shouldly counterpart is the `customMessage` parameter, a plain string with no format arguments, so use interpolation:

```csharp
// FA
count.Should().Be(3, "the cache warms {0} entries on startup", entries);

// Shouldly
count.ShouldBe(3, $"the cache warms {entries} entries on startup");
```

The message is appended to the failure output under "Additional Info":

```
count
    should be
3
    but was
2

Additional Info:
    the cache warms 42 entries on startup
```

### Per-element assertions (`AllSatisfy`)

FA's `AllSatisfy` runs an assertion action against every element and reports every failing element at once. It is not the same as `ShouldAllBe`, which takes a boolean predicate. Map each one to the right tool:

```csharp
// FA, a boolean predicate: use ShouldAllBe
items.Should().OnlyContain(x => x.IsActive);
items.ShouldAllBe(x => x.IsActive);

// FA, an assertion action per element: project each element into a condition and pass them to
// Should.Satisfy, which runs them all and aggregates every failure just like AllSatisfy.
inputs.Should().AllSatisfy(i => i.GetAttribute("type").Should().Be("checkbox"));
Should.Satisfy([.. inputs.Select(i => new Action(() => i.GetAttribute("type").ShouldBe("checkbox")))]);
```

If you do not need the aggregated report, a plain `foreach` of assertions also works, but it stops at the first failing element rather than listing them all:

```csharp
foreach (var i in inputs)
    i.GetAttribute("type").ShouldBe("checkbox");
```

### No drop-in for `ContainInOrder` or `ContainEquivalentOf`

Two collection assertions have no Shouldly counterpart:

* `ContainInOrder(a, b, c)` asserts the items appear in that relative order (gaps allowed). There is no built-in; a small local helper covers it:

  ```csharp
  static void ShouldContainInOrder<T>(IEnumerable<T> actual, params T[] expected)
  {
      var list = actual.ToList();
      var idx = -1;
      foreach (var e in expected)
      {
          var next = list.FindIndex(idx + 1, x => EqualityComparer<T>.Default.Equals(x, e));
          next.ShouldBeGreaterThan(idx, $"expected '{e}' after index {idx}, in order");
          idx = next;
      }
  }
  ```
* `ContainEquivalentOf(item)` on a collection (structural match of an element) has no equivalent. Assert with `ShouldContain(x => …)` on the members you care about, or loop. On a string it just means a case-insensitive substring, which is `ShouldContain(x, Case.Insensitive)`.

## Object equivalence: `ShouldBeEquivalentTo`

`ShouldBeEquivalentTo` walks the object graph and compares public fields and properties recursively. Like FA's `BeEquivalentTo`, it is **direction-sensitive**: comparison is driven by the *expected* value's members, and any extra members the actual value carries are ignored. It does **not** require the two sides to be the same type, so two structurally identical objects of different types are equivalent:

```csharp
var actual   = new Dto      { Name = "Bob", Age = 30 };
var expected = new Customer { Name = "Bob", Age = 30 };

actual.ShouldBeEquivalentTo(expected);   // passes: same members, same values
```

Because the expectation drives member selection, you assert a **subset** by passing an anonymous type (or any type) that carries only the members you care about. This is the direct replacement for FA's "project to a shape and compare" pattern:

```csharp
var person = new Person { Name = "Bob", Age = 30, Internal = "secret" };

person.ShouldBeEquivalentTo(new { Name = "Bob", Age = 30 });   // passes: Internal is not checked
```

A member present on the expected value but missing on the actual value is a failure, and every difference is collected rather than stopping at the first:

```csharp
var dto  = new Dto            { Name = "Bob", Age = 30 };
var full = new PersonExtended { Name = "Bob", Age = 30, Extra = "x" };

full.ShouldBeEquivalentTo(dto);   // passes: dto's members all match; full's Extra is ignored
dto.ShouldBeEquivalentTo(full);   // fails: full expects an Extra member dto doesn't have
```

```
Comparing object equivalence, at path:
dto [PersonExtended]
    Extra

    Expected a public member named
"Extra"
    but was not found on
Dto
```

### Options

A second overload takes an `EquivalencyOptions`:

```csharp
actual.ShouldBeEquivalentTo(expected, new EquivalencyOptions
{
    IgnoreOrder = true,                          // compare collections order-insensitively
    MembersToIgnore = { "CreatedAt", "Id" },     // skip these members anywhere in the graph
});
```

* `MembersToIgnore` is the counterpart to FA's `.Excluding(...)`, matched by member name anywhere in the graph.
* `IgnoreOrder` makes sequences compare order-insensitively; sets and dictionaries are always compared unordered/by key.

### What still differs from FA

* **Sequences are ordered by default.** Opt into `IgnoreOrder` for FA's unordered collection behavior.
* **`Equals` overrides on complex types are ignored** — comparison is always member-wise, so a type with a custom `Equals` is still compared property-by-property. (Well-known value-semantic types such as `string`, `Guid`, `DateTime`, and `Uri` are treated as leaves and compared with `Equals`.)
* **Comparers and tolerances** — a fluent `.WithAutoConversion()` switch, `.Using<T>(...)` custom comparers, or approximate numeric/`DateTime` matching — are not exposed as options yet. Numeric *leaves* are auto-converted across kinds, so `int` `5` is equivalent to `long` `5` or `double` `5.0`.

For the rare test that needs an FA feature with no counterpart today (custom comparison rules, member selection by predicate), assert the members individually with [`ShouldSatisfy`](/documentation/satisfyallconditions) so you still get every failure at once, or keep a dedicated equivalence library for those cases.

## Exceptions and messages

`ShouldThrow<T>()` returns the caught exception, so assert on its message directly. There is no `WithMessage`:

```csharp
var ex = Should.Throw<InvalidOperationException>(() => Widget.Spin());
ex.Message.ShouldContain("jammed");     // substring, like FA's WithMessage("*jammed*")
ex.Message.ShouldBe("Widget jammed");   // exact match
```

Async is the same shape. `ShouldThrowAsync<T>` returns a `Task<T>`, so `await` it:

```csharp
var ex = await Should.ThrowAsync<InvalidOperationException>(() => widget.SpinAsync());
ex.Message.ShouldContain("jammed");
```

Like FA's `Throw<T>`, `ShouldThrow<T>` matches derived exception types: asserting `ShouldThrow<ArgumentException>()` is satisfied by an `ArgumentNullException`. There is no `ThrowExactly`; if you need an exact type, check it explicitly:

```csharp
var ex = act.ShouldThrow<ArgumentException>();
ex.ShouldBeOfType<ArgumentException>();   // fails for the derived ArgumentNullException
```

## Grouping assertions (FA's `AssertionScope`)

FluentAssertions uses `using (new AssertionScope())` to report several failures together. Shouldly does not have assertion scopes; use [`ShouldSatisfy`](/documentation/satisfyallconditions) (or the static `Should.Satisfy` for unrelated conditions), which runs every condition and reports all failures at once:

```csharp
person.ShouldSatisfy(
[
    p => p.Name.ShouldBe("Alice"),
    p => p.Age.ShouldBeGreaterThan(0),
]);
```

```
person
    should satisfy all the conditions specified, but does not.
The following errors were found ...
---------------- Error 1 ----------------
    p.Name
        should be
    "Alice"
        but was
    "Bob"
...
```

`ShouldSatisfyAllConditions` still exists but is obsolete in v5: it can't capture the asserted expression and isn't trimming/AOT-safe. Prefer `ShouldSatisfy` or `Should.Satisfy`.

## Features without a direct equivalent

| FluentAssertions                                                  | Shouldly today                                                                                                                                         |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `BeEquivalentTo(…).Excluding(…)`                                  | `ShouldBeEquivalentTo(…, new EquivalencyOptions { MembersToIgnore = { … } })`                                                                          |
| `BeEquivalentTo(…)` with custom comparers / `.WithAutoConversion` | No fluent equivalent yet; assert members with `ShouldSatisfy`, or use an anonymous-type subset ([see above](#object-equivalence-shouldbeequivalentto)) |
| `ThrowExactly<T>()`                                               | `ShouldThrow<T>()` + `ex.ShouldBeOfType<T>()`                                                                                                          |
| `.WithMessage("x*")`                                              | `ex.Message.ShouldContain("x")` or `ShouldMatch(regex)`                                                                                                |
| `AssertionScope`                                                  | `ShouldSatisfy` or `Should.Satisfy`                                                                                                                    |
| `.And` / `.Which` chaining                                        | Separate statements, or use the value a `Should…` returns                                                                                              |
| `SatisfyRespectively(…)`                                          | `ShouldSatisfy` with one condition per element (indexed manually)                                                                                      |
| `ContainInOrder(…)` / `ContainEquivalentOf(…)`                    | [see above](#no-drop-in-for-containinorder-or-containequivalentof)                                                                                     |
| `Implement<TInterface>()`                                         | `typeof(IFoo).IsAssignableFrom(typeof(MyType)).ShouldBeTrue()`                                                                                         |


# Configuration

Shouldly has a few configuration options:

## DefaultFloatingPointTolerance

Allows specifying a floating point tolerance for all assertions

**Default value:** 0.0d

## DefaultTaskTimeout

`Should.Throw(Func<Task>)` blocks, the timeout is a safeguard for deadlocks.

Shouldly runs the lambda without a synchronisation context, but deadlocks are still possible. Use `Should.ThrowAsync` to be safe then await the returned task to prevent possible deadlocks.

**Default value:** 10 seconds

## CompareAsObjectTypes

Types which also are IEnumerable of themselves.

An example is `Newtonsoft.Json.Linq.JToken` which looks like this `class JToken : IEnumerable<JToken>`.

**Default value:** Newtonsoft.Json.Linq.JToken

## MaxStringLengthInMessages

How many characters of the actual and expected values a string assertion echoes back in its failure message. Longer values are truncated, and the message says so along with the value's full length:

```
actual
    should be
"Lorem ipsum dolor sit amet" (truncated to 1000 of 42317 characters, see ShouldlyConfiguration.MaxStringLengthInMessages)
```

This only bounds the verbatim echo. The `difference` section is always computed from the full, untruncated values, so lowering this can never hide a difference — it just trims the surrounding noise. Raise it when you want more of the raw value in the message:

```csharp
ShouldlyConfiguration.MaxStringLengthInMessages = 20000;
```

Scoped to the logical call context, so it flows through `async`/`await` and concurrent tests get their own value.

**Default value:** 1000

Note that the `difference` section has its own fixed limits, independent of this setting: it shows at most 3 differing regions, at most 20 changed lines per side in line mode, and windows each region to roughly 60 characters of surrounding context. `ShouldContain` and friends separately clip the searched string to 100 characters, since the useful information there is the substring, not the haystack.

## DiffStyle

Character set used for the markers that point at a difference. `Unicode` uses `▼`/`▲`, `Ascii` uses `v`/`^` for terminals that can't render the arrows.

**Default value:** `DiffStyle.Unicode`

## EscapeStyle

How control characters are rendered in difference output.

| Value             | `\r\n` renders as |
| ----------------- | ----------------- |
| `CStyle`          | `\r`, `\n`        |
| `ControlPictures` | `␍`, `␊`          |
| `Descriptive`     | `<CR>`, `<LF>`    |

Scoped to the logical call context, so it flows through `async`/`await` and concurrent tests get their own value.

**Default value:** `EscapeStyle.CStyle`


# Equality


# ShouldBe

## Objects

`ShouldBeExamples` works on all types and compares using `.Equals`.

```cs
var theSimpsonsCat = new Cat { Name = "Santas little helper" };
theSimpsonsCat.Name.ShouldBe("Snowball 2");
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/ShouldBeExamples.cs#L14-L19) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeObjects)

**Exception**

```
theSimpsonsCat.Name
    should be
"Snowball 2"
    but was
"Santas little helper"
    difference
Expected: "Snowball 2"
Actual:   "Santas little helper"
```

## Numeric

`ShouldBe` numeric overloads accept tolerances and has overloads for `float`, `double` and `decimal` types.

```cs
const decimal pi = (decimal)Math.PI;
pi.ShouldBe(3.24m, 0.01m);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeExamples.Numeric.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeExamples.Numeric.codeSample.approved.cs)

**Exception**

```
pi
    should be within
0.01m
    of
3.24m
    but was
3.14159265358979m
```

## DateTime(Offset)

DateTime overloads are similar to the numeric overloads and support tolerances.

```cs
var date = new DateTime(2000, 6, 1);
date.ShouldBe(new(2000, 6, 1, 1, 0, 1), TimeSpan.FromHours(1));
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeExamples.DateTime.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeExamples.DateTime.codeSample.approved.cs)

**Exception**

```
date
    should be within
01:00:00
    of
2000-06-01T01:00:01.0000000
    but was
2000-06-01T00:00:00.0000000
```

## TimeSpan

TimeSpan also has tolerance overloads

```cs
var timeSpan = TimeSpan.FromHours(1);
timeSpan.ShouldBe(timeSpan.Add(TimeSpan.FromHours(1.1d)), TimeSpan.FromHours(1));
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeExamples.TimeSpanExample.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeExamples.TimeSpanExample.codeSample.approved.cs)

**Exception**

```
timeSpan
    should be within
01:00:00
    of
02:06:00
    but was
01:00:00
```

## Enumerables

Enumerable comparison is done on the elements in the enumerable, so you can compare an array to a list and have it pass.

```cs
var apu = new Person { Name = "Apu" };
var homer = new Person { Name = "Homer" };
var skinner = new Person { Name = "Skinner" };
var barney = new Person { Name = "Barney" };
var theBeSharps = new List<Person> { homer, skinner, barney };
theBeSharps.ShouldBe(new[] { apu, homer, skinner, barney });
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeExamples.Enumerables.codeSample.approved.cs#L1-L6) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeExamples.Enumerables.codeSample.approved.cs)

**Exception**

```
theBeSharps
    should be
[Apu, Homer, Skinner, Barney]
    but was
[Homer, Skinner, Barney]
    difference
[*Homer*, *Skinner*, *Barney*, *]
```

## Enumerables of Numerics

If you have enumerables of `float`, `decimal` or `double` types then you can use the tolerance overloads, similar to the value extensions.

```cs
var firstSet = new[] { 1.23m, 2.34m, 3.45001m };
var secondSet = new[] { 1.4301m, 2.34m, 3.45m };
firstSet.ShouldBe(secondSet, 0.1m);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeExamples.EnumerablesOfNumerics.codeSample.approved.cs#L1-L3) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeExamples.EnumerablesOfNumerics.codeSample.approved.cs)

**Exception**

```
firstSet
    should be within
0.1m
    of
[1.4301m, 2.34m, 3.45m]
    but was
[1.23m, 2.34m, 3.45001m]
    difference
[*1.23m*, 2.34m, *3.45001m*]
```

## Bools

```cs
const bool myValue = false;
myValue.ShouldBe(true, "Some additional context");
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeExamples.BooleanExample.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeExamples.BooleanExample.codeSample.approved.cs)

**Exception**

```
myValue
    should be
True
    but was
False

Additional Info:
    Some additional context
```


# NotBe

`ShouldNotBe` is the inverse of `ShouldBe`.

## Objects

`ShouldNotBe` works on all types and compares using `.Equals`.

```cs
var theSimpsonsCat = new Cat { Name = "Santas little helper" };
theSimpsonsCat.Name.ShouldNotBe("Santas little helper");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldNotBeExamples.Objects.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldNotBeExamples.Objects.codeSample.approved.cs)

**Exception**

```
theSimpsonsCat.Name
    should not be
"Santas little helper"
    but was
```

## Numeric

`ShouldNotBe` also allows you to compare numeric values, regardless of their value type.

### Integer

```cs
const int one = 1;
one.ShouldNotBe(1);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldNotBeExamples.NumericInt.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldNotBeExamples.NumericInt.codeSample.approved.cs)

**Exception**

```
one
    should not be
1
    but was
```

### Long

```cs
const long aLong = 1L;
aLong.ShouldNotBe(1);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldNotBeExamples.NumericLong.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldNotBeExamples.NumericLong.codeSample.approved.cs)

**Exception**

```
aLong
    should not be
1L
    but was
```

## DateTime(Offset)

`ShouldNotBe` DateTime overloads are similar to the numeric overloads and also support tolerances.

```cs
var date = new DateTime(2000, 6, 1);
date.ShouldNotBe(new(2000, 6, 1, 1, 0, 1), TimeSpan.FromHours(1.5));
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldNotBeExamples.DateTime.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldNotBeExamples.DateTime.codeSample.approved.cs)

**Exception**

```
date
    should not be within
01:30:00
    of
2000-06-01T01:00:01.0000000
    but was
2000-06-01T00:00:00.0000000
```

## TimeSpan

`TimeSpan` also has tolerance overloads

```cs
var timeSpan = TimeSpan.FromHours(1);
timeSpan.ShouldNotBe(timeSpan.Add(TimeSpan.FromHours(1.1d)), TimeSpan.FromHours(1.5d));
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldNotBeExamples.TimeSpanExample.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldNotBeExamples.TimeSpanExample.codeSample.approved.cs)

**Exception**

```
timeSpan
    should not be within
01:30:00
    of
02:06:00
    but was
01:00:00
```


# Null

`ShouldBeNull` and `ShouldNotBeNull` allow you to check whether a value is null.

`ShouldNotBeNull` returns the non-null value if it succeeds so that further assertions can be chained. When used with a reference type, the returned value is the same reference annotated as non-null. Equivalently, when used on a `System.Nullable<T>` expression, the returned value is the unwrapped `T` value.

## ShouldBeNull

```cs
var myRef = "Hello World";
myRef.ShouldBeNull();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeNullNotNullExamples.ShouldBeNull.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeNullNotNullExamples.ShouldBeNull.codeSample.approved.cs)

**Exception**

```
myRef
    should be null but was
"Hello World"
```

### ShouldBeNull (nullable value type)

```cs
int? nullableValue = 42;
nullableValue.ShouldBeNull();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeNullNotNullExamples.NullableValueShouldBeNull.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeNullNotNullExamples.NullableValueShouldBeNull.codeSample.approved.cs)

**Exception**

```
nullableValue
    should be null but was
42
```

## ShouldNotBeNull

```cs
string? myRef = null;
myRef.ShouldNotBeNull();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeNullNotNullExamples.ShouldNotBeNull.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeNullNotNullExamples.ShouldNotBeNull.codeSample.approved.cs)

**Exception**

```
myRef
    should not be null but was
```

### ShouldNotBeNull (nullable value type)

```cs
int? myRef = null;
myRef.ShouldNotBeNull();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeNullNotNullExamples.NullableValueShouldNotBeNull.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeNullNotNullExamples.NullableValueShouldNotBeNull.codeSample.approved.cs)

**Exception**

```
myRef
    should not be null but was
```

## ShouldNotBeNull with chaining

```cs
var myRef = (string?)"1234";
myRef.ShouldNotBeNull().Length.ShouldBe(5);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeNullNotNullExamples.ShouldNotBeNullWithChaining.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeNullNotNullExamples.ShouldNotBeNullWithChaining.codeSample.approved.cs)

**Exception**

```
myRef.ShouldNotBeNull().Length
    should be
5
    but was
4
```

### ShouldNotBeNull with chaining (nullable value type)

```cs
SomeStruct? nullableValue = new SomeStruct { IntProperty = 41 };
nullableValue.ShouldNotBeNull().IntProperty.ShouldBe(42);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeNullNotNullExamples.NullableValueShouldNotBeNullWithChaining.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeNullNotNullExamples.NullableValueShouldNotBeNullWithChaining.codeSample.approved.cs)

**Exception**

```
nullableValue.ShouldNotBeNull().IntProperty
    should be
42
    but was
41
```


# Bool

`ShouldBeTrue` and `ShouldBeFalse` work on boolean values.

## ShouldBeTrue

```cs
var myValue = false;
myValue.ShouldBeTrue();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeTrueFalseExamples.ShouldBeTrue.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeTrueFalseExamples.ShouldBeTrue.codeSample.approved.cs)

**Exception**

```
myValue
    should be
True
    but was
False
```

## ShouldBeFalse

```cs
var myValue = true;
myValue.ShouldBeFalse();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeTrueFalseExamples.ShouldBeFalse.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeTrueFalseExamples.ShouldBeFalse.codeSample.approved.cs)

**Exception**

```
myValue
    should be
False
    but was
True
```


# Flags

`ShouldHaveFlag` allows you to assert whether an object is an enum and has a flag specified.

Conversely `ShouldNotHaveFlag` allows you to assert the opposite; that an object is an enum but does not have a flag specified.

## ShouldHaveFlag

```cs
var actual = TestEnum.FlagTwo;
var value = TestEnum.FlagOne;
actual.ShouldHaveFlag(value);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldHaveFlagNotHaveFlagExamples.ShouldHaveFlag.codeSample.approved.cs#L1-L3) | [anchor](#snippet-ShouldHaveFlagNotHaveFlagExamples.ShouldHaveFlag.codeSample.approved.cs)

**Exception**

```
actual
    should have flag
TestEnum.FlagOne
    but had
TestEnum.FlagTwo
```

## ShouldNotHaveFlag

```cs
var actual = TestEnum.FlagOne;
var value = TestEnum.FlagOne;
actual.ShouldNotHaveFlag(value);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldHaveFlagNotHaveFlagExamples.ShouldNotHaveFlag.codeSample.approved.cs#L1-L3) | [anchor](#snippet-ShouldHaveFlagNotHaveFlagExamples.ShouldNotHaveFlag.codeSample.approved.cs)

**Exception**

```
actual
    should not have flag
TestEnum.FlagOne
    but it had
TestEnum.FlagOne
```


# AssignableTo

```cs
var theSimpsonsDog = new Person { Name = "Santas little helper" };
theSimpsonsDog.ShouldBeAssignableTo<Pet>();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeAssignableToExamples.ShouldBeAssignableTo.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeAssignableToExamples.ShouldBeAssignableTo.codeSample.approved.cs)

**Exception**

```
theSimpsonsDog
    should be assignable to
Simpsons.Pet
    but was
Simpsons.Person
```


# OfType

`ShouldBeOfType` is the inverse of `ShouldNotBeOfType`.

## ShouldBeOfType

```cs
var theSimpsonsDog = new Cat { Name = "Santas little helper" };
theSimpsonsDog.ShouldBeOfType<Dog>();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeOfTypeExamples.ShouldBeOfType.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeOfTypeExamples.ShouldBeOfType.codeSample.approved.cs)

**Exception**

```
theSimpsonsDog
    should be of type
Simpsons.Dog
    but was
Simpsons.Cat
```

## ShouldNotBeOfType

```cs
var theSimpsonsDog = new Cat { Name = "Santas little helper" };
theSimpsonsDog.ShouldNotBeOfType<Cat>();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeOfTypeExamples.ShouldNotBeOfType.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeOfTypeExamples.ShouldNotBeOfType.codeSample.approved.cs)

**Exception**

```
theSimpsonsDog
    should not be of type
Simpsons.Cat
    but was
Santas little helper
```


# OneOf

`ShouldNotBeOneOf` is the inverse of `ShouldBeOneOf`.

The candidates are passed as a single collection, not as individual arguments:

```cs
status.ShouldBeOneOf([Status.Active, Status.Pending]);
```

This was a `params` array in v4 — see the [4 to 5 upgrade guide](/documentation/4to5).

## ShouldBeOneOf

```cs
var apu = new Person { Name = "Apu" };
var homer = new Person { Name = "Homer" };
var skinner = new Person { Name = "Skinner" };
var barney = new Person { Name = "Barney" };
var theBeSharps = new List<Person> { homer, skinner, barney };
apu.ShouldBeOneOf(theBeSharps.ToArray());
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldBeOneOfExamples.ShouldBeOneOf.codeSample.approved.cs#L1-L6) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeOneOfExamples.ShouldBeOneOf.codeSample.approved.cs)

**Exception**

```
apu
    should be one of
[Homer, Skinner, Barney]
    but was
Apu
```

## ShouldNotBeOneOf

```cs
var apu = new Person { Name = "Apu" };
var homer = new Person { Name = "Homer" };
var skinner = new Person { Name = "Skinner" };
var barney = new Person { Name = "Barney" };
var wiggum = new Person { Name = "Wiggum" };
var theBeSharps = new List<Person> { apu, homer, skinner, barney, wiggum };
wiggum.ShouldNotBeOneOf(theBeSharps.ToArray());
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldBeOneOfExamples.ShouldNotBeOneOf.codeSample.approved.cs#L1-L7) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeOneOfExamples.ShouldNotBeOneOf.codeSample.approved.cs)

**Exception**

```
wiggum
    should not be one of
[Apu, Homer, Skinner, Barney, Wiggum]
    but was
Wiggum
```


# Greater/Less Than

`ShouldBeGreaterThan` is the inverse of `ShouldBeLessThan`.

## ShouldBeGreaterThan

```cs
var mrBurns = new Person { Name = "Mr. Burns", Salary = 30000 };
mrBurns.Salary.ShouldBeGreaterThan(300000000);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeGreater_LessThanExamples.ShouldBeGreaterThan.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeGreater_LessThanExamples.ShouldBeGreaterThan.codeSample.approved.cs)

**Exception**

```
mrBurns.Salary
    should be greater than
300000000
    but was
30000
```

## ShouldBeGreaterThanOrEqualTo

```cs
var mrBurns = new Person { Name = "Mr. Burns", Salary = 299999999 };
mrBurns.Salary.ShouldBeGreaterThanOrEqualTo(300000000);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeGreater_LessThanExamples.ShouldBeGreaterThanOrEqualTo.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeGreater_LessThanExamples.ShouldBeGreaterThanOrEqualTo.codeSample.approved.cs)

**Exception**

```
mrBurns.Salary
    should be greater than or equal to
300000000
    but was
299999999
```

## ShouldBeLessThan

```cs
var homer = new Person { Name = "Homer", Salary = 300000000 };
homer.Salary.ShouldBeLessThan(30000);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeGreater_LessThanExamples.ShouldBeLessThan.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeGreater_LessThanExamples.ShouldBeLessThan.codeSample.approved.cs)

**Exception**

```
homer.Salary
    should be less than
30000
    but was
300000000
```

## ShouldBeLessThanOrEqualTo

```cs
var homer = new Person { Name = "Homer", Salary = 30001 };
homer.Salary.ShouldBeLessThanOrEqualTo(30000);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeGreater_LessThanExamples.ShouldBeLessThanOrEqualTo.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeGreater_LessThanExamples.ShouldBeLessThanOrEqualTo.codeSample.approved.cs)

**Exception**

```
homer.Salary
    should be less than or equal to
30000
    but was
30001
```


# InRange

`ShouldBeInRange` is the inverse of `ShouldNotBeInRange`.

## ShouldBeInRange

```cs
var homer = new Person { Name = "Homer", Salary = 300000000 };
homer.Salary.ShouldBeInRange(30000, 40000);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeInRangeExamples.ShouldBeInRange.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeInRangeExamples.ShouldBeInRange.codeSample.approved.cs)

**Exception**

```
homer.Salary
    should be in range
{ from = 30000, to = 40000 }
    but was
300000000
```

## ShouldNotBeInRange

```cs
var mrBurns = new Person { Name = "Mr. Burns", Salary = 30000 };
mrBurns.Salary.ShouldNotBeInRange(30000, 40000);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeInRangeExamples.ShouldNotBeInRange.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldBeInRangeExamples.ShouldNotBeInRange.codeSample.approved.cs)

**Exception**

```
mrBurns.Salary
    should not be in range
{ from = 30000, to = 40000 }
    but was
30000
```


# MatchApproved

Based on the [ApprovalTest.Net](https://github.com/approvals/ApprovalTests.Net), Shouldly has `ShouldMatchApproved()` to do approval based testing. The main goal of Shouldly's approval testing is for it to be simple, intuitive and give great error messages.

To configure failed approvals to display a comparison of the approved and failed files, install the [Shouldly.DiffEngine](https://www.nuget.org/packages/Shouldly.DiffEngine/) nuget package and confgure it as follows:

```
// In your test setup
ShouldlyConfiguration.ShouldMatchApprovedDefaults.ConfigureDiffEngine();
```

## Approved File does not exist

When you first run a `ShouldMatchApproved` test, you will be presented with a diff viewer and a failing test.

```cs
var simpsonsQuote = "Hi Super Nintendo Chalmers";
simpsonsQuote.ShouldMatchApproved();
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldMatchApprovedExamples.ApprovedFileDoesNotExist.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldMatchApprovedExamples.ApprovedFileDoesNotExist.codeSample.approved.cs)

**Exception**

```
To approve the changes run this command:
copy /Y "C:\PathToCode\shouldly\src\DocumentationExamples\ShouldMatchApprovedExamples.ApprovedFileDoesNotExist.received.txt" "C:\PathToCode\shouldly\src\DocumentationExamples\ShouldMatchApprovedExamples.ApprovedFileDoesNotExist.approved.txt"
----------------------------

Approval file C:\PathToCode\shouldly\src\DocumentationExamples\ShouldMatchApprovedExamples.ApprovedFileDoesNotExist.approved.txt
    does not exist
```

**Screenshot**

![Initial diff.png](/files/Kv2Gz2pbC6PlT1eKifWr)

## Approved File does not match received

After you have approved the text, when it changes you get a different experience.

```cs
var simpsonsQuote = "Me fail english? That's unpossible";
simpsonsQuote.ShouldMatchApproved();
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldMatchApprovedExamples.ApprovedFileIsDifferent.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldMatchApprovedExamples.ApprovedFileIsDifferent.codeSample.approved.cs)

**Exception**

```
To approve the changes run this command:
copy /Y "C:\PathToCode\shouldly\src\DocumentationExamples\ShouldMatchApprovedExamples.ApprovedFileIsDifferent.received.txt" "C:\PathToCode\shouldly\src\DocumentationExamples\ShouldMatchApprovedExamples.ApprovedFileIsDifferent.approved.txt"
----------------------------

simpsonsQuote
    should match approved with options: Ignoring line endings
"Hi Super Nintendo Chalmers"
    but was
"Me fail english? That's unpossible"
    difference
Expected: "Hi Super Nintendo Chalmers"
Actual:   "Me fail english? That's unpossible"
```

**Screenshot**

![Changed diff.png](/files/MtSqXJSLTIBXa4I837sU)

## Options and customisation

While the defaults should work fine, often you need to customise things easily. ApprovalTests is highly configurable but the configuration is not always discoverable. Shouldly wants to make configuration simple and discoverable. This section covers the local customisations availble for a single ShouldMatchApproved call.

### Defaults

The first thing to note is that by default **Shouldly ignores line endings**. This saves painful failures on the build server when git checks out the approved files with `\n` rather than `\r\n` which the received file has. You can opt out of this behaviour for a single call, or globally. For global defaults see the Configuration section.

### Usage

```
toVerify.ShouldMatchApproved(configurationBuilder => configurationBuilder.OPTION())
```

Where OPTION can be one of the following methods.

### DoNotIgnoreLineEndings

Tells shouldly to use a line ending sensitive comparison.

```
toVerify.ShouldMatchApproved(c => c.DoNotIgnoreLineEndings())
```

### WithStringCompareOptions

Sets the string comparison options

```
var options = StringCompareShould.IgnoreCase | StringCompareShould.IgnoreLineEndings;
toVerify.ShouldMatchApproved(c => c.WithStringCompareOptions(options))
```

### WithDiscriminator

By default the approved and received files are named `{SourceFileName}.{MethodName}.approved.txt`, `WithDiscriminator` allows you to discriminate multiple files, useful for data driven tests which can have multiple executions of a single method. For example

```
[Fact]
public void Simpsons()
{
  toVerify.ShouldMatchApproved(c => c.WithDiscriminator("Bart"));
}
```

Will result in an approved file with the name `SimpsonsTests.Simpsons.Bart.approved.txt` (for a test in `SimpsonsTests.cs`)

### Diff

Opens the diff viewer if the files do not match. Requires the [Shouldly.DiffEngine](https://www.nuget.org/packages/Shouldly.DiffEngine/) package to be installed.

```csharp
toVerify.ShouldMatchApproved(c => c.Diff())
```

### NoDiff

Prevents the diff viewer from opening up. Doing this you can use Shouldly's error messages to verify the changes then run the command in the exception message to approve the changes.

```
toVerify.ShouldMatchApproved(c => c.NoDiff())
```

### WithFileExtension

Override the file extension of the approved/received files. The default is .txt.

```
toVerify.ShouldMatchApproved(c => c.WithFileExtension(".cs"))
```

### SubFolder

Put the approved/received files into a sub-directory

```
toVerify.ShouldMatchApproved(c => c.SubFolder("Approvals"))
```

### Wrapping ShouldMatchApproved in a helper

`ShouldMatchApproved` captures the calling test method at compile time via `[CallerMemberName]` and `[CallerFilePath]`, and uses them to name and place the approval files: a test named `MyTest` in `MyTests.cs` produces `MyTests.MyTest.received.txt` next to the source file.

When you wrap `ShouldMatchApproved` in a utility method, capture the same caller info on your helper and pass it through — otherwise the files are named after the helper:

```
[Fact]
public void MyTest()
{
    SomeUtilityMethod("Foo");
}

void SomeUtilityMethod(string toApprove,
    [CallerMemberName] string testMethodName = "",
    [CallerFilePath] string sourceFilePath = "")
{
    toApprove.ShouldMatchApproved(testMethodName: testMethodName, sourceFilePath: sourceFilePath);
}

// -> MyTests.MyTest.received.txt - without the pass-through the file would be called MyTests.SomeUtilityMethod.received.txt
```

Helpers nested more than one level deep forward the same two parameters at each level. This replaces the `UseCallerLocation()` and `LocateTestMethodUsingAttribute<T>()` options from Shouldly 4, which pointed the old stack-walking mechanism at the right frame — see the [4 to 5 upgrade guide](/documentation/4to5) for details.

### WithScrubber

Scrubbers allow you to remove dynamic content, such as the current date

```
toVerify.ShouldMatchApproved(c => c.WithScrubber(s => Regex.Replace(s, "\d{1,2}/\d{1,2}/\d{2,4}", "<date>"))
```

Will turn `Today is 01/01/2016` into `Today is <date>` in the received file.

## Configuration

### Changing default options

All of the instance based configuration can be changed globally through `ShouldlyConfiguration.ShouldMatchApprovedDefaults`. For example to make the default behaviour be line ending sensitive you can just run this before any tests execute `ShouldlyConfiguration.ShouldMatchApprovedDefaults.DoNotIgnoreLineEndings()`

### Diff tools

Shouldly.DiffEngine uses [DiffEngine](https://github.com/VerifyTests/DiffEngine) for launching diff tools. Use the following to configure enable the diff viewer when not disabled within DiffEngine:

```
// In your test setup
ShouldlyConfiguration.ShouldMatchApprovedDefaults.ConfigureDiffEngine();
```


# Enumerable

```cs
var apu = new Person { Name = "Apu" };
var homer = new Person { Name = "Homer" };
var skinner = new Person { Name = "Skinner" };
var barney = new Person { Name = "Barney" };
var theBeSharps = new List<Person> { homer, skinner, barney };
theBeSharps.ShouldBe(new[] { apu, homer, skinner, barney });
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/EnumerableShouldBeExamples.ShouldBe.codeSample.approved.cs#L1-L6) | [anchor](#snippet-EnumerableShouldBeExamples.ShouldBe.codeSample.approved.cs)

**Exception**

```
theBeSharps
    should be
[Apu, Homer, Skinner, Barney]
    but was
[Homer, Skinner, Barney]
    difference
[*Homer*, *Skinner*, *Barney*, *]
```


# SameAs

## ShouldBeSameAs

```cs
var principleSkinner = new Person { Name = "Armin Tamzarian" };
var seymourSkinner = new Person { Name = "Seymour Skinner" };
principleSkinner.ShouldBeSameAs(seymourSkinner);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeSameAsExamples.ShouldBeSameAs.codeSample.approved.cs#L1-L3) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeSameAsExamples.ShouldBeSameAs.codeSample.approved.cs)

**Exception**

```
principleSkinner
    should be same as
Seymour Skinner
    but was
Armin Tamzarian
```

## ShouldNotBeSameAs

```cs
var person = new Person { Name = "Armin Tamzarian" };
person.ShouldNotBeSameAs(person);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldBeSameAsExamples.ShouldNotBeSameAs.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeSameAsExamples.ShouldNotBeSameAs.codeSample.approved.cs)

**Exception**

```
person
    should not be same as
Armin Tamzarian
    but was
Armin Tamzarian
```


# String

```cs
var target = "Homer";
target.ShouldBe("Bart");
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldBe.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-StringExamples.ShouldBe.codeSample.approved.cs)

**Exception**

```
target
    should be
"Bart"
    but was
"Homer"
    difference
Expected: "Bart"
Actual:   "Homer"
```

> **A note on alignment.** The `▼`/`▲` markers point at the differing grapheme clusters using estimated terminal widths. On terminals or fonts that render emoji, CJK, or other wide characters at different widths than expected, markers may shift by a column. When the difference involves a combining mark, zero-width character, flag emoji, or right-to-left script, a `Difference at index N: U+XXXX vs U+YYYY` line is appended so the codepoints are unambiguous regardless of how your terminal renders the glyphs.

## ShouldNotBe

```cs
var target = "Bart";
target.ShouldNotBe("Bart");
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldNotBe.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-StringExamples.ShouldNotBe.codeSample.approved.cs)

**Exception**

```
target
    should not be
"Bart"
    but was
```


# ExampleClasses

The classes used in these samples are:

```cs
namespace Simpsons;

public abstract class Pet
{
    public abstract string? Name { get; set; }

    public override string? ToString() => Name;
}

public class Cat : Pet
{
    public override string? Name { get; set; }
}

public class Dog : Pet
{
    public override string? Name { get; set; }
}

public class Person
{
    public Person()
    {
    }

    public Person(string name)
    {
        Name = name ?? throw new ArgumentNullException(nameof(name));
    }

    public string? Name { get; set; }
    public int Salary { get; set; }

    public override string? ToString() => Name;
}
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/ExampleClasses.cs#L1-L35) | [anchor](#snippet-DocumentationExamples/ExampleClasses.cs)


# String


# Match

## ShouldMatch

```cs
var target = "Homer Simpson";
target.ShouldMatch("Bart .*");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldMatch.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldMatch.codeSample.approved.cs)

**Exception**

```
target
    should match
"Bart .*"
    but was
"Homer Simpson"
```

## ShouldNotMatch

```cs
var target = "Homer Simpson";
target.ShouldNotMatch("Homer .*");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldNotMatch.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldNotMatch.codeSample.approved.cs)

**Exception**

```
target should not match "Homer .*" but did
```


# Contain

## ShouldContain

```cs
var target = "Homer";
target.ShouldContain("Bart");
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldContain.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-StringExamples.ShouldContain.codeSample.approved.cs)

**Exception**

```
target
    should contain
"Bart"
    but was actually
"Homer"
```

## ShouldContainWithoutWhitespace

```cs
var target = "Homer Simpson";
target.ShouldContainWithoutWhitespace(" Bart Simpson ");
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldContainWithoutWhitespace.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-StringExamples.ShouldContainWithoutWhitespace.codeSample.approved.cs)

**Exception**

```
target
    should contain without whitespace
" Bart Simpson "
    but was actually
"Homer Simpson"
```

## ShouldNotContain

```cs
var target = "Homer";
target.ShouldNotContain("Home");
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldNotContain.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-StringExamples.ShouldNotContain.codeSample.approved.cs)

**Exception**

```
target
    should not contain
"Home"
    but was actually
"Homer"
```

## ShouldContainAll

```cs
var target = "Homer Simpson";
target.ShouldContainAll(["Homer", "Bart"]);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldContainAll.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-StringExamples.ShouldContainAll.codeSample.approved.cs)

**Exception**

```
target
    should contain all
"Homer, Bart"
    but was actually
"Homer Simpson"
```

## ShouldContainAny

```cs
var target = "Homer";
target.ShouldContainAny(["Bart", "Marge"]);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldContainAny.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-StringExamples.ShouldContainAny.codeSample.approved.cs)

**Exception**

```
target
    should contain any
"Bart, Marge"
    but was actually
"Homer"
```

## ShouldNotContainAll

```cs
var target = "Homer Simpson";
target.ShouldNotContainAll(["Homer", "Simpson"]);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldNotContainAll.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-StringExamples.ShouldNotContainAll.codeSample.approved.cs)

**Exception**

```
target
    should not contain all
"Homer, Simpson"
    but was actually
"Homer Simpson"
```

## ShouldNotContainAny

```cs
var target = "Homer";
target.ShouldNotContainAny(["Home", "Moe"]);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldNotContainAny.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-StringExamples.ShouldNotContainAny.codeSample.approved.cs)

**Exception**

```
target
    should not contain any
"Home, Moe"
    but was actually
"Homer"
```


# Null and Empty

## ShouldBeNull

```cs
var target = "Homer";
target.ShouldBeNull();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldBeNull.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldBeNull.codeSample.approved.cs)

**Exception**

```
target
    should be null but was
"Homer"
```

## ShouldBeNullOrEmpty

```cs
var target = "Homer";
target.ShouldBeNullOrEmpty();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldBeNullOrEmpty.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldBeNullOrEmpty.codeSample.approved.cs)

**Exception**

```
target ("Homer")
    should be null or empty
```

## ShouldBeEmpty

```cs
var target = "Homer";
target.ShouldBeEmpty();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldBeEmpty.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldBeEmpty.codeSample.approved.cs)

**Exception**

```
target
    should be empty but was
"Homer"
```

## ShouldNotBeNull

```cs
string? target = null;
target.ShouldNotBeNull();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldNotBeNull.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldNotBeNull.codeSample.approved.cs)

**Exception**

```
target
    should not be null but was
```

## ShouldNotBeNullOrEmpty

```cs
var target = "";
target.ShouldNotBeNullOrEmpty();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldNotBeNullOrEmpty.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldNotBeNullOrEmpty.codeSample.approved.cs)

**Exception**

```
target ("")
    should not be null or empty
```

## ShouldNotBeEmpty

```cs
var target = "";
target.ShouldNotBeNullOrEmpty();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldNotBeNullOrEmpty.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldNotBeNullOrEmpty.codeSample.approved.cs)

**Exception**

```
target
    should not be empty but was
```


# StartWith

## ShouldStartWith

```cs
var target = "Homer";
target.ShouldStartWith("Bart");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldStartWith.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldStartWith.codeSample.approved.cs)

**Exception**

```
target
    should start with
"Bart"
    but was
"Homer"
```

## ShouldNotStartWith

```cs
var target = "Homer Simpson";
target.ShouldNotStartWith("Homer");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldNotStartWith.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldNotStartWith.codeSample.approved.cs)

**Exception**

```
target
    should not start with
"Homer"
    but was
"Homer Simpson"
```


# EndWith

## ShouldEndWith

```cs
var target = "Homer";
target.ShouldEndWith("Bart");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldEndWith.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldEndWith.codeSample.approved.cs)

**Exception**

```
target
    should end with
"Bart"
    but was
"Homer"
```

## ShouldNotEndWith

```cs
var target = "Homer Simpson";
target.ShouldNotEndWith("Simpson");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/StringExamples.ShouldNotEndWith.codeSample.approved.cs#L1-L2) | [anchor](#snippet-StringExamples.ShouldNotEndWith.codeSample.approved.cs)

**Exception**

```
target
    should not end with
"Simpson"
    but was
"Homer Simpson"
```


# Enumerable


# All

```cs
var mrBurns = new Person { Name = "Mr.Burns", Salary = 3000000 };
var kentBrockman = new Person { Name = "Homer", Salary = 3000000 };
var homer = new Person { Name = "Homer", Salary = 30000 };
var millionaires = new List<Person> { mrBurns, kentBrockman, homer };
millionaires.ShouldAllBe(m => m.Salary > 1000000);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/EnumerableShouldAllBeExamples.ShouldAllBe.codeSample.approved.cs#L1-L5) | [anchor](#snippet-EnumerableShouldAllBeExamples.ShouldAllBe.codeSample.approved.cs)

**Exception**

```
millionaires
    should satisfy the condition
(m.Salary > 1000000)
    but
[Homer]
    do not
```


# Empty

## ShouldBeEmpty

```cs
var homer = new Person { Name = "Homer" };
var powerPlantOnTheWeekend = new List<Person> { homer };
powerPlantOnTheWeekend.ShouldBeEmpty();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/EnumerableShouldBeEmptyExamples.ShouldBeEmpty.codeSample.approved.cs#L1-L3) | [anchor](#snippet-EnumerableShouldBeEmptyExamples.ShouldBeEmpty.codeSample.approved.cs)

**Exception**

```
powerPlantOnTheWeekend
    should be empty but had
1
    item and was
[Homer]
```

## ShouldNotBeEmpty

```cs
var moesTavernOnTheWeekend = new List<Person>();
moesTavernOnTheWeekend.ShouldNotBeEmpty();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/EnumerableShouldBeEmptyExamples.ShouldNotBeEmpty.codeSample.approved.cs#L1-L2) | [anchor](#snippet-EnumerableShouldBeEmptyExamples.ShouldNotBeEmpty.codeSample.approved.cs)

**Exception**

```
moesTavernOnTheWeekend
    should not be empty but was
```


# OneOf

The candidates are passed as a single collection, not as individual arguments:

```cs
status.ShouldBeOneOf([Status.Active, Status.Pending]);
```

This was a `params` array in v4 — see the [4 to 5 upgrade guide](/documentation/4to5).

## ShouldBeOneOf

```cs
var apu = new Person { Name = "Apu" };
var homer = new Person { Name = "Homer" };
var skinner = new Person { Name = "Skinner" };
var barney = new Person { Name = "Barney" };
var theBeSharps = new List<Person> { homer, skinner, barney };
apu.ShouldBeOneOf(theBeSharps.ToArray());
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldBeOneOfExamples.ShouldBeOneOf.codeSample.approved.cs#L1-L6) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeOneOfExamples.ShouldBeOneOf.codeSample.approved.cs)

**Exception**

```
apu
    should be one of
[Homer, Skinner, Barney]
    but was
Apu
```

## ShouldNotBeOneOf

```cs
var apu = new Person { Name = "Apu" };
var homer = new Person { Name = "Homer" };
var skinner = new Person { Name = "Skinner" };
var barney = new Person { Name = "Barney" };
var wiggum = new Person { Name = "Wiggum" };
var theBeSharps = new List<Person> { apu, homer, skinner, barney, wiggum };
wiggum.ShouldNotBeOneOf(theBeSharps.ToArray());
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldBeOneOfExamples.ShouldNotBeOneOf.codeSample.approved.cs#L1-L7) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldBeOneOfExamples.ShouldNotBeOneOf.codeSample.approved.cs)

**Exception**

```
wiggum
    should not be one of
[Apu, Homer, Skinner, Barney, Wiggum]
    but was
Wiggum
```


# Contain

## ShouldContain

```cs
var mrBurns = new Person { Name = "Mr.Burns", Salary = 3000000 };
var kentBrockman = new Person { Name = "Kent Brockman", Salary = 3000000 };
var homer = new Person { Name = "Homer", Salary = 30000 };
var millionaires = new List<Person> { kentBrockman, homer };
millionaires.ShouldContain(mrBurns);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/EnumerableShouldContainExamples.ShouldContain.codeSample.approved.cs#L1-L5) <sup>|</sup> [<sup>anchor</sup>](#snippet-EnumerableShouldContainExamples.ShouldContain.codeSample.approved.cs)

**Exception**

```
millionaires
    should contain
Mr.Burns
    but was actually
[Kent Brockman, Homer]
```

### With Predicate

```cs
var homer = new Person { Name = "Homer", Salary = 30000 };
var moe = new Person { Name = "Moe", Salary = 20000 };
var barney = new Person { Name = "Barney", Salary = 0 };
var millionaires = new List<Person> { homer, moe, barney };
millionaires.ShouldContain(m => m.Salary > 1000000);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/EnumerableShouldContainExamples.ShouldContain_Predicate.codeSample.approved.cs#L1-L5) <sup>|</sup> [<sup>anchor</sup>](#snippet-EnumerableShouldContainExamples.ShouldContain_Predicate.codeSample.approved.cs)

**Exception**

```
millionaires
    should contain an element satisfying the condition
(m.Salary > 1000000)
    but does not
```

## ShouldNotContain

```cs
var homerSimpson = new Person { Name = "Homer" };
var homerGlumplich = new Person { Name = "Homer" };
var lenny = new Person { Name = "Lenny" };
var carl = new Person { Name = "carl" };
var clubOfNoHomers = new List<Person> { homerSimpson, homerGlumplich, lenny, carl };
clubOfNoHomers.ShouldNotContain(homerSimpson);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/EnumerableShouldNotContainExamples.ShouldNotContain.codeSample.approved.cs#L1-L6) <sup>|</sup> [<sup>anchor</sup>](#snippet-EnumerableShouldNotContainExamples.ShouldNotContain.codeSample.approved.cs)

**Exception**

```
clubOfNoHomers
    should not contain
Homer
    but was actually
[Homer, Homer, Lenny, carl]
```

### With Predicate

```cs
var mrBurns = new Person { Name = "Mr.Burns", Salary = 3000000 };
var kentBrockman = new Person { Name = "Homer", Salary = 3000000 };
var homer = new Person { Name = "Homer", Salary = 30000 };
var millionaires = new List<Person> { mrBurns, kentBrockman, homer };
millionaires.ShouldNotContain(m => m.Salary < 1000000);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/EnumerableShouldNotContainExamples.ShouldNotContain_Predicate.codeSample.approved.cs#L1-L5) <sup>|</sup> [<sup>anchor</sup>](#snippet-EnumerableShouldNotContainExamples.ShouldNotContain_Predicate.codeSample.approved.cs)

**Exception**

```
millionaires
    should not contain an element satisfying the condition
(m.Salary < 1000000)
    but
[Homer]
    do
```


# Unique

```cs
var lisa = new Person { Name = "Lisa" };
var bart = new Person { Name = "Bart" };
var maggie = new Person { Name = "Maggie" };
var simpsonsKids = new List<Person> { bart, lisa, maggie, maggie };
simpsonsKids.ShouldBeUnique();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/EnumerableShouldBeUniqueExamples.ShouldBeUnique.codeSample.approved.cs#L1-L5) | [anchor](#snippet-EnumerableShouldBeUniqueExamples.ShouldBeUnique.codeSample.approved.cs)

**Exception**

```
simpsonsKids
    should be unique but
[Maggie]
    was duplicated
```


# SubsetOf

```cs
var lisa = new Person { Name = "Lisa" };
var bart = new Person { Name = "Bart" };
var maggie = new Person { Name = "Maggie" };
var homer = new Person { Name = "Homer" };
var marge = new Person { Name = "Marge" };
var ralph = new Person { Name = "Ralph" };
var simpsonsKids = new List<Person> { bart, lisa, maggie, ralph };
var simpsonsFamily = new List<Person> { lisa, bart, maggie, homer, marge };
simpsonsKids.ShouldBeSubsetOf(simpsonsFamily);
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/EnumerableShouldBeSubsetOfExamples.ShouldBeSubsetOf.codeSample.approved.cs#L1-L9) | [anchor](#snippet-EnumerableShouldBeSubsetOfExamples.ShouldBeSubsetOf.codeSample.approved.cs)

**Exception**

```
simpsonsKids
    should be subset of
[Lisa, Bart, Maggie, Homer, Marge]
    but
[Ralph]
    is outside subset
```


# Have

## ShouldHaveCount

```cs
var maggie = new Person { Name = "Maggie" };
var homer = new Person { Name = "Homer" };
var simpsonsBabies = new List<Person> { homer, maggie };
simpsonsBabies.ShouldHaveCount(3);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/EnumerableShouldHaveCountExamples.ShouldHaveCount.codeSample.approved.cs#L1-L4) <sup>|</sup> [<sup>anchor</sup>](#snippet-EnumerableShouldHaveCountExamples.ShouldHaveCount.codeSample.approved.cs)

**Exception**

```
simpsonsBabies
    should have 3 items but had
2
    items and was
[Homer, Maggie]
```

## ShouldHaveSingleItem

```cs
var maggie = new Person { Name = "Maggie" };
var homer = new Person { Name = "Homer" };
var simpsonsBabies = new List<Person> { homer, maggie };
simpsonsBabies.ShouldHaveSingleItem();
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/EnumerableShouldHaveSingleItemExamples.ShouldHaveSingleItem.codeSample.approved.cs#L1-L4) <sup>|</sup> [<sup>anchor</sup>](#snippet-EnumerableShouldHaveSingleItemExamples.ShouldHaveSingleItem.codeSample.approved.cs)

**Exception**

```
simpsonsBabies
    should have single item but had
2
    items and was
[Homer, Maggie]
```


# Dictionary


# ContainKey

## ShouldContainKey

```cs
var websters = new Dictionary<string, string> { { "Embiggen", "To empower or embolden." } };
websters.ShouldContainKey("Cromulent");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/DictionaryShouldContainKeyExamples.ShouldContainKey.codeSample.approved.cs#L1-L2) | [anchor](#snippet-DictionaryShouldContainKeyExamples.ShouldContainKey.codeSample.approved.cs)

**Exception**

```
websters
    should contain key
"Cromulent"
    but does not
```

## ShouldNotContainKey

```cs
var websters = new Dictionary<string, string> { { "Chazzwazzers", "What Australians would have called a bull frog." } };
websters.ShouldNotContainKey("Chazzwazzers");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/DictionaryShouldContainKeyExamples.ShouldNotContainKey.codeSample.approved.cs#L1-L2) | [anchor](#snippet-DictionaryShouldContainKeyExamples.ShouldNotContainKey.codeSample.approved.cs)

**Exception**

```
websters
    should not contain key
"Chazzwazzers"
    but does
```


# ContainKeyAndValue

## ShouldContainKeyAndValue

```cs
var websters = new Dictionary<string, string> { { "Cromulent", "I never heard the word before moving to Springfield." } };
websters.ShouldContainKeyAndValue("Cromulent", "Fine, acceptable.");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/DictionaryShouldContainKeyAndValueExamples.ShouldContainKeyAndValue.codeSample.approved.cs#L1-L2) | [anchor](#snippet-DictionaryShouldContainKeyAndValueExamples.ShouldContainKeyAndValue.codeSample.approved.cs)

**Exception**

```
websters
    should contain key
"Cromulent"
    with value
"Fine, acceptable."
    but value was
"I never heard the word before moving to Springfield."
```

## ShouldNotContainKeyAndValue

```cs
var websters = new Dictionary<string, string> { { "Chazzwazzers", "What Australians would have called a bull frog." } };
websters.ShouldNotContainValueForKey("Chazzwazzers", "What Australians would have called a bull frog.");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/DictionaryShouldContainKeyAndValueExamples.ShouldNotContainKeyAndValue.codeSample.approved.cs#L1-L2) | [anchor](#snippet-DictionaryShouldContainKeyAndValueExamples.ShouldNotContainKeyAndValue.codeSample.approved.cs)

**Exception**

```
websters
    should not contain key
"Chazzwazzers"
    with value
"What Australians would have called a bull frog."
    but does
```


# Exceptions


# Throw

## ShouldThrowAction

```cs
var homer = new Person { Name = "Homer", Salary = 30000 };
var denominator = 1;
Should.Throw<DivideByZeroException>(() =>
                {
                    var y = homer.Salary / denominator;
                });
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldThrowExamples.ShouldThrowAction.codeSample.approved.cs#L1-L6) | [anchor](#snippet-ShouldThrowExamples.ShouldThrowAction.codeSample.approved.cs)

**Exception**

```
`var y = homer.Salary / denominator;`
    should throw
System.DivideByZeroException
    but did not
```

## ShouldThrowAsync

```cs
Task doSomething() => Task.CompletedTask;
var exception = await Should.ThrowAsync<DivideByZeroException>(() => doSomething());
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/Shouldly.Tests/ShouldThrowAsync/FuncOfTaskScenarioAsync.cs#L91-L95) | [anchor](#snippet-ShouldThrowAsync)

**Exception**

Task `doSomething()` should throw System.DivideByZeroException but did not

## ShouldThrow Action Extension

```cs
var homer = new Person { Name = "Homer", Salary = 30000 };
var denominator = 1;
var action = () =>
                {
                    var y = homer.Salary / denominator;
                };
action.ShouldThrow<DivideByZeroException>();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldThrowExamples.ShouldThrowActionExtension.codeSample.approved.cs#L1-L7) | [anchor](#snippet-ShouldThrowExamples.ShouldThrowActionExtension.codeSample.approved.cs)

**Exception**

```
`action()`
    should throw
System.DivideByZeroException
    but did not
```

## ShouldThrowFunc

```cs
Should.Throw<ArgumentNullException>(() => new Person("Homer"));
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldThrowExamples.ShouldThrowFunc.codeSample.approved.cs#L1-L1) | [anchor](#snippet-ShouldThrowExamples.ShouldThrowFunc.codeSample.approved.cs)

**Exception**

```
`new Person("Homer")`
    should throw
System.ArgumentNullException
    but did not
```

## ShouldThrow Func Extension

```cs
var func = () => new Person("Homer");
func.ShouldThrow<ArgumentNullException>();
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldThrowExamples.ShouldThrowFuncExtension.codeSample.approved.cs#L1-L2) | [anchor](#snippet-ShouldThrowExamples.ShouldThrowFuncExtension.codeSample.approved.cs)

**Exception**

```
`func()`
    should throw
System.ArgumentNullException
    but did not
```

## ShouldThrowFuncOfTask

```cs
var homer = new Person { Name = "Homer", Salary = 30000 };
var denominator = 1;
Should.Throw<DivideByZeroException>(() =>
                {
                    var task = Task.Factory.StartNew(
                        () =>
                        {
                            var y = homer.Salary / denominator;
                        });
                    return task;
                });
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldThrowExamples.ShouldThrowFuncOfTask.codeSample.approved.cs#L1-L11) | [anchor](#snippet-ShouldThrowExamples.ShouldThrowFuncOfTask.codeSample.approved.cs)

**Exception**

```
Task `var task = Task.Factory.StartNew( () => { var y = homer.Salary / denominator; }); return task;`
    should throw
System.DivideByZeroException
    but did not
```


# NotThrow

## ShouldNotThrowAction

```cs
var homer = new Person { Name = "Homer", Salary = 30000 };
var denominator = 0;
Should.NotThrow(() =>
                {
                    var y = homer.Salary / denominator;
                });
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldNotThrowExamples.ShouldNotThrowAction.codeSample.approved.cs#L1-L6) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldNotThrowExamples.ShouldNotThrowAction.codeSample.approved.cs)

**Exception**

```
`var y = homer.Salary / denominator;`
    should not throw but threw
System.DivideByZeroException
    with message
"Attempted to divide by zero."
```

## ShouldNotThrow Action Extension

```cs
var homer = new Person { Name = "Homer", Salary = 30000 };
var denominator = 0;
var action = () =>
                {
                    var y = homer.Salary / denominator;
                };
action.ShouldNotThrow();
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldNotThrowExamples.ShouldNotThrowActionExtension.codeSample.approved.cs#L1-L7) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldNotThrowExamples.ShouldNotThrowActionExtension.codeSample.approved.cs)

**Exception**

```
`action()`
    should not throw but threw
System.DivideByZeroException
    with message
"Attempted to divide by zero."
```

## ShouldNotThrow specific exception

`ShouldNotThrow<TException>` verifies that a specific exception type is not thrown. Exceptions of other types are not caught and will propagate to the caller.

```cs
var homer = new Person { Name = "Homer", Salary = 30000 };
var denominator = 0;
Should.NotThrow<DivideByZeroException>(() =>
                {
                    var y = homer.Salary / denominator;
                });
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldNotThrowExamples.ShouldNotThrowSpecificException.codeSample.approved.cs#L1-L6) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldNotThrowExamples.ShouldNotThrowSpecificException.codeSample.approved.cs)

**Exception**

```
`var y = homer.Salary / denominator;`
    should not throw
System.DivideByZeroException
    but did, with message
"Attempted to divide by zero."
```

## ShouldNotThrowFunc

```cs
string? name = null;
Should.NotThrow(() => new Person(name!));
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldNotThrowExamples.ShouldNotThrowFunc.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldNotThrowExamples.ShouldNotThrowFunc.codeSample.approved.cs)

**Exception**

```
`new Person(name!)`
    should not throw but threw
System.ArgumentNullException
    with message
"Value cannot be null. (Parameter 'name')"
```

## ShouldNotThrow Func Extension

```cs
string? name = null;
var func = () => new Person(name!);
func.ShouldNotThrow();
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldNotThrowExamples.ShouldNotThrowFuncExtension.codeSample.approved.cs#L1-L3) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldNotThrowExamples.ShouldNotThrowFuncExtension.codeSample.approved.cs)

**Exception**

```
`func()`
    should not throw but threw
System.ArgumentNullException
    with message
"Value cannot be null. (Parameter 'name')"
```

## ShouldNotThrowFuncOfTask

```cs
var homer = new Person { Name = "Homer", Salary = 30000 };
var denominator = 0;
Should.NotThrow(() =>
                {
                    var task = Task.Factory.StartNew(
                        () =>
                        {
                            var y = homer.Salary / denominator;
                        });
                    return task;
                });
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/tree/master/src/DocumentationExamples/CodeExamples/ShouldNotThrowExamples.ShouldNotThrowFuncOfTask.codeSample.approved.cs#L1-L11) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldNotThrowExamples.ShouldNotThrowFuncOfTask.codeSample.approved.cs)

**Exception**

```
`var task = Task.Factory.StartNew( () => { var y = homer.Salary / denominator; }); return task;`
    should not throw but threw
System.DivideByZeroException
    with message
"Attempted to divide by zero."
```


# SatisfyAllConditions

Asserts that every one of the supplied conditions holds, reporting *all* the failures at once rather than stopping at the first.

Which form to use:

* **All the assertions hang off a common subject** → use the `value.ShouldSatisfy([...])` extension method. The value under test is passed to each condition, and it appears as the subject in the failure message.
* **A group of otherwise unrelated assertions** → use the static `Should.Satisfy([...])` method. There is no single subject to pass, so each condition is a self-contained assertion.

> `ShouldSatisfyAllConditions` is the original name for this assertion. It still works but is now obsolete: it cannot capture the asserted expression via `CallerArgumentExpression` and falls back to stack-trace parsing, which is not trim- or AOT-safe. Prefer `ShouldSatisfy` / `Should.Satisfy`.

## A common subject — `ShouldSatisfy`

```cs
var mrBurns = new Person { Name = null };
mrBurns.ShouldSatisfy(
                [
                    p => p.Name.ShouldNotBeNullOrEmpty(),
                    p => p.Name.ShouldBe("Mr.Burns")
                ]);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldSatisfyAllConditionsExamples.ShouldSatisfy.codeSample.approved.cs#L1-L6) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldSatisfyAllConditionsExamples.ShouldSatisfy.codeSample.approved.cs)

**Exception**

```
mrBurns
    should satisfy all the conditions specified, but does not.
The following errors were found ...
---------------- Error 1 ----------------
    p.Name (null)
        should not be null or empty

---------------- Error 2 ----------------
    p.Name
        should be
    "Mr.Burns"
        but was
    null

-----------------------------------------
```

## Unrelated conditions — `Should.Satisfy`

```cs
var mrBurns = new Person { Name = null };
var homer = new Person { Name = "Homer" };
Should.Satisfy(
                [
                    () => mrBurns.Name.ShouldNotBeNullOrEmpty(),
                    () => homer.Name.ShouldBe("Mr.Burns")
                ]);
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldSatisfyAllConditionsExamples.Satisfy.codeSample.approved.cs#L1-L7) <sup>|</sup> [<sup>anchor</sup>](#snippet-ShouldSatisfyAllConditionsExamples.Satisfy.codeSample.approved.cs)

**Exception**

```
The conditions specified should all be satisfied, but were not.
The following errors were found ...
---------------- Error 1 ----------------
    mrBurns.Name (null)
        should not be null or empty

---------------- Error 2 ----------------
    homer.Name
        should be
    "Mr.Burns"
        but was
    "Homer"
        difference
    Expected: "Mr.Burns"
    Actual:   "Homer"

-----------------------------------------
```


# CompleteIn

```cs
Should.CompleteIn(
                    action: () => { Thread.Sleep(TimeSpan.FromSeconds(15)); },
                    timeout: TimeSpan.FromSeconds(0.5),
                    customMessage: "Some additional context");
```

[snippet source](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/ShouldCompleteInExamples.ShouldCompleteIn.codeSample.approved.cs#L1-L4) | [anchor](#snippet-ShouldCompleteInExamples.ShouldCompleteIn.codeSample.approved.cs)

**Exception**

```

Delegate
    should complete in
00:00:00.5000000
    but did not

Additional Info:
    Some additional context
```


# DynamicShould

## HaveProperty

```cs
dynamic theFuture = new ExpandoObject();
DynamicShould.HaveProperty(() => theFuture, "RobotTeachers");
```

[<sup>snippet source</sup>](https://github.com/shouldly/shouldly/blob/master/src/DocumentationExamples/CodeExamples/DynamicShouldExamples.HaveProperty.codeSample.approved.cs#L1-L2) <sup>|</sup> [<sup>anchor</sup>](#snippet-DynamicShouldExamples.HaveProperty.codeSample.approved.cs)

**Exception**

```
Dynamic object "theFuture" should contain property "RobotTeachers" but does not.
```


# Writing Custom Assertions

Shouldly is designed to be extended with your own domain-specific assertions. Since v5 a custom assertion is just an extension method — no attributes or registration required. This page covers the patterns, from simplest to most control.

## The anatomy of an assertion

Shouldly's error messages read like a sentence built from three parts: the source text of the value being asserted, the assertion method's name, and the expected/actual values:

```
person.Manager
    should be awesome
"an awesome person"
    but was
null
```

The first line is the **caller argument expression** — the literal source text `person.Manager`, captured at compile time by `[CallerArgumentExpression]`. The second line is the assertion method's name (`ShouldBeAwesome` → "should be awesome"), captured by `[CallerMemberName]`. Your custom assertion gets all of this for free as long as it declares and forwards the right parameters.

## The basic pattern

A custom assertion method needs three things: the `this` parameter being asserted, an optional `customMessage`, and a `[CallerArgumentExpression]` parameter that captures the call-site text:

```csharp
public static class CustomAssertions
{
    public static void ShouldBeAwesome(
        this Person? actual,
        string? customMessage = null,
        [CallerArgumentExpression(nameof(actual))] string? actualExpression = null)
    {
        if (actual is not { IsAwesome: true })
            throw new ShouldAssertException(
                new ExpectedActualShouldlyMessage("an awesome person", actual,
                    customMessage, actualExpression: actualExpression).ToString());
    }
}
```

> **Pass `actualExpression` by name.** The message constructors take a `[CallerMemberName]` parameter *before* `actualExpression` — passing it positionally silently clobbers the method name instead. Always write `actualExpression: actualExpression`.

The message classes cover the common shapes:

| Class                                              | Message shape                        |
| -------------------------------------------------- | ------------------------------------ |
| `ExpectedShouldlyMessage`                          | `x should … expected but does not`   |
| `ActualShouldlyMessage`                            | `x should … but was actual`          |
| `ExpectedActualShouldlyMessage`                    | `x should … expected but was actual` |
| `ExpectedActualToleranceShouldlyMessage`           | adds a tolerance line                |
| `ExpectedActualWithCaseSensitivityShouldlyMessage` | adds case-sensitivity wording        |

All take the failing method's name via `[CallerMemberName]`, so constructing them directly inside your assertion produces correctly-worded messages. Note that the method name also drives wording: a name containing `Not` is phrased as a negated assertion.

## Composing existing assertions

Often a custom assertion is just a bundle of existing ones. Forward your captured expression into the inner calls so the failure message points at the *caller's* code rather than your helper's parameter:

```csharp
public static void ShouldBeValidOrder(
    this Order? actual,
    string? customMessage = null,
    [CallerArgumentExpression(nameof(actual))] string? actualExpression = null)
{
    actual.ShouldNotBeNull(customMessage, actualExpression: actualExpression);
    actual.Lines.ShouldNotBeEmpty(customMessage, actualExpression: $"{actualExpression}.Lines");
    actual.Total.ShouldBeGreaterThan(0m, customMessage, actualExpression: $"{actualExpression}.Total");
}
```

Without the forwarding, a failure inside `ShouldBeValidOrder(order)` would report the expression as `actual` — with it, the message says `order.Total`.

## Predicate-style assertions with `AssertAwesomely`

For assert-a-predicate cases, `ShouldlyCoreExtensions.AssertAwesomely` wraps the throw-a-message dance:

```csharp
public static void ShouldBePositive(
    this Money actual,
    string? customMessage = null,
    [CallerArgumentExpression(nameof(actual))] string? actualExpression = null)
{
    actual.AssertAwesomely(m => m.Amount > 0, actual, "a positive amount",
        customMessage, actualExpression: actualExpression);
}
```

## Verifying your wiring with the trip-wire

Forgetting the `[CallerArgumentExpression]` parameter — or forgetting to forward it — does not fail any test; it just silently degrades your failure messages. Shouldly ships the guard it uses on its own test suite: arm `ShouldlyConfiguration.AssertCallerArgumentExpressionIsUsed()` once for the test run, and any Shouldly message built without a captured expression throws `InvalidOperationException` instead of degrading:

```csharp
internal static class ModuleInitializer
{
    [ModuleInitializer]
    internal static void Initialize() =>
        _ = ShouldlyConfiguration.AssertCallerArgumentExpressionIsUsed();
}
```

Call sites that legitimately cannot use caller argument expressions (e.g. assertions invoked through `dynamic`) can opt out locally with `using (ShouldlyConfiguration.AllowStackWalking()) { … }`.

## Wrapping `ShouldMatchApproved`

`ShouldMatchApproved` names and places its approval files using the calling test method, captured via `[CallerMemberName]` and `[CallerFilePath]`. A helper that wraps it must capture those itself and pass them through, otherwise the files are named after the helper:

```csharp
public static void ShouldMatchMySnapshot(
    this string actual,
    [CallerMemberName] string testMethodName = "",
    [CallerFilePath] string sourceFilePath = "") =>
    actual.ShouldMatchApproved(c => c.WithScrubber(Scrub),
        testMethodName: testMethodName, sourceFilePath: sourceFilePath);
```

## Polish

Two attributes Shouldly applies to its own assertion classes are worth copying:

* `[DebuggerStepThrough]` on the class keeps the debugger from stepping into assertion internals when a test fails.
* `[EditorBrowsable(EditorBrowsableState.Never)]` hides the static class from IntelliSense while leaving the extension methods visible on the asserted values.

### Do I still need `[ShouldlyMethods]`?

Not on modern targets. The attribute only matters to the legacy stack-walking fallback, which runs solely for `netstandard2.0` consumers whose compiler does not supply `[CallerArgumentExpression]` values. If your assertion library multi-targets and supports such consumers, keep `[ShouldlyMethods]` on the class so the fallback can skip your frames; otherwise omit it.


# Upgrade 3 to 4

This is a work in progress. Please send a PR with any amendments.

Also see the [4.0 milestone](https://github.com/shouldly/shouldly/milestone/2?closed=1).

## Class constraint added to `ShouldNotBeNull`

In previous versions it was possible to assert that a non-nullable type `ShouldNotBeNull`, even though this logically makes no sense. For exmaple, the following would happily compile, but of course could never cause a test failure:

```
const long value = 1;

value.ShouldNotBeNull();
```

The `class` constraint was added to `ShouldNotBeNull` in v4, which means the above code will no longer compile. This is a good thing because it allows you to find and fix nonsensical tests in your codebase!

## `Func<string> customMessage` removed

All overloads that accepted a `Func<string> customMessage` have been changed to `string customMessage`.

## Diff tool functionality moved to DiffEngine

Diff tool functionality is now provided by [DiffEngine](https://github.com/VerifyTests/DiffEngine).

As such, the following APIs have been removed:

* `Shouldly.Configuration.DiffTool`
* `ShouldlyConfiguration.DiffTools`
* `Shouldly.Configuration.IShouldNotLaunchDiffTool`
* `Shouldly.Configuration.DiffToolConfiguration`
* `Shouldly.Configuration.KnownDiffTools`
* `Shouldly.Configuration.KnownDoNotLaunchStrategies`


# Upgrade 4 to 5

This is a work in progress. Please send a PR with any amendments.

## `ShouldBeOneOf` and friends take a collection, not `params`

`ShouldBeOneOf`, `ShouldNotBeOneOf`, and `ShouldBeOfTypes` were variadic in v4. In v5 they take their candidates as a single collection, so call sites passing individual values fail with **`CS1503`**:

```csharp
// v4 — compiles
result.ShouldBeOneOf(Status.Active, Status.Pending, Status.Closed);

// v5 — error CS1503: Argument 2: cannot convert from 'Status' to 'Status[]'
result.ShouldBeOneOf([Status.Active, Status.Pending, Status.Closed]);
```

Wrap the values in a collection expression, or in `new[] { … }` below C# 12. Call sites that already pass an array or a collection expression are unaffected.

This is the most widely hit compile break in the upgrade — `ShouldBeOneOf(a, b, c)` was the canonical v4 spelling — but it is a purely mechanical fix and the compiler points at every occurrence.

The `params` array was dropped because a `params` parameter must come last, which leaves no room for the `[CallerArgumentExpression]` parameter these assertions now carry. That parameter is why the failure message reports your source text:

```
result
    should be one of
[Status.Active, Status.Pending, Status.Closed]
    but was
Status.Draft
```

## String assertions default to case-sensitive comparison

`ShouldContain`, `ShouldNotContain`, `ShouldStartWith`, `ShouldEndWith`, `ShouldNotStartWith`, and `ShouldNotEndWith` defaulted to `Case.Insensitive` in v4, while `ShouldBe` compared case-sensitively. In v5 all string assertions are case-sensitive by default, aligning with `ShouldBe`, C# string semantics, and other assertion libraries. `Case.Insensitive` remains available as an opt-in.

## String failure messages echo less of the value

In v4, `ShouldBe` on strings echoed the actual and expected values up to a hardcoded 5000 characters, silently, and computed the `difference` section from those already-truncated values. Two long strings differing past character 5000 therefore produced a 10 KB message showing two identical-looking values and no difference at all.

In v5 the difference is always computed from the full values, and only the echo is clipped — to [`ShouldlyConfiguration.MaxStringLengthInMessages`](/documentation/configuration#maxstringlengthinmessages), which now defaults to **1000**. Truncation is announced rather than silent:

```
"Lorem ipsum dolor sit amet" (truncated to 1000 of 42317 characters, see ShouldlyConfiguration.MaxStringLengthInMessages)
```

Messages for long strings get substantially shorter, and the `difference` section carries the information the raw echo used to bury. If you were relying on the old volume of output, set `ShouldlyConfiguration.MaxStringLengthInMessages = 5000;`.

Line-mode diffs also now cap at 20 changed lines per side, with an `... and N more line(s)` marker.

## The `Shouldly.Configuration` namespace has been removed

The approval-test configuration types — `ShouldMatchConfiguration`, `ShouldMatchConfigurationBuilder`, and friends — lived in the `Shouldly.Configuration` namespace in v4. In v5 they have been collapsed into the root `Shouldly` namespace, and `Shouldly.Configuration` no longer exists. (`FirstNonShouldlyMethodFinder`, `ITestMethodFinder`, and `FindMethodUsingAttribute<T>` are gone entirely — see [`ShouldMatchApproved` no longer walks the stack](#shouldmatchapproved-no-longer-walks-the-stack).)

Any `using Shouldly.Configuration;` now fails to compile with **`CS0234`**:

```csharp
using Shouldly.Configuration;
// error CS0234: The type or namespace name 'Configuration' does not exist in the namespace 'Shouldly'
```

Delete the `using Shouldly.Configuration;` line — the types resolve via `using Shouldly;`.

## `ShouldMatchApprovedDefaults` has moved

The static `ShouldMatchApprovedDefaults` builder used to configure approval tests has moved off `ShouldlyConfiguration` and onto a new `ShouldMatchConfiguration` class. Code referencing the old accessor fails to compile with **`CS0117`**:

```csharp
// v4 — compiles
ShouldlyConfiguration.ShouldMatchApprovedDefaults.DoNotIgnoreLineEndings();

// v5 — error CS0117: 'ShouldlyConfiguration' does not contain a definition for 'ShouldMatchApprovedDefaults'
ShouldMatchConfiguration.ShouldMatchApprovedDefaults.DoNotIgnoreLineEndings();
```

Change the class name only. Both types live in the root `Shouldly` namespace, so no `using` change is needed beyond `using Shouldly;`.

## `ShouldMatchApproved` no longer walks the stack

In v4, `ShouldMatchApproved` walked the runtime stack trace to find the test method, and used its reflected declaring type, method name, and PDB source information to name and place the approval files. In v5 the test method is captured at compile time instead, via `[CallerMemberName]` and `[CallerFilePath]` parameters on `ShouldMatchApproved` itself. This removes the reflection dependency (`ShouldMatchApproved` no longer carries `[RequiresUnreferencedCode]`, so it works under trimming and Native AOT), and no longer requires compiling with full debug information.

Two things to check when upgrading:

### Approval files are now prefixed with the source file name

The default approval file name is now `{SourceFileName}.{MethodName}.approved.{extension}` where v4 used the declaring type's name. Under the usual one-class-per-file convention these are identical, so most suites are unaffected. If a test class lives in a file with a different name — including nested classes and multiple classes per file — the expected file name changes, and the test will fail reporting the new missing `.approved` file. Either rename the source file to match the class, rename the approval file to the reported name, or restore the old name with `WithFilenameGenerator`. Custom `FilenameGenerator` delegates keep compiling: `TestMethodInfo.DeclaringTypeName` still exists but is `[Obsolete]` and now returns the source file name; prefer the new `TestMethodInfo.SourceFileName`.

### `UseCallerLocation` and `LocateTestMethodUsingAttribute` are removed

Both existed to point the stack walk at the right frame when `ShouldMatchApproved` was wrapped in a helper method. With compile-time capture there is no stack walk to redirect, so both now fail with **`CS0619`**. The replacement is for the helper to capture the caller info itself and pass it through:

```csharp
// v4
public static void ShouldMatchMySnapshot(this string actual) =>
    actual.ShouldMatchApproved(c => c.UseCallerLocation());

// v5
public static void ShouldMatchMySnapshot(this string actual,
    [CallerMemberName] string testMethodName = "",
    [CallerFilePath] string sourceFilePath = "") =>
    actual.ShouldMatchApproved(testMethodName: testMethodName, sourceFilePath: sourceFilePath);
```

This is the same forwarding idiom used for `[CallerArgumentExpression]`, and unlike the v4 stack walk it is immune to inlining, async state machines, and obfuscation. Helpers nested more than one level deep forward the same two parameters at each level.

## Custom assertion extensions no longer need `[ShouldlyMethods]`

In v4, custom assertion classes had to be annotated with `[ShouldlyMethods]` so the stack-walking machinery could tell Shouldly frames from test frames when reconstructing the asserted expression. In v5 the expression is captured at compile time with `[CallerArgumentExpression]`, so the attribute is no longer required — a custom assertion is just an extension method. See [Writing custom assertions](/documentation/extending) for the v5 pattern, including how to verify your `[CallerArgumentExpression]` wiring with `ShouldlyConfiguration.AssertCallerArgumentExpressionIsUsed()`.

The attribute still exists and is still honored by the legacy stack-walking fallback that only runs for `netstandard2.0` consumers whose compiler does not supply caller argument expressions; keep it if your custom assertion library supports those targets.

## `ShouldBeEquivalentTo` has been rewritten

The comparison engine behind `ShouldBeEquivalentTo` was rewritten for v5 (see [the roadmap](https://github.com/shouldly/shouldly/issues/1265) for the full rationale). The public API is unchanged, plus a new optional `EquivalencyOptions` parameter.

### The new model

A comparison strategy is selected per node in the object graph:

| Node type                                                                                                                                                                   | Strategy                                                                                                                                            |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Leaf values: primitives, enums, `string` (ordinal), `Guid`, `DateTime`/`DateTimeOffset`/`TimeSpan`, `Uri`, `Type`, delegates, and `System.*` value-semantic types generally | Compared with `Equals`, with lossless cross-numeric equality (`int 1` ≡ `long 1`)                                                                   |
| Dictionaries (`IDictionary`, `IDictionary<,>`, `IReadOnlyDictionary<,>`, immutable variants)                                                                                | Matched **by key**, recursing into values                                                                                                           |
| Sets (`ISet<>`, `IReadOnlySet<>`, immutable variants)                                                                                                                       | Order-insensitive                                                                                                                                   |
| Other collections                                                                                                                                                           | Order-sensitive, element-by-element, recursing; container type ignored (array ≡ `List<T>`); multidimensional arrays compared by rank and dimensions |
| Everything else — classes *and* structs                                                                                                                                     | Member-wise and recursive over public properties and fields; indexers skipped                                                                       |

Member selection uses **the expectation's members, with declared types and subset semantics**: every member on the expectation must exist and match on the actual object; extra members on the actual object are ignored; runtime type identity is no longer required. This enables the most-requested scenario:

```csharp
person.ShouldBeEquivalentTo(new { Name = "John" });
```

Failures now collect **every difference** and report them all with their paths, instead of stopping at the first.

### What newly passes

Most changes turn v4 failures (or crashes) into passes: cross-type and anonymous-type expectations, dictionaries compared insertion-order-insensitively with complex values compared structurally, sets compared order-insensitively, array vs `List<T>` with equal elements, structs and tuples holding reference-type members, types with indexers (previously `NotSupportedException`), `Type`- and `Uri`-valued members, and numeric members of different types holding the same value.

### What newly fails

* **`Equals` overrides on complex types are ignored** — members are compared. Values that were `Equals`-equal but structurally different passed in v4 and now fail. (Equivalency is for comparing structure; `ShouldBe` honors `Equals`.)
* **Structs are compared by public members**, not `ValueType.Equals`, so structs whose equality depended on private state compare only their public surface — and a struct with *no* public members now trips the vacuous-comparison guard below.
* **Multidimensional arrays check shape**: a 2×3 array no longer equals a 3×2 array with the same flat content.
* **Vacuous comparisons fail.** If a node selects zero comparable members (e.g. `new object()`, an empty marker interface as the declared type, or ignoring every member via options), the assertion fails with guidance instead of silently passing.

### Audit note: derived members in base-typed slots

v4 walked **runtime** types, so asserting on derived instances held in base-typed members also compared derived-only members. v5 selects members from the **declared** type, so those members are no longer compared — if your tests rely on this, cast the expectation to the concrete type. The vacuous-comparison guard catches the zero-member extreme, but a partial narrowing will not fail loudly.

### Deliberate differences from FluentAssertions

Lists and arrays remain **order-sensitive** by default (opt out with `EquivalencyOptions.IgnoreOrder`), different enum types with equal underlying values are **not** equivalent, cyclic graphs are compared gracefully without configuration, and recursion depth is unbounded. The executable comparison suite at `src/EquivalencyComparisonTests` documents every remaining divergence.

### `EquivalencyOptions`

```csharp
actual.ShouldBeEquivalentTo(expected, new EquivalencyOptions
{
    IgnoreOrder = true,                    // order-insensitive collection comparison
    MembersToIgnore = { "Id", "Created" }, // skip these members wherever they appear
});
```

### Trimming and AOT

`ShouldBeEquivalentTo` no longer carries `[RequiresUnreferencedCode]`. The reflection-based comparison sits behind the `Shouldly.Equivalency.IsReflectionEnabledByDefault` feature switch (the same pattern as `System.Text.Json`); a companion source-generation package that registers AOT-safe type shapes is planned, at which point trimmed publishes can disable the switch and drop the reflection path entirely.


