I have the following class with a Log function that for the testing purpose just returns true.
public SomeClass : ILogger
{
// Other functions
public bool Log()
{
return true;
}
}
How ever in my unit test I have the following:
Mock<ILogger> logger = new Mock<ILogger>();
logger.Setup(func => func.Log()).Returns(() => false).Verifiable();
SomeClass testMe = new SomeClass(logger.Object);
bool result = testMe.Log();
logger.Verify(); //This fails saying that the Log function was never called
The bool result is not set to false, but to true. Which leads me to believe my setup is incorrect. Is this the case?
That is because you haven't called Log() method of injected logger instance. Call logger.Log() inside your SomeClass Log method
public SomeClass : ILogger
{
private ILogger logger;
// Other functions
public SomeClass(ILogger logger)
{
this.logger = logger;
}
public bool Log()
{
return logger.Log();
//return true;
}
}
Related
I should be getting a list of Product Types but GetProductTypesAsync() returns a null.
Is the class ProductTypeRepo meant to be mocked since it calls the acutal API.
Anyone able to assist?
namespace UnitTest.Service
{
public class ProductTypeServiceTests
{
private readonly IServiceProvider _serviceProvider;
private readonly Mock<IProductTypeRepo> _productTypeRepoMock;
private readonly Mock<ILogger> _LoggerMock
private IProductTypeService _productTypeService;
public ProductTypeServiceTests()
{
_productTypeRepoMock = new Mock<IProductTypeRepo>();
_LoggerMock= new Mock<ILogger>();
_productTypeService = new ProductTypeService(_productTypeRepoMock.Object, _LoggerMock.Object);
}
[Fact]
public async Task GetProductType_ReturnOKStatusCode()
{
var serviceResponse = await _productTypeService.GetProductTypesAsync();
Assert.Equal(
expected: serviceResponse,
actual: serviceResponse
);
}
}
}
--
namespace Service.ProductType
{
public class ProductTypeService : IProductTypeService
{
private readonly IProductTypeRepo _repository;
private readonly ILogger _Logger;
public ProductTypeService(IProductTypeRepo repository, ILogger logger)
{
_repository = repository;
_logger = logger;
}
public async Task<List<Domain.DTO.ProductTypeResponse>> GetProductTypesAsync()
{
var productTypes = await _repository.GetProductTypesAsync();
if (productTypes == null)
{
throw new ProductTypeNotFoundException($"No Product Types were retrieved");
}
return productTypes;
}
}
}
xxxxxxxxxx xxxxxxxx xxxxxx xxxxx
Nowhere in the test is the mock configured to return anything when invoked.
//...
[Fact]
public async Task GetProductType_ReturnOKStatusCode() {
//Arrange
List<Domain.DTO.ProductTypeResponse> expected = new List<Domain.DTO.ProductTypeResponse>();
//..add items to expected list if necessary
_productTypeRepoMock
.Setup(_ => _.GetProductTypesAsync()) //<-- when this is invoked
.ReturnsAsync(expected); //<-- return something.
//Act
List<Domain.DTO.ProductTypeResponse> actual = await _productTypeService.GetProductTypesAsync();
//Assert
Assert.Equal(expected, actual);
}
I have below unit test C# code,
public class ServiceTest
{
public readonly Service _sut;
private readonly Mock<IServiceClient> _serviceClientMock = new Mock<IServiceClient>();
private readonly Mock<ILogger<Service>> _loggerMock = new Mock<ILogger<Service>>();
public ServiceTest()
{
_sut = new Service(_serviceClientMock.Object, _loggerMock.Object);
}
[Fact]
public void Constructor_throws_Exception()
{
Assert.Throws<ArgumentNullException>(() => new Service(null, null));
}
[Fact]
public async Task Do_Test_For_DoMethod()
{
await _sut.DoMethod();
}
}
I have Constructor_throws_Exception which only covers one argument null exception, but not the other. How to cover both argument null exception plus the catch block for method? Is there a way I can merge with all in a single test? I am using xUnit.
You have to create a unique test for each invalid combination. Could be something like this:
public static IEnumerable<object[]> GetInvalidConstructorArguments()
{
yield return new object[] { new Mock<IServiceClient>().Object, null };
yield return new object[] { null, new Mock<ILogger<Service>>().Object };
}
[Theory]
[MemberData(nameof(GetInvalidConstructorArguments))]
public void ThrowsOnNullArgument(IServiceClient serviceClient, ILogger<Service> logger)
{
Assert.Throws<ArgumentNullException>(() => new Service(serviceClient, logger));
}
Getting a working mock for the ILogger<> is more complicated then it seems in the first spot. The problem is, that all convenient methods are extensions methods, which can't be mocked. Under the hood, all of these methods will call the Log<TState>() method which must be mocked. Thankfully to this answer, this can be done as follows:
public class MyTests
{
[Fact]
public void ExceptionShouldBeWrittenToLog()
{
// Instruct service client to throw exception when being called.
var serviceClient = new Mock<IServiceClient>();
var exception = new InvalidOperationException($"Some message {Guid.NewGuid()}");
serviceClient.Setup(s => s.Do()).Throws(exception);
// Create a strict mock, that shows, if an error log should be created.
var logger = new Mock<ILogger<MyService>>(MockBehavior.Strict);
logger.Setup(l => l.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((o, t) => o.ToString() == exception.Message),
It.IsAny<InvalidOperationException>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()));
// Setup SUT and call method.
var service = new MyService(serviceClient.Object, logger.Object);
service.DoSomething();
// Check if method of logger was being called.
logger.VerifyAll();
}
}
public interface IServiceClient
{
public void Do();
}
public class MyService
{
private readonly IServiceClient serviceClient;
private readonly ILogger<MyService> logger;
public MyService(IServiceClient serviceClient, ILogger<MyService> logger)
{
this.serviceClient = serviceClient;
this.logger = logger;
}
public void DoSomething()
{
try
{
serviceClient.Do();
}
catch (Exception ex)
{
logger.LogError(ex.Message);
}
}
}
I'm trying to make an unit test for a logger in an application.
For example I need to test the method Logger.info("some message"), but this method is static and return void.
Searching on Google I understand that I have to use Moq but am unable to implement that on the UnitTest class.
The Logger constructor does not have an argument and in x.Debug I have an error that says that I can't access
from instance reference.
Is there a way to implement UnitTest without editing the production code?
[TestClass()]
public class LoggerTests
{
[TestMethod()]
public void DebugTest()
{
var mock = new Mock<Logger>();
mock.Setup(x => x.Debug(It.IsAny<string>());
new Logger(mock.Object).AddLog("testing");
mock.VerifyAll;
}
}
Program.cs
private static void ConfigureLogger()
{
Logger.AddLog(new NLogAppender());
Logger.Level = TraceLevel.Verbose;
Logger.Info("Configured Logger");
}
Logger.cs
public class Logger
{
public static readonly List<IAppender> loggings = new List<IAppender>();
public static void AddLog(IAppender appender)
{
loggings.Add(appender);
}
public static TraceLevel Level { get; set; }
static Logger()
{
Level = TraceLevel.Verbose;
}
public static void Info(string message)
{
LogMessage(message);
}
}
NlogAppender.cs
public class NLogAppender : IAppender
{
public NLog.Logger logger;
public NLogAppender()
{
logger = LogManager.GetLogger(nameof(NLogAppender));
}
public void AddLog(string str)
{
}
}
IAppender.cs
public interface IAppender
{
void AddLog(string str);
}
You can't mock a static class, and you shouldn't mock the class/system under test.
Add a mock appender to the logger:
// Arrange
var logString = "test-info"
var appenderMock = new Mock<IAppender>();
appenderMock.Setup(a => a.AddLog(logString));
Logger.AddLog(appenderMock.Object);
// Act
Logger.Info(logString);
// Assert
// TODO: exactly once
appenderMock.VerifyAll();
Note this static class may persist data between tests causing unexpected results, consult your test framework for configuring this.
Apart from that, you usually don't want to roll your own logging infrastructure, there's lots of things you can do wrong and why reinvent the wheel? Plenty of ILogger(<T>) implementations around.
I am planning to do an async/await in C# Blazor class constructor method. Although this is written in Blazor it's a generic for C# so it doesn't matter.
public class DoctorsService : IDoctorsService
{
private readonly IConfiguration _config;
public DoctorsService(IConfiguration config, IClinicsDoctorsService clinicsDoctorsService)
{
_config = config;
clinicsDoctorsService.GetClinicsDoctorsListAsync(new Dictionary<string, string>());
}
}
If you noticed the clinicsDoctorsService isn't awaited using await, that's bc the compiler will complain that the method must be in async Task. If I write it like public async Task DoctorsService(), the compiler will complain with another issue because you cannot name a method same with the class name.
Sync ctor + async Init
public class DoctorsService : IDoctorsService
{
private readonly IConfiguration _config;
private readonly IClinicsDoctorsService _clinicsDoctorsService;
private bool _isInitialized = false;
internal DoctorsService(IConfiguration config, IClinicsDoctorsService clinicsDoctorsService)
{
_config = config;
_clinicsDoctorsService = clinicsDoctorsService;
//Set only those fields which are NOT depending on external data
}
internal async Task Init()
{
_clinicsDoctorsService.GetClinicsDoctorsListAsync(new Dictionary<string, string>());
_isInitialized = true;
//Set those fields which are depending on external data
}
public void SomeMethod()
{
if (!_isInitialized)
throw new NotSupportedException("Please call Init before you call any other instance method.");
//Some business logic goes here
}
}
Please note the internal access modifiers.
Dummy factory method
public static class DoctorServiceFactory
{
public static async Task<IDoctorsService> CreateDoctorsService(IConfiguration config, IClinicsDoctorsService clinicsDoctorsService)
{
IDoctorsService svc = new DoctorsService(config, clinicsDoctorsService);
await svc.Init();
return svc;
}
}
I am creating an app in .net core and I'm trying to output a log.
I was able to log output from the controller and I also want to log output from the model, which is shown below.
Unfortunately, I don't know what to pass as arguments to the constructor. My guess is the same Ilogger<controller> as the controller, but I would like to know if there is a correct way.
Thanks in advance.
Controller:
public class SampleController : Controller {
public readonly ILogger<SampleController > _logger;
public SampleController (ILogger<SampleController > logger)
{
_logger = logger;
}
[HttpGet("[action]")]
public string FindSample()
{
_logger.LogInformation("FindSample Start");
// ***** Is it necessary to carry over the log of the sample controller to a much lower DAO from here? *****
var model = new SampleModel(_logger);
var result = model.Find();
_logger.LogInformation("FindSample End");
return result;
}
}
Model:
public class SampleModel
{
public readonly ILogger<SampleController> _logger;
public SampleModel(ILogger<SampleController> logger)
{
_logger = logger;
}
public string Find()
{
_logger.LogInformation("FindModel Start");
var dao = new SampleDao(_logger);
var code = dao.GetCode();
_logger.LogInformation("FindModel End");
return code;
}
}
Dao:
public class SampleDao
{
public readonly ILogger<SampleController> _logger;
public SampleContext SampleContext;
// ***** Should I pass it as an argument forever? *****
public SampleDao(ILogger<SampleController> logger)
{
_logger = logger;
if (SampleContext == null)
{
SampleContext = new SampleContext();
}
}
public string GetCode()
{
_logger.LogInformation("GetCode Start");
var code = SampleContext.SampleTable.FirstOrDefault().code;
_logger.LogInformation("GetCode End");
return code;
}
}
You should pass as a generic argument the class in which the logger is being injected. So you should use:
In your Dao
ILogger<SampleDao> logger
In your Model
ILogger<SampleModel> logger
For more information you could check:
Logging in ASP.NET core