Я несколько лет проработал на проектах, где тестовый фреймворк был xUnit. Он первым поддержал .net core и во многие проекты попал именно по этой причине.

В целом, он выглядит как обычный юнит тест фреймворк, но с нюансами и своеобразной философией, которая не всегда применима к реальным проектам.

Погнали:

1) Нет возможности добавлять сообщения к ассертам. Я сторонник такого подхода, что по упавшему тесту должно быть понятно почему он упал. Для этого мне хотелось бы добавлять поясняющее сообщение к асерту. Например Assert.Empty(entityCollection, “all outdated items should be removed from database”). Но в xUnit думают иначе и такой перегрузки нет. Все что вы получите на упавшем тесте - это «Еxpected: empty, Actual: not empty. Но все же пару методов с такой сигнатурой прокралось - Assert.True и Assert.False.

2) Нет возможности выполнить функцию перед запуском всех тестов. Например вам нужно почистить/поднять базу перед тестами. У xUnit нет такой возможности.
Что советуют в интернете - использовать коллекции. Похоже на решение. Мы создаём коллекцию, в конструкторе создаём базу, в методе Dispose удаляем, все классы помечаем этой коллекцией и все, задача решена. Но нет! Проблема в том, что xUnit, в одной коллекции, выполняет тесты параллельно, а значит таким подходом мы заставим тесты работать в разы дольше.
Ещё варианты? Есть план: можно унаследовать все тесты от базового класса, а в нем создать статический конструктор (который гарантированно выполнится только один раз) и дело в шляпе. Снова нет. Если вы используете IClassFixture в своих тестов, то знаете, что сначала выполняются они, а потом методы с тестового класса. Это значит, что если ваш fixture пытается что-то записать в базу, то ее ещё может и не быть, ведь до этого момента никто не обращался к базовому тест классу.
План Б - перенести этот подход на fixtures. Унаследовать их от базовой fixture и в статическом конструкторе поднимать базу. Но тут вы должны понимать, что любой тестовый класс теперь должен реализовывать IClassFixture. Сложно, но жить можно.

3) Если вы у cебя в коде используете Result, GetAwaiter().GetResult() или Wait() то вполне вероятно, что поймаете deadlock и в xUnit это никого не смущает, ведь философия - использовать только async await. xunit deadlock

4) Результаты тестов. Допустим у вас есть 100 тестов в проекте, вы их все запускали в пределах одной коллекции. Допустим 99 из них прошло, 1 упал. А потом, в методе Dispose на классе коллекции вы что-то не учли и поймали null reference exception. И в логах вы обнаружите, что у вас прошло 99 тест кейсов и упало 101. Как так, ведь было только 100?

Если знаешь 5-ю причину - ппиши ниже под постом.