Я не считаю себя авторитетом в том, как выполнять модульное тестирование, но, поскольку раздел комментариев ограничен в символах, я буду писать свои комментарии здесь.
Обычно, когда вы попадаете в ситуацию, когдаТрудно придумать хороший модульный тест (я не буду определять «хороший» здесь чаще), потому что есть некоторые проблемы со структурой проекта / дизайном кода, а не реальные ограничения самого модульного тестирования. (опять же, не то, чтобы модульное тестирование не имело своих ограничений, но я думаю, что это не так).
На основании вышеизложенного я попросил вас включить код действия, чтобы мы могли проверить, что именноВы пытаетесь проверить, и почему это так сложно.
Вот часть моего комментария, основанная на мнениях, но я оставляю это на ваше усмотрение, если вы захотите принять это или оставить.
Это не правило, но хорошее практическое правило заключается в том, что контроллер должен содержать очень мало бизнес-логики, что означает, что модультестирование контроллера должно в основном проверять различные пути, по которым может идти запрос, как только он попадает в контроллер.
Как правило, вы хотите что-то вроде этого:
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = await _userManager.FindByNameAsync(model.UserName);
...
return View(result);
, который затем вы можете объединитьпроверить что-то вроде этого:
public async Task Register_Returns_BadRequest_On_Invalid_Model()
{
var testUsername = "TestUsername";
var mockUserManager = new Mock<IUserManager>();
mockUserManager.Setup(m => m.FindByNameAsync(testUsername))
.Returns(Task.FromResult(**Not sure about this part**))
var controller = new RegisterController(mockUserManager.Object);
var result = await controller.Register(model: null);
var actionResult = Assert.IsType<ActionResult<IdentityResult>>(result);
Assert.IsType<BadRequestObjectResult>(actionResult.Result);
}
Для счастливого пути вы хотите только проверить, что на действительном ModelState результат имеет тип ActionResult>
Что такоемоя идея:
- Когда вы тестируете блок контроллером, вас не должны беспокоить реальные данные, это ответственность других частей приложения
- Тест блока контроллера должен быть простымпросто, в большинстве случаев вам следует тестировать только эти два случая - неверные данные возвращают своего рода BadRequest, действительные данные возвращают ожидаемый ответ
- Если вы обнаружите, что большую часть времени вы смеетесь над объектами, это яснопризнак того, что вам нужен дополнительный уровень абстракции.
В вашем случае, чтобы улучшить мой кодструктурированный и более легкий для тестирования, я бы сделал следующее:
- Первый тест на недопустимый ModelState - вы не хотите продолжать, если ModelState недопустим, и это также должно охватываться модульным тестом.
Менеджеры должны быть более высокого уровня абстракции. Такие методы, как FindByNameAsync и CreateAsync больше подходят для уровня доступа к данным. В случае этого действия ваш UserManager
может иметь метод, подобный Register
, поэтому действие вашего контроллера выглядит следующим образом:
if (! ModelState.IsValid)
{
return BadRequest()
}
var result = _userManager.Register (model.UserName);
return View (результат);
сейчасВы можете удалить методы Find и Create из контроллера и создать UserRepository
, где я использую эти методы и где вы можете проверить их изолированно.
В этой настройке у вас есть этиАбстракция Контроллер -> Менеджер -> Хранилище. Теперь вы пытаетесь протестировать три из них в одном методе, который, на мой взгляд, вызывает проблемы.
Кроме того, просто потому, что я считаю это немного более аккуратным, обычно вы используете слой Service
иесли структура слишком сложна, вы добавляете слой менеджера, чтобы он стал Controller -> Manager -> Service -> Repository. В вашем случае я не уверен, что вам нужна эта сложность, поэтому, возможно, ради лучшего именования, переименуйте UserManager
в UserService
, чтобы ваш поток кода был Controller -> Service -> Repository.
Кроме того, последний совет. Тестирование контроллера всегда было противоречивым, так что не беспокойтесь, если вы не покрываете свой контроллер юнит-тестами так же, как другие части кода. Это несколько ожидаемо, и я хотел сказать в этом посте, главным образом, что проблема не в том, как тестировать, а в том, что тестируемый код сам по себе, что, на мой взгляд, может быть улучшено, как я показал выше. Да, мое предложение также не идеально, но оно создает меньшие куски кода, которые инкапсулированы и не имеют такого количества зависимостей, что в конечном итоге облегчает их тестирование. И, конечно, это не замена тестов интеграции.
Надеюсь, это дало вам пищу для размышлений.