ASP.NET Core에서 ILogger로 단위 테스트하는 방법
이것은 내 컨트롤러입니다.
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);
}
}
보시다시피 두 가지 종속성, a IDAO및 aILogger
그리고 이것은 내 테스트 클래스입니다. xUnit을 사용하여 테스트하고 Moq를 사용하여 모의 및 스텁을 만듭니다. DAO쉽게 모의 할 수 있지만 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.Extensions.Logging.Abstractions를) 사용 하려면 패키지를 설치해야 할 것입니다 ILogger<T>.
또한 실제 로거를 만들 수 있습니다.
var serviceProvider = new ServiceCollection()
.AddLogging()
.BuildServiceProvider();
var factory = serviceProvider.GetService<ILoggerFactory>();
var logger = factory.CreateLogger<BlogController>();
실제로 Microsoft.Extensions.Logging.Abstractions.NullLogger<>완벽한 솔루션처럼 보이는 것을 찾았습니다 .
ITestOutputHelper(xunit에서)를 사용하여 출력 및 로그를 캡처 하는 사용자 정의 로거를 사용하십시오 . 다음은 state출력 에만을 쓰는 작은 샘플입니다 .
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
}
}
이미 언급했듯이 다른 인터페이스처럼 조롱 할 수 있습니다.
var logger = new Mock<ILogger<QueuedHostedService>>();
여태까지는 그런대로 잘됐다.
좋은 점은 당신이 사용할 수 있다는 것입니다 Moq위해 특정 호출이 수행되었는지 확인합니다 . 예를 들어 여기에서 로그가 특정 Exception.
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>>()));
Verify요점을 사용할 때 확장 메서드가 아닌 인터페이스 의 실제 Log메서드 에 대해 수행하는 것 ILooger입니다.
내 2 센트를 더하면 이것은 일반적으로 정적 도우미 클래스에 배치되는 도우미 확장 메서드입니다.
static class MockHelper
{
public static ISetup<ILogger<T>> MockLog<T>(this Mock<ILogger<T>> logger, LogLevel level)
{
return logger.Setup(x => x.Log(level, It.IsAny<EventId>(), It.IsAny<object>(), It.IsAny<Exception>(), It.IsAny<Func<object, Exception, string>>()));
}
private static Expression<Action<ILogger<T>>> Verify<T>(LogLevel level)
{
return x => x.Log(level, 0, It.IsAny<object>(), It.IsAny<Exception>(), It.IsAny<Func<object, Exception, string>>());
}
public static void Verify<T>(this Mock<ILogger<T>> mock, LogLevel level, Times times)
{
mock.Verify(Verify<T>(level), times);
}
}
그런 다음 다음과 같이 사용합니다.
//Arrange
var logger = new Mock<ILogger<YourClass>>();
logger.MockLog(LogLevel.Warning)
//Act
//Assert
logger.Verify(LogLevel.Warning, Times.Once());
물론 기대치 (예 : 기대치, 메시지 등)를 조롱하기 위해 쉽게 확장 할 수 있습니다.
And when using StructureMap / Lamar:
var c = new Container(_ =>
{
_.For(typeof(ILogger<>)).Use(typeof(NullLogger<>));
});
Docs:
- https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.abstractions.nulllogger?view=aspnetcore-2.1
- http://structuremap.github.io/generics/
It is easy as other answers suggest to pass mock ILogger, but it suddenly becomes much more problematic to verify that calls actually were made to logger. The reason is that most calls do not actually belong to the ILogger interface itself.
So the most calls are extension methods that call the only Log method of the interface. The reason it seems is that it's way easier to make implementation of the interface if you have just one and not many overloads that boils down to same method.
The drawback is of course that it is suddenly much harder to verify that a call has been made since the call you should verify is very different from the call that you made. There are some different approaches to work around this, and I have found that custom extension methods for mocking framework will make it easiest to write.
Here is an example of a method that I have made to work with 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>>());
}
}
And this is how it can be used:
_logger.Received(1).LogError("Something bad happened");
It looks exactly as if you used the method directly, the trick here is that our extension method gets priority because it's "closer" in namespaces than the original one, so it will be used instead.
It does not give unfortunately 100% what we want, namely error messages will not be as good, since we don't check directly on a string but rather on a lambda that involves the string, but 95% is better than nothing :) Additionally this approach will make the test code
P.S. For Moq one can use the approach of writing an extension method for the Mock<ILogger<T>> that does Verify to achieve similar results.
참고URL : https://stackoverflow.com/questions/43424095/how-to-unit-test-with-ilogger-in-asp-net-core
'Program Club' 카테고리의 다른 글
| Amazon Redshift에서 열 데이터 유형 변경 (0) | 2020.10.20 |
|---|---|
| CSV 파일에 UTF-8을 쓰는 방법 (0) | 2020.10.20 |
| return 문의 목적은 무엇입니까? (0) | 2020.10.20 |
| wget / curl을 사용하여 주어진 웹 페이지의 .zip 파일에 대한 모든 링크를 다운로드하는 방법은 무엇입니까? (0) | 2020.10.20 |
| 바람둥이 포트 번호 변경 방법 (0) | 2020.10.20 |