Это мой контроллер:
public class BlogController : Controller
{
private IDAO<Blog> _blogDAO;
private readonly ILogger<BlogController> _logger;
public BlogController(ILogger<BlogController> logger, IDAO<Blog> blogDAO)
{
this._blogDAO = blogDAO;
this._logger = logger;
}
public IActionResult Index()
{
var blogs = this._blogDAO.GetMany();
this._logger.LogInformation("Index page say hello", new object[0]);
return View(blogs);
}
}
Как вы можете видеть у меня есть 2 зависимости, а IDAOи
ILogger`
И это мой тестовый класс, я использовать xUnit тестирования и Moq для того чтобы создать макет и заглушки, я могу издеваться над Дао
легко, но с ILogger-я Дон'т знаю, что делать так что я просто передать null и закомментируйте вызов для входа в контроллер при запуске теста. Есть ли способ, чтобы проверить, но почему-то регистратор ?
public class BlogControllerTest
{
[Fact]
public void Index_ReturnAViewResult_WithAListOfBlog()
{
var mockRepo = new Mock<IDAO<Blog>>();
mockRepo.Setup(repo => repo.GetMany(null)).Returns(GetListBlog());
var controller = new BlogController(null,mockRepo.Object);
var result = controller.Index();
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<Blog>>(viewResult.ViewData.Model);
Assert.Equal(2, model.Count());
}
}
Просто издеваются над ним, а также любой другой зависимости:
var mock = new Mock<ILogger<BlogController>>();
ILogger<BlogController> logger = mock.Object;
//or use this short equivalent
logger = Mock.Of<ILogger<BlogController>>()
var controller = new BlogController(logger);
Вы, вероятно, нужно установить пакет Microsoft.Расширения.Лесозаготовки.Пакет абстракцийиспользовать
ILogger в<Т>`.
Кроме того, вы можете создать настоящий логгер:
var serviceProvider = new ServiceCollection()
.AddLogging()
.BuildServiceProvider();
var factory = serviceProvider.GetService<ILoggerFactory>();
var logger = factory.CreateLogger<BlogController>();
На самом деле, я'вэ нашли Майкрософт.Расширения.Лесозаготовки.Абстракций.Разными, четко различаемыми типами<>
, который выглядит как идеальное решение.
Использовать пользовательское средство, которое использует ITestOutputHelper
(из xUnit), чтобы захватить выходные и журналов. Ниже приведен небольшой пример, который только пишет "государство" на выход.
public class XunitLogger<T> : ILogger<T>, IDisposable
{
private ITestOutputHelper _output;
public XunitLogger(ITestOutputHelper output)
{
_output = output;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
_output.WriteLine(state.ToString());
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public IDisposable BeginScope<TState>(TState state)
{
return this;
}
public void Dispose()
{
}
}
Использовать его в вашем юнит тестов как
public class BlogControllerTest
{
private XunitLogger<BlogController> _logger;
public BlogControllerTest(ITestOutputHelper output){
_logger = new XunitLogger<BlogController>(output);
}
[Fact]
public void Index_ReturnAViewResult_WithAListOfBlog()
{
var mockRepo = new Mock<IDAO<Blog>>();
mockRepo.Setup(repo => repo.GetMany(null)).Returns(GetListBlog());
var controller = new BlogController(_logger,mockRepo.Object);
// rest
}
}
Добавить мои 2 цента, это вспомогательный метод расширения обычно помещается в статический вспомогательный класс: `` статический класс MockHelper { публичный статический ISetup<ILogger<П>> MockLog в<Т>(это макет<ILogger<П>> логгер, уровень уровень) { возвращать регистратор.Установки(х => У Х.Журнал(уровень, это.Если в<код> У(), это.Если в<Объект> У(), это.Если<исключение>(), это.Если<изм<объект, исключение, строка>>())); }
частная статическое выражение<действий<ILogger<П>>> проверить в<Т>(уровень уровень) { возвращает X => х.Журнал(уровень, 0, он.Если в<Объект> У(), это.Если<исключение>(), это.Если<изм<объект, исключение, строка>>()); }
публичный статический пустота проверить в<Т>(это макет<ILogger<П>> макет, уровень, Уровень, раз раз)
{
глумитесь.Убедитесь, (проверьте в<Т>(уровень) раз);
}
}
Затем, вы используете его так:
//Организовать
ВАР регистратор = новый макет<ILogger<в yourclass>>();
логгер.MockLog(Мышиloglevel.Предупреждение)
//Закона
//Утверждать логгер.Проверить(Уровень.Предупреждение, Раз.Когда-то()); `` И конечно, вы можете легко расширить его на поругание каких-либо ожиданий (т. е. ожидание, сообщение, etc ...)
Уже упоминал, что вы можете посмеяться над этим, как и любой другой интерфейс.
var logger = new Mock<ILogger<QueuedHostedService>>();
До сих пор так хорошо.
Хорошая вещь заключается в том, что вы можете использовать минимальный заказ
до убедитесь, что некоторые звонки были выполнены. Например, здесь я проверяю, что журнал был назван с определенным "исключение".
logger.Verify(m => m.Log(It.Is<LogLevel>(l => l == LogLevel.Information), 0,
It.IsAny<object>(), It.IsAny<TaskCanceledException>(), It.IsAny<Func<object, Exception, string>>()));
При использовании "проверить" дело в том, чтобы сделать это против метода реальных "журнал" из меню ILooger, а не методы расширения.
Это просто, как и другие ответы предлагаю пройти ILogger макет, но он вдруг становится гораздо более проблематичным, чтобы убедиться, что на самом деле были сделаны звонки в логгер. Причина в том, что большинство звонков на самом деле не принадлежат самому интерфейса ILogger
.
Поэтому большинство вызовов методов расширения, вызовите метод только "журнал" интерфейса. Причина, похоже, заключается в том, что он's путь проще сделать реализацию интерфейса, если у вас только одна, а не много перегрузки, что сводится к тому же способ.
Этот недостаток, конечно, что это вдруг намного тяжелее, чтобы убедиться, что звонок был сделан с момента вызова, вы должны убедиться, является очень отличается от вызова, который вы сделали. Есть несколько различных подходов, чтобы обойти эту проблему, и я обнаружил, что пользовательские методы продления насмешливый рамок позволит сделать его легче писать.
Вот пример метода, который я сделал для работы с NSubstitute`:
public static class LoggerTestingExtensions
{
public static void LogError(this ILogger logger, string message)
{
logger.Log(
LogLevel.Error,
0,
Arg.Is<FormattedLogValues>(v => v.ToString() == message),
Arg.Any<Exception>(),
Arg.Any<Func<object, Exception, string>>());
}
}
И это, как он может быть использован:
_logger.Received(1).LogError("Something bad happened");
Он выглядит точно так же, как если бы вы использовали метод напрямую, хитрость тут заключается в том, что наш метод расширения получает приоритет, потому что он'ы с "ближе" в пространствах имен, чем оригинал, поэтому он будет использоваться вместо этого.
Это не дает к сожалению 100% то, что мы хотим, а именно сообщения об ошибках, будет не таким хорошим, поскольку мы не'т проверить непосредственно на веревочке, а на лямбда-выражение, которое включает в себя строки, но 95% - это лучше, чем ничего :) кроме того, такой подход позволит сделать тестовый код
P. S. Для Moq одно можно использовать подход, написать метод расширения для макет<ILogger<П>>что делает
проверить`, чтобы достичь подобных результатов.
И когда через помощью StructureMap / Ламар:
var c = new Container(_ =>
{
_.For(typeof(ILogger<>)).Use(typeof(NullLogger<>));
});
Документы:
Для .чистая сердечник 3 ответов, используя минимальный заказ
больше не работает из-за изменения, описанные в вопросе такт в ILogger.Журнал<такт> используемый, чтобы быть объектом, теперь FormattedLogValues
Lickily stakx предоставили хороший обходной путь. Поэтому я'м отправляю его в надежде, что это может сэкономить время для других (потребовалось время, чтобы понять вещи):
loggerMock.Проверить( х => х.Журнал( Мышиloglevel.Информация, Он.Если в<код>(), Он.Это<нем.IsAnyType>((о, т) => строка.Равна("на страницу индекса, сказать Привет", О.Метод ToString(), Значение Stringcomparison.InvariantCultureIgnoreCase)), Он.Если<исключение>(), (Функ<нем.IsAnyType, исключение, строка>) это.Если в<Объект> У()), Раз.Один раз);