I am not sure about how such a pattern named or even if it exists, but I named it 'container pattern'.
What I am trying to accomplish: to have an abstraction to hold a list of entities, being able only to add entities, and remove them only when entities saved to the database. I must say it works quite well and I like it much more than passing around List<> like I did earlier.
I just learned that testing private fields is big no-no, but I don't know how I can test Add method alone. Or how to test SaveAndClean without invoking Add. So far testing private field using additional constructor seem clean, but probably there are better solutions.
namespace test
{
class Container
{
private readonly List<Entity> _results;
private readonly IRepostory _repo;
// used for prod
public Container(IRepostory repo)
: this(new List<Entity>(500000), repo)
{
}
// used for tests
internal Container(List<Entity> results, IRepostory repo)
{
_results = results;
_repo = repo;
}
public void Add(Entity entity)
{
_results.Add(entity);
}
public async Task<bool> SaveAndClearAsync()
{
if (!_results.Any())
{
return true;
}
try
{
await _repo.SaveAsync(_results);
_results.Clear();
return true;
}
catch (Exception ex)
{
// logging
}
return false;
}
}
}
[Fact]
public void Add_AddToExisting_EntityAdded()
{
// Arrange
var results = new List<Entity>();
results.Add(new Entity { Name = "md51" });
var repo = new Mock<IRepository>(MockBehavior.Strict);
var service = new Container(results, repo.Object);
var newEntity = new Entity { Name "md52" };
// Act
service.Add(newEntity);
// Assert
Assert.Equal("md51", results[0].Name);
Assert.Equal("md52", results[1].Name);
Assert.Equal(2, results.Count);
}
In your case I would test the behavior as a black box. And from a black box perspective only calling Add doesn't produce any behavior so I'd leave it at that. But calling Add() 2 times and SaveAndClearAsync does, so just test that.
You shouldn't change your code interface for the sole purpose of testing. That's an anti-pattern as well.
I recommend this Dave Farley video on test mistakes.
Related
I have created ICollectionFixture implementation in hope to create a database instance and share it among various test classes. But, its just not happening and the DBContext gets created on every test method call. On debugging, I can clearly see that after every test method completion , Dispose method of my InMemoryDbContextFixture class gets called , and so every time , a new instance of DBContext gets created which does not saves any of the data that I passed through my first test method..
XUnit document clearly says that when you want to create a single test context and share it among tests in several test classes, we can create ICollectionFixture implementation.
What does sharing mean, when we always are creating a new instance of DBContext? Please help me understand such behavior in name of sharing the instance.
I can achieve the present behavior using
Static classes
as well. So why to use ICollectionFixture. This interface should prevent me from creating new instance of DBContext for every test case.
My Fixture class goes like
public class InMemoryDbContextFixture : IDisposable
{
private static bool _isdbInitialized = false;
static MyDbContext databaseContext = null;
private readonly object _lock = new object();
public InMemoryDbContextFixture()
{
lock (_lock)
{
if (!_isdbInitialized)
{
var options = new DbContextOptionsBuilder<MyDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
databaseContext = new MyDbContext(options);
databaseContext.Database.EnsureDeleted(); //Ensure first old db is deleted.
databaseContext.Database.EnsureCreated();
_isdbInitialized = true; //db is initialized for the first time.
// ... initialize data in the test database ...
Context = databaseContext;
}
}
}
public void Dispose()
{
this.Context.Dispose();
}
public MyDbContext Context
{
get { return databaseContext; }
private set { }
}
}
ICollectionFixture implementation.
[CollectionDefinition("SharedTestDatabaseDemo")]
public class SharedTestDatabaseDBContextCollection : ICollectionFixture<InMemoryDbContextFixture>
{
// This class has no code, and should never be created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
My Test Class
//using namespaces
namespace Project.TestControllers
{
[Collection("SharedTestDatabaseDemo")]
public class TestAuthController
{
private readonly MyDbContext myDbContext = null;
public TestAuthController(InMemoryDbContextFixture dbcontextForTest)
{
//Initialize non-mocked objects.
nimblyDbContext = dbcontextForTest.Context;
//Arrange mocks
objAuthCountroller = new AuthController(mock1.Object, configSettings, myDbContext,
mock2.Object, mock3.Object);
}
[Fact]
public async Task Test_LoginUserWithCorrectCredentials()
{
//Arrange dependencies.
InputModel logincred = new InputModel() {
Email = "XunitTestUser#testmail.com",
Password = "Pass#9799",
RememberMe = true
};
myDbContext.AspNetUserTokens.Add(new AspNetUserTokens
{
UserId = "2",
LoginProvider = "JWT",
Name = "XunitTestUser#testmail.com"
});
myDbContext.AspNetUsers.Add(new AspNetUsers
{
Id = "2",
Name = "XunitTestUser1",
UserName = "XunitTestUser1",
Email = "XunitTestUser#testmail.com",
NormalizedEmail = "XUNITTESTUSER#TESTMAIL.COM",
EmailConfirmed = true
});
await myDbContext.SaveChangesAsync();
//ACT
var result = await objAuthCountroller.Login(logincred);
Assert.NotNull(result);
}
[Fact]
public async Task Test_ShouldNotAuthenticateUserWithInvalidPassword()
{
InputModel logincred = new InputModel();
logincred.Email = "rahul#testmail.com";
logincred.Password = "InvalidPassword";
logincred.RememberMe = true;
var result = await objAuthCountroller.Login(logincred);
Assert.NotNull(result);
}
}
}
Yes, on running all the test cases at one go, I got the answer..
Actually , we might get this issue when we are trying to test only one Test method and not the entire Test Project. I got confused because when trying to debug Test methods individually in the test class.
Now, when I run all the run cases in visual studio , I can clearly see that ICollectionFixture instance is created only once and shared among various test classes using the collection.
While implementing custom NLog filter https://github.com/NLog/NLog/wiki/Filtering-log-messages#fully-dynamic-filtering I tried to rely on WhenMethodFilter, which accepts lambda callback ShouldLogEvent. However to unit test that callback lambda I need to make it public in the class that generates the config, which is not ideal - I hate making methods public just for the sake of testing.
private void ReconfigureNlog(object state)
{
var nlogConfig = ConstructNlogConfiguration();
foreach (var rule in nlogConfig.LoggingRules)
{
rule.Filters.Add(new WhenMethodFilter(ShouldLogEvent));
}
// TODO: maybe no need to reconfigure every time but just modify filter reference?
NLog.Web.NLogBuilder.ConfigureNLog(nlogConfig);
}
Another approach would be to inherit Filter base class and try to cover it with unit tests. But the issue that it does not have a public interface :
internal FilterResult GetFilterResult(LogEventInfo logEvent)
protected abstract FilterResult Check(LogEventInfo logEvent);
which makes it also not testable unless I make my own methods public. As much as this seems to be a small issue, I'm curious if there is a better way. From my perspective, making GetFilterResult internal is absolutely unnecessary, although it kinda following best design practices. Thoughts?
Update 1. Code of the class to be tested:
public class TenancyLogFilter: Filter
{
private readonly ITenancyLoggingConfiguration _loggingConfigurationConfig;
private readonly IHttpContextAccessor _httpContextAccessor;
public TenancyLogFilter(ITenancyLoggingConfiguration loggingConfigurationConfig, IHttpContextAccessor httpContextAccessor)
{
_loggingConfigurationConfig = loggingConfigurationConfig;
_httpContextAccessor = httpContextAccessor;
}
protected override FilterResult Check(LogEventInfo logEvent)
{
var result = FilterResult.Neutral;
if (CanEmitEvent(logEvent.Level))
{
result = FilterResult.Log;
}
return result;
}
private LogLevel GetMinLogLevel()
{
var level = LogLevel.Trace;
if (!_loggingConfigurationConfig.TenantMinLoggingLevel.Any())
return level;
var context = _httpContextAccessor?.HttpContext;
if (context == null)
return level;
if (context.Request.Headers.TryGetValue(CustomHeaders.TenantId, out var tenantIdHeaders))
{
var currentTenant = tenantIdHeaders.First();
if (_loggingConfigurationConfig.TenantMinLoggingLevel.ContainsKey(currentTenant))
{
level = _loggingConfigurationConfig.TenantMinLoggingLevel[currentTenant];
}
}
return level;
}
private bool CanEmitEvent(LogLevel currentLogLevel)
{
return currentLogLevel >= GetMinLogLevel();
}
}
For the purpose of testing the class you can derive from the subject class to expose what is needed to invoke the member under test since the target member is protected.
There is no way to change/access the internal member, but in this case the source shows it to be a simple implementation
/// <summary>
/// Gets the result of evaluating filter against given log event.
/// </summary>
/// <param name="logEvent">The log event.</param>
/// <returns>Filter result.</returns>
internal FilterResult GetFilterResult(LogEventInfo logEvent)
{
return Check(logEvent);
}
Source
From there it is just a matter of providing the necessary dependencies that would allow the test to be exercised to completion.
For example.
[TestClass]
public class TenancyLogFilterTests {
[TestMethod]
public void Should_Log_LogEvent() {
//Arrange
string expectedId = Guid.NewGuid().ToString();
LogLevel expectedLevel = LogLevel.Error;
FilterResult expected = FilterResult.Log;
var context = new DefaultHttpContext();
context.Request.Headers.Add(CustomHeaders.TenantId, expectedId);
var accessor = Mock.Of<IHttpContextAccessor>(_ => _.HttpContext == context);
var level = new Dictionary<string, LogLevel> {
{ expectedId, expectedLevel }
};
var config = Mock.Of<ITenancyLoggingConfiguration>(_ => _.TenantMinLoggingLevel == level);
var subject = new TestTenancyLogFilter(config, accessor);
var info = new LogEventInfo { Level = expectedLevel };
//Act
FilterResult actual = subject.GetFilterResult(info);
//Assert - FluentAssertions
actual.Should().Be(expected);
}
class TestTenancyLogFilter : TenancyLogFilter {
public TestTenancyLogFilter(ITenancyLoggingConfiguration loggingConfigurationConfig, IHttpContextAccessor httpContextAccessor)
: base(loggingConfigurationConfig, httpContextAccessor) { }
public FilterResult GetFilterResult(LogEventInfo logEvent) {
return Check(logEvent);
}
}
}
This allows the test to be isolated and also works around the limitation provided by the external 3rd party dependency.
The actual filter remains as before without having to expose anything additional.
From your original code example, take note that when faced with blocking issue like that, it should be see as a design issue and a sign to extract a service abstraction.
For example
public interface ILogEventAssessor {
FilterResult GetFilterResult(LogEventInfo logEvent);
}
whose implementation would encapsulate what was done in the custom TenancyLogFilter filter, and injected into the target class
private readonly ILogEventAssessor service;
//...assumed injected service
private void ReconfigureNlog(object state) {
var nlogConfig = ConstructNlogConfiguration();
foreach (var rule in nlogConfig.LoggingRules) {
rule.Filters.Add(new WhenMethodFilter(ShouldLogEvent));
}
// TODO: maybe no need to reconfigure every time but just modify filter reference?
NLog.Web.NLogBuilder.ConfigureNLog(nlogConfig);
}
private FilterResult ShouldLogEvent(LogEventInfo logEvent) {
return service.GetFilterResult(logEvent);
}
//...
Now there really is no need to test the 3rd party filter to verify your logic.
You can test your ILogEventAssessor implementation to verify your custom logic in isolation.
Usually the ugly trick is to make the method internal and then add this to your AssemblyInfo.cs in main-project:
using System;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleToAttribute("Tenancy.UnitTests")]
Then the unit-test-project Tenancy.UnitTests will be allowed to unit-test the internal-methods of the main-project.
See also https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.internalsvisibletoattribute
I'm having a difficult time trying to understand how to appropriately return mocked data from a simulated database call in a unit test.
Here's an example method I want to unit test (GetBuildings):
public class BuildingService : IBuildingService {
public IQueryable<Building> GetBuildings(int propertyId)
{
IQueryable<Building> buildings;
// Execution path for potential exception thrown
// if (...) throw new SpecialException();
// Another execution path...
// if (...) ...
using (var context = DataContext.Instance())
{
var Params = new List<SqlParameter>
{
new SqlParameter("#PropertyId", propertyId)
};
// I need to return mocked data here...
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
So GetBuildings calls a stored procedure.
So I need to mock the DataContext, that of which I can override and set a testable instance. So what happens here is, in the above example DataContext.Instance() does return the mocked object.
[TestFixture]
public class BuildingServiceTests
{
private Mock<IDataContext> _mockDataContext;
[SetUp]
public void Setup() {
_mockDataContext = new Mock<IDataContext>();
}
[TearDown]
public void TearDown() {
...
}
[Test]
public void SomeTestName() {
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
DataContext.SetTestableInstance(_mockDataContext.Object);
var builings = BuildingService.GetBuildings(1, 1);
// Assert...
}
Please ignore some of the parameters, like propertyId. I've stripped those out and simplified this all. I simply can't get the ExecuteQuery method to return any data.
All other simple peta-poco type methods I can mock without issue (i.e. Get, Insert, Delete).
Update
DataContext.Instance returns the active instance of the DataContext class, if exists, and if not exists, returns a new one. So the method of test under question returns the mocked instance.
Do not mock DataContext. Because mocking DataContext will produce tests tightly coupled to the implementation details of DataContext. And you will be forced to change tests for every change in the code even behavior will remain same.
Instead introduce a "DataService" interface and mock it in the tests for BuildingService.
public interface IDataService
{
IEnumerable<Building> GetBuildings(int propertyId)
}
Then, you can tests implementation of IDataService agains real database as part of integration tests or tests it agains database in memory.
If you can test with "InMemory" database (EF Core or Sqlite) - then even better -> write tests for BuildingService against actual implementation of DataContext.
In tests you should mock only external resources (web service, file system or database) or only resources which makes tests slow.
Not mocking other dependencies will save you time and give freedom while you refactoring your codebase.
After update:
Based on the updated question, where BuildingService have some execution path - you can still testing BuildingService and abstract data related logic to the IDataService.
For example below is BuildingService class
public class BuildingService
{
private readonly IDataService _dataService;
public BuildingService(IDataService dataService)
{
_dataService = dataService;
}
public IEnumerable<Building> GetBuildings(int propertyId)
{
if (propertyId < 0)
{
throw new ArgumentException("Negative id not allowed");
}
if (propertyId == 0)
{
return Enumerable.Empty<Building>();
}
return _myDataService.GetBuildingsOfProperty(int propertyId);
}
}
In tests you will create a mock for IDataService and pass it to the constructor of BuildingService
var fakeDataService = new Mock<IDataContext>();
var serviceUnderTest = new BuildingService(fakeDataService);
Then you will have tests for:
"Should throw exception when property Id is negative"
"Should return empty collection when property Id equals zero"
"Should return collection of expected buildings when valid property Id is given"
For last test case you will mock IDataService to return expected building only when correct propertyId is given to _dataService.GetBuildingsOfProperty method
In order for the mock to return data is needs to be set up to behave as expected given a provided input.
currently in the method under test it is being called like this
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
Yet in the test the mock context is being setup like
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
Note what the mock is told to expect as parameters.
The mock will only behave as expected when provided with those parameters. Otherwise it will return null.
Consider the following example of how the test can be exercised based on the code provided in the original question.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
DataContext.SetTestableInstance(_mockDataContext.Object);
var subject = new BuildingService();
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}
That said, the current design of the system under test is tightly coupled to a static dependency which is a code smell and makes the current design follow some bad practices.
The static DataContext which is currently being used as a factory should be refactored as such,
public interface IDataContextFactory {
IDataContext CreateInstance();
}
and explicitly injected into dependent classes instead of calling the static factory method
public class BuildingService : IBuildingService {
private readonly IDataContextFactory factory;
public BuildingService(IDataContextFactory factory) {
this.factory = factory
}
public IQueryable<Building> GetBuildings(int propertyId) {
IQueryable<Building> buildings;
using (var context = factory.CreateInstance()) {
var Params = new List<SqlParameter> {
new SqlParameter("#PropertyId", propertyId)
};
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
This will allow for a proper mock to be created in injected into the subject under test without using a static workaround hack.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
var factoryMock = new Mock<IDataContextFactory>();
factoryMock
.Setup(_ => _.CreateInstance())
.Returns(_mockDataContext.Object);
var subject = new BuildingService(factoryMock.Object);
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}
I'm creating my first test unit with Moq, but cannot seem to make it work.
I have a Generic repository that injects my ApplicationDbContext. I'm trying to recieve a list of all the foods stored in a database. In my real service I use Simple Injector and everything works fine there.
ApplicationDbContext:
public class ApplicationDbContext : IdentityDbContext<AppUser>
{
public ApplicationDbContext()
: base("ApplicationDbContext")
{
}
...
}
Generic repository:
public class Repository<T> : IRepository<T> where T : class
{
private ApplicationDbContext _context;
private readonly IDbSet<T> _entities;
public Repository(ApplicationDbContext context)
{
_context = context;
_entities = _context.Set<T>();
}
.. async methods .. (GetAllAsync)
}
Moq test:
[TestClass]
public class FoodServicesTest
{
private Mock<IRepository<Food>> _foodRepository;
[TestInitialize]
public void Initialize()
{
_foodRepository = new Mock<IRepository<Food>>();
}
[TestMethod]
public async Task CanGetAllFoods()
{
// Before edit 2
//IList<Food> foods = await _foodRepository.Object.GetAllAsync();
//_foodRepository.Setup(m => m.GetAllAsync()).ReturnsAsync(foods);
_foodRepository.Setup(m => m.GetAllAsync()).ReturnsAsync(List<Food>());
IList<Food> foods = await _foodRepository.Object.GetAllAsync();
Assert.IsTrue(foods.Count >= 1);
}
}
EDIT 2:
After placing the Setup above GetAllAsync() (thanks to Patrick Quirk) and replacing its parameter to 'new List()' the food list doesn't return a null anymore but a count 0 which presumably is better but I expect it to be 2 (like in service).
the return value is an empty list. this is specified by this line of your code
_foodRepository.Setup(m => m.GetAllAsync()).ReturnsAsync(new List<Food>());
the instruction above is actually telling to the mock object to return a new empty list when GetAllAsync is invoked.
You should instead create new Food objects to "simulate" a result from the database, like so:
var foodList = new List<Food>();
foodList.Add(new Food() { ...insert your mocked values here });
foodList.Add(new Food() { ...insert your mocked values here });
_foodRepository.Setup(m => m.GetAllAsync()).ReturnsAsync(foodList);
EDIT
looking better at the code I can only see that you're just using the mock object and see if returns a result. are you sure that is really needed this test? is useful to use mock objects on repositories when there's some business logic involved to be tested. maybe your code was just rewritten for making the question but is worthwhile to point this out.
You can specify the value to return this way:
var foods=new List<Food>();
//// add two items here
foods.Add(new food(){.. set values });
foods.Add(new food(){.. set values });
_foodRepository.Setup(m => m.GetAllAsync()).ReturnsAsync(foods);
IList<Food> foods = await _foodRepository.Object.GetAllAsync();
I'm a beginner at writing unit tests and I have a test I'm trying to get working. I'll start of by explaining what I'm trying to test.
I'm trying to test a method which saves messages in a Mvc 4 project. The method is called SaveMessage and is shown below.
namespace ChatProj.Service_Layer
{
public class UserService : IUserService
{
public MessageContext messageContext = new MessageContext();
public UserService()
{
_messageRepository = new MessageRepository(messageContext);
}
private IMessageRepository _messageRepository;
-> public void SaveMessage(Message message)
{
messageContext.Messages.Add(message);
_messageRepository.Save();
}
The _messageRepository.Save in the SaveMessage method is implemented in my DAL layer MessageRepository and looks like this:
public void Save()
{
context.SaveChanges();
}
This way of saving will seem a bit overcomplicated, but I structured the project this way because I didn't want the service layer (IUserService & UserService) to handle operations that could & should (i think) be handled by the Data Access Layer (IMessageRepository & MessageRepository).
Now comes the tricky part. I've been trying to understand how I could unit test this. This is my try:
namespace ChatProj.Tests
{
[TestFixture]
class MessageRepositoryTests
{
[SetUp]
public void Setup()
{
}
[Test]
public void SaveMessage_SaveWorking_VerifyUse()
{
//Arrange
var userServiceMock = new Mock<UserService>();
var message = new Message { MessageID = 0, Name = "Erland", MessageString = "Nunit Test", MessageDate = DateTime.Now };
var repositoryMock = new Mock<IMessageRepository>();
var contextMock = new Mock<MessageContext>();
MessageRepository messageRepository = new MessageRepository(contextMock.Object);
UserService userService = new UserService();
//Act
userService.SaveMessage(message);
//Assert
repositoryMock.Verify(m => m.Save());
userServiceMock.Verify(m => m.SaveMessage(message));
}
}
I get this error: Imgur link , and I'm not quite sure how to solve it. I've tried looking at several other SO posts but I fail to make the test work.
So I'm wondering, how do I practically get my Unit Test to work?
You should setup your MessageContext properties to return fake data and don't make real Db call with SaveChanges method.
Right now it still tries to access a real DB.
But you can setup only virtual properties or if it will be an inteface.
So the best solution is to extract an interface from your MessageContext and inject it into repository. Then you can easily mock your IMessageContext interface and force it to return appropriate in-memory data.
Take a look at these two lines:
UserService userService = new UserService();
//Act
userService.SaveMessage(message);
You're creating a userService instance, and then immediately saving your message. Now jump into the SaveMessage code.
public void SaveMessage(Message message)
{
messageContext.Messages.Add(message);
_messageRepository.Save();
}
Ok, now you're adding stuff to messageContext, and then calling _messageRepository.Save(). But where are messageContext and _messageRepository instantiated?
public MessageContext messageContext = new MessageContext();
public UserService()
{
_messageRepository = new MessageRepository(messageContext);
}
You're creating them at instantiation. The mocks that you've created in your test aren't being used. Instead of creating instances of these objects in the constructor, you might consider passing them into the UserService constructor as arguments. Then, you can pass in mocked instances in your test.