Here is my class:
public class AuditInfo
{
public String ActionDescription { get; set; }
public String ActionWho { get; set; }
public BasicProjectProfile Project { get; set; }
public AuditInfo ()
{ }
public void SaveInfo ()
{
using (CIHEntities _dbContext = new CIHEntities())
{
AuditInfoEntity aie = new AuditInfoEntity();
aie.ActionDescription = this.ActionDescription;
aie.ActionWhen = DateTime.Now;
if (this.ActionWho != null)
{
aie.ActionWho = this.ActionWho;
}
else
{
aie.ActionWho = "Not Specified";
}
aie.ProjectAssoc = _dbContext.ProjectEntity
.Where(r => r.Id == this.Project.Id)
.First();
_dbContext.SaveChanges();
}
}
}
CIHEntities is a Entity Framework Database.
I would like to unit test the SaveInfo method but it shouldn't actually save to the Database. How Can this be done?
Thanks
Eric
use the Repository pattern and move the responsibility for saving the enity to the Repository:
public class AuditInfo
{
public String ActionDescription { get; set; }
public String ActionWho { get; set; }
public BasicProjectProfile Project { get; set; }
}
write an interface for your repository:
public interface IRepository
{
void Save();
}
then your implemention:
public RealRepository : IRepository
{
public void SaveInfo ()
{
using (CIHEntities _dbContext = new CIHEntities())
{
AuditInfoEntity aie = new AuditInfoEntity();
aie.ActionDescription = this.ActionDescription;
aie.ActionWhen = DateTime.Now;
if (this.ActionWho != null)
{
aie.ActionWho = this.ActionWho;
}
else
{
aie.ActionWho = "Not Specified";
}
aie.ProjectAssoc = _dbContext.ProjectEntity
.Where(r => r.Id == this.Project.Id)
.First();
_dbContext.SaveChanges();
}
}
}
then make your client code that takes a repository as a parameter will be something like this
public class MyClient{
IRepository _repository;
public MyClient(){
_repository = new RealRepository();
}
public MyClient(IRepository repository)
{
_repository = repository;
}
public void Main(){
AuditInfo entity = new AuditInfo();
//do whatever you want
_repository.Save();
}
}
Your real application can use the default constructor which will use the RealRepository while you unit tests can can pass a Fake Repository implementation that doesn't talk to the database (or even better use a Mocking framework like Moq).
This is the basic concept, you can improve it to have a Generic Repository, use UoW pattern ... You can read more about it in this msdn article: Testability and EF
Given : that Id don't know what is the interface of CIHEntities
When : Unit Test is required for AuditInfo.SaveInfo
Then : inject the interface of CIHEntities from constructor of AuditInfo
Then : create a mock of CIHEntities from its interface and use it for Unit Test
Please mock the expectation and verify SaveChanges was called by using a mock library e.g Moq
Use the TransactionScope. Unless you actually commit the changes, it won't be added to the database. That's what I use in my unit tests, and it works flawlessly.
To use it, you have to add a reference to System.Transactions, and after that you might get some Windows error. If I remember correctly, you have to start some Windows Service.. However, Google it when you get that far.
Related
I am trying to test a class that only has a private constructor. This is for a course registration system. The courses do not get create via our application, therefore we intentionally have no public constructor. Instead we use EF to get the courses that are already in the database, and register students to them.
I am trying to test the register method of the Course class, however I have no way of creating an instance. I could use
course = (Course)Activator.CreateInstance(typeof(Course), true);, but then I don't have a way to setup the necessary properties since those are private.
What is the recommended approach for unit testing without a constructor?
This is a slimmed down version of the code.
public class Course
{
private Course()
{
}
public int Id { get; private set; }
public string Name { get; private set; }
public bool Open { get; private set; }
public virtual ICollection<Student> Students { get; private set; }
public void Register(string studentName)
{
if (Open)
{
var student = new Student(studentName);
Students.Add(student);
}
}
}
// Usage //
using (var db = new SchoolContext())
{
var course = db.Courses.Include(x => x.Students).Where(x => x.Name == courseName).First();
course.Register(studentName);
db.SaveChanges();
}
// Unit Test //
[Fact]
public void CanRegisterStudentForOpenClass(){
// HERE I HAVE NO WAY TO CHANGE THE OPEN VARIABLE
var course = (Course)Activator.CreateInstance(typeof(Course), true);
course.Register("Bob");
}
Yes you can using reflexion. your code is neraly there;
you can get properties and fields of the types with typeof(Course).GetProperty("PropertyName") then you can use SetValue to set the desired value, and pass as parameter first the instance to modify then the value.
in your case true;
note: in your example you will need to add the Collection of students too, if your Open is true.
Here there is a working example:
[Fact]
public void CanRegisterStudentForOpenClass()
{
var course = (Course)Activator.CreateInstance(typeof(Course), true);
typeof(Course).GetProperty("Open").SetValue(course, true, null);
ICollection<Student> students = new List<Student>();
typeof(Course).GetProperty("Students").SetValue(course, students, null);
course.Register("Bob");
Assert.Single(course.Students);
}
If you would rather not use reflection, then I recommend you use internal classes (instead of private) and using the InternalsVisibleToAttribute on your implementation assembly.
You can find more about the attribute here. Here's a quick guide on how you can use it!
Step 1. Add this attribute to your assembly that wants its internal code tested.
[assembly: InternalsVisibleToAttribute("MyUnitTestedProject.UnitTests")]
Step 2. Change private to internal.
public class Course
{
internal Course()
{
}
public int Id { get; internal set; }
public string Name { get; internal set; }
public bool Open { get; internal set; }
public virtual ICollection<Student> Students { get; internal set; }
/* ... */
}
Step 3. Write your tests like normal!
[Fact]
public void CanRegisterStudentForOpenClass()
{
var course = new Course();
course.Id = "#####";
course.Register("Bob");
}
As a few people have mentioned here, unit testing something private is either a code smell, or a sign you're writing the wrong tests.
In this case, what you would want to do is use EF's in-memory database if you're using Core, or mocking with EF6.
For EF6 You can follow the docs here
I would say rather than newing your dbContext where you do, pass it in via Dependency Injection. If that's beyond the scope of the work you're doing, (I'm assuming this is actual coursework, so going to DI may be overkill) then you can create a wrapper class that takes a dbcontext and use that in place.
Taking a few liberties with where this code is called from...
class Semester
{
//...skipping members etc
//if your original is like this
public RegisterCourses(Student student)
{
using (var db = new SchoolContext())
{
RegisterCourses(student, db);
}
}
//change it to this
public RegisterCourses(Student student, SchoolContext db)
{
var course = db.Courses.Include(x => x.Students).Where(x => x.Name == courseName).First();
course.Register(studentName);
db.SaveChanges();
}
}
[Fact]
public void CanRegisterStudentForOpenClass()
{
//following after https://learn.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking#testing-query-scenarios
var mockCourseSet = new Mock<DbSet<Course>>();
mockCourseSet.As<IQueryable<Course>>().Setup(m => m.Provider).Returns(data.Provider);
mockCourseSet.As<IQueryable<Course>>().Setup(m => m.Expression).Returns(data.Expression);
mockCourseSet.As<IQueryable<Course>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockCourseSet.As<IQueryable<Course>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
//create an aditional mock for the Student dbset
mockStudentSet.As.........
var mockContext = new Mock<SchoolContext>();
mockContext.Setup(c => c.Courses).Returns(mockCourseSet.Object);
//same for student so we can include it
mockContext.Include(It.IsAny<string>()).Returns(mockStudentSet); //you can change the isAny here to check for Bob or such
var student = Institution.GetStudent("Bob");
var semester = Institution.GetSemester(Semester.One);
semester.RegisterCourses(student, mockContext);
}
If you're using EFCore you can follow it along from here
You can fake private constructors and members using TypeMock Isolator or JustMock (both paid) or using MS Fakes (only available in VS Enterprise).
There is also a free Pose library that allows you to fake access to properties.
Unfortunately, the private constructor can't be forged. Therefore, you will need to create an instance of the class using reflection.
Add package.
Open namespace:
using Pose;
Test code:
[Fact]
public void CanRegisterStudentForOpenClass()
{
var course = (Course)Activator.CreateInstance(typeof(Course), true);
ICollection<Student> students = new List<Student>();
Shim studentsPropShim = Shim.Replace(() => Is.A<Course>().Students)
.With((Course _) => students);
Shim openPropShim = Shim.Replace(() => Is.A<Course>().Open)
.With((Course _) => true);
int actual = 0;
PoseContext.Isolate(() =>
{
course.Register("Bob");
actual = course.Students.Count;
},
studentsPropShim, openPropShim);
Assert.Equal(1, actual);
}
You can create a JSON representation of your default instance and deserialize it with Newtonsoft.
Something like this:
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using privateConstructor;
namespace privateConstructorTest
{
[TestClass]
public class CourseTest
{
[TestMethod]
public void Register_WhenOpenIsTrue_EnableAddStudents()
{
// Arrange
const string json = #"{'Id': 1, 'name':'My Course', 'open':'true', 'students':[]}";
var course = CreateInstance<Course>(json);
// Act
course.Register("Bob");
// Assert
Assert.AreEqual(1, course.Students.Count);
}
[TestMethod]
public void Register_WhenOpenIsFalse_DisableAddStudents()
{
// Arrange
const string json = #"{'Id': 1, 'name':'My Course', 'open':'false', 'students':[]}";
var course = CreateInstance<Course>(json);
// Act
course.Register("Bob");
// Assert
Assert.AreEqual(0, course.Students.Count);
}
private static T CreateInstance<T>(string json) =>
JsonConvert.DeserializeObject<T>(json, new JsonSerializerSettings
{
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
ContractResolver = new ContractResolverWithPrivates()
});
public class ContractResolverWithPrivates : CamelCasePropertyNamesContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
if (prop.Writable) return prop;
var property = member as PropertyInfo;
if (property == null) return prop;
var hasPrivateSetter = property.GetSetMethod(true) != null;
prop.Writable = hasPrivateSetter;
return prop;
}
}
}
}
In order to have a cleaner test class, you can extract the JSON strings and the helper code that creates the instance.
In my application, I need to interact with two databases. I have two domain classes which are located in two different databases. I also have a generic repository pattern which accepts an UoW in its constructor. I am looking a way to inject appropriate UoW based on Domain class. I do not want to write second generic repository for the second database.. Is there any neat solution?
public interface IEntity
{
int Id { get; set; }
}
Located in Database A
public class Team: IEntity
{
public int Id { get; set; }
public string Name{ get; set; }
}
Located in Database B
public class Player: IEntity
{
public int Id { get; set; }
public string FullName { get; set; }
}
I also have a generic repository pattern with UoW
public interface IUnitOfWork
{
IList<IEntity> Set<T>();
void SaveChanges();
}
public class DbADbContext : IUnitOfWork
{
public IList<IEntity> Set<T>()
{
return new IEntity[] { new User() { Id = 10, FullName = "Eric Cantona" } };
}
public void SaveChanges()
{
}
}
public class DbBDataContext: IUnitOfWork
{
public IList<IEntity> Set<T>()
{
return new IEntity[] { new Tender() { Id = 1, Title = "Manchester United" } };
}
public void SaveChanges()
{
}
public interface IRepository<TEntity> where TEntity: class, IEntity
{
IList<IEntity> Table();
}
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
{
protected readonly IUnitOfWork Context;
public BaseRepository(IUnitOfWork context)
{
Context = context;
}
IList<IEntity> IRepository<TEntity>.Table()
{
return Context.Set<TEntity>();
}
}
I've already found articles saying that Autofac overrides the registration with the last value. I know my problem is how DbContexts are registered.
var builder = new ContainerBuilder();
// problem is here
builder.RegisterType<DbADbContext >().As<IUnitOfWork>()
builder.RegisterType<DbBDbContext >().As<IUnitOfWork>()
builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IRepository<>));
var container = builder.Build();
I inspired from #tdragon's answer.
The first step is registering Named DbContext
builder.RegisterType<Database1>()
.Keyed<IUnitOfWork>(DbName.Db1)
.Keyed<DbContext>(DbName.Db1).AsSelf().InstancePerRequest();
builder.RegisterType<Database2>()
.Keyed<IUnitOfWork>(DbName.Db2)
.Keyed<DbContext>(DbName.Db2).AsSelf().InstancePerRequest();
Please note that DbName is just an enum.
The following code scans the data access layer assembly for finding Domain classes. Then, it registers ReadOnlyRepository and BaseRepository. the place of this code is in DIConfig
Type entityType = typeof(IEntity);
var entityTypes = Assembly.GetAssembly(typeof(IEntity))
.DefinedTypes.Where(t => t.ImplementedInterfaces.Contains(entityType));
var baseRepoType = typeof(BaseRepository<>);
var readOnlyRepoType = typeof(ReadOnlyRepository<>);
var baseRepoInterfaceType = typeof(IRepository<>);
var readOnlyRepoInterfaceType = typeof(IReadOnlyRepository<>);
var dbContextResolver = typeof(DbContextResolverHelper).GetMethod("ResolveDbContext");
foreach (var domainType in entityTypes)
{
var baseRepositoryMaker = baseRepoType.MakeGenericType(domainType);
var readonlyRepositoryMarker = readOnlyRepoType.MakeGenericType(domainType);
var registerAsForBaseRepositoryTypes = baseRepoInterfaceType.MakeGenericType(domainType);
var registerAsForReadOnlyRepositoryTypes = readOnlyRepoInterfaceType.MakeGenericType(domainType);
var dbResolver = dbContextResolver.MakeGenericMethod(domainType);
// register BaseRepository
builder.Register(c => Activator.CreateInstance(baseRepositoryMaker, dbResolver.Invoke(null, new object[] { c }))
).As(registerAsForBaseRepositoryTypes).InstancePerRequest(jobTag);
//register readonly repositories
builder.Register(c => Activator.CreateInstance(readonlyRepositoryMarker, dbResolver.Invoke(null, new object[] { c })))
.As(registerAsForReadOnlyRepositoryTypes).InstancePerRequest(jobTag);
}
The following methods try to find DbSet in each DbContext in order to find out the Domain Classes belongs to which DataContext/Database.
public class DbContextResolverHelper
{
private static readonly ConcurrentDictionary<Type, DbName> TypeDictionary = new ConcurrentDictionary<Type, DbName>();
public static DbContext ResolveDbContext<TEntity>(IComponentContext c) where TEntity : class, IEntity
{
var type = typeof(DbSet<TEntity>);
var dbName = TypeDictionary.GetOrAdd(type, t =>
{
var typeOfDatabase1 = typeof(Database1);
var entityInDatabase1 = typeOfDatabase1 .GetProperties().FirstOrDefault(p => p.PropertyType == type);
return entityInDatabase1 != null ? DbName.Db1: DbName.Db2;
});
return c.ResolveKeyed<DbContext>(dbName);
}
}
What about this:
builder.RegisterType<DbContextBase>().As<IUnitOfWork>()
And
DbADataContext: DbContextBase,IUnitOfWork
DbBDataContext: DbContextBase,IUnitOfWork
Or in your registration you can just do something like :
containerBuilder.RegisterGeneric(typeof(DbADataContext<>)).Named("DbADataContext", typeof(IUnitOfWork<>));
containerBuilder.RegisterGeneric(typeof(DbBDataContext<>)).Named("DbBDataContext", typeof(IUnitOfWork<>));
If you want to keep single BaseRepository and its interface, you have to somehow configure, with entity would be handled by which DbContext. It could be done in registration part of application, but in that case you cannot register your BaseRepostory<T> as open generic, but be explicit in your registrations, like this:
containerBuilder.RegisterType<DbADataContext>().Named<IUnitOfWork>("A");
containerBuilder.RegisterType<DbBDataContext>().Named<IUnitOfWork>("B");
containerBuilder.Register(c => new BaseRepository<Team>(c.ResolveNamed<IUnitOfWork>("A")).As<IRepostory<Team>>();
containerBuilder.Register(c => new BaseRepository<Player>(c.ResolveNamed<IUnitOfWork>("B")).As<IRepository<Player>>();
(just proof of concept, code not tested)
Autofac is not smart enough to know "automatically" which unit of work you want to use in each of your repository.
I want to change the connection to a database at runtime in a REST Api. I want to put a variable of the request and let the Api decide which connectionstring to use.
For example:
I put the variable "dbid" with the value "develop" in the request header and send it to the Api.
The Api sees the header and gets the correct connectionstring from the web.config.
I have three layers (data, business, api). The data contains EntityFramework to get and set data. Like this:
public class WebsiteContext : IocDbContext, IWebsites
{
public DbSet<Website> Websites { get; set; }
public IEnumerable<Website> GetAll()
{
return Websites.ToList();
}
}
(IoCDbContext.cs)
public class IocDbContext : DbContext, IDbContext
{
public IocDbContext() : base("develop")
{
}
public void ChangeDatabase(string connectionString)
{
Database.Connection.ConnectionString= connectionString;
}
}
In the business I have a class to retrieve data from the datalayer and do some logical stuff (not needed here, but still good for the story).
public class Websites : IWebsites
{
private readonly Data.Interfaces.IWebsites _websiteContext;
#region Constructor
public Websites(Data.Interfaces.IWebsites websiteContext)
{
_websiteContext = websiteContext;
}
#endregion
#region IWebsites implementation
public IEnumerable<Website> GetWebsites()
{
List<Data.Objects.Website> websiteDtos = _websiteContext.GetAll().ToList();
return websiteDtos.Select(web => web.ToModel()).ToList();
}
#endregion
}
public static class WebsiteMapper
{
public static Website ToModel(this Data.Objects.Website value)
{
if (value == null)
return null;
return new Website
{
Id = value.Id,
Name = value.Name
};
}
}
And, last but not least, the controller:
public class WebsiteController : ApiController
{
private readonly IWebsites _websites;
public WebsiteController(IWebsites websites)
{
_websites = websites;
}
public IEnumerable<Website> GetAll()
{
return _websites.GetWebsites().ToList();
}
}
My Unity configuration:
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<Business.Interfaces.IWebsites, Websites>();
container.RegisterType<IDbContext, IocDbContext>();
container.RegisterType<IWebsites, WebsiteContext>();
// e.g. container.RegisterType<ITestService, TestService>();
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
So as you can see the connection string with the name "develop" is used by default. This will return a website with the name "website". Now I would change the header variable "dbid" to "live". The api should see this and should get the connectionstring that corresponds with the name "live". This last part is something I am trying, but nothing works.
This I tried:
Adding session to webapi. This means I break the stateless idea of REST api: not done
Statics cannot work either, because everyone could get the same connectionstring, but its user specific
Google, but most of the examples don't work for me
Searching StackOverflow... See previous point.
This is driving me crazy! There should be a way to change the connectionstring given by a value in a request header, right?
I have the same scenario in a multi-tenant application I created where I use a different connection string for each tenant.
It doesn't matter the implementation you choose, but you have to determine how you are going to differentiate each request per connection string. In my application, I created a custom route value, and used it in the url to differentiate each request. The important thing is to create whatever this mechanism is, and it needs to be the 1st thing you register in your DI framework, on a per request basis.
For example (using Ninject):
private static void RegisterServicdes(IKernel kernel)
{
kernel.Bind<ISiteContext>().To<SiteContext>().InRequestScope();
kernel.Bind<IDbContextFactory>().To<DbContextFactory>().InRequestScope();
// register other services...
}
Rather than your implementation of your DbContext, I would change to be this, then always create your DbContext instance via a DbContextFactory.
public class IocDbContext : DbContext, IDbContext
{
public IocDbContext(string connectionStringType) : base(connectionStringType) { }
}
Then you need to create a DbContextFactory that you use when you create your DbContext, and take the above class as a dependency. Or you can take the dependency into your services, and pass it into the DbContextFactory instead.
public interface IDbContextFactory
{
TestModel CreateContext();
}
public class DbContextFactory : IDbContextFactory
{
private string _siteType;
public DbContextFactory(ISiteContext siteContext)
{
_siteType = siteContext.Tenant;
}
public TestModel CreateContext()
{
return new TestModel(FormatConnectionStringBySiteType(_siteType));
}
// or you can use this if you pass the IMultiTenantHelper dependency into your service
public static TestModel CreateContext(string siteName)
{
return new TestModel(FormatConnectionStringBySiteType(siteName));
}
private static string FormatConnectionStringBySiteType(string siteType)
{
// format from web.config
string newConnectionString = #"data source={0};initial catalog={1};integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";
if (siteType.Equals("a"))
{
return String.Format(newConnectionString, #"(LocalDb)\MSSQLLocalDB", "DbOne");
}
else
{
return String.Format(newConnectionString, #"(LocalDb)\MSSQLLocalDB", "DbTwo");
}
}
}
Then you can use it like so when accessing your DbContext:
public class DbAccess
{
private IDbContextFactory _dbContextFactory;
public DbAccess(IDbContextFactory dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public void DoWork()
{
using (IocDbContext db = _dbContextFactory.CreateContext())
{
// use EF here...
}
}
}
ISiteContext interface implementation (for using route).
public interface ISiteContext
{
string Tenant { get; }
}
public class SiteContext : ISiteContext
{
private const string _routeId = "tenantId";
private string _tenant;
public string Tenant { get { return _tenant; } }
public SiteContext()
{
_tenant = GetTenantViaRoute();
}
private string GetTenantViaRoute()
{
var routedata = HttpContext.Current.Request.RequestContext.RouteData;
// Default Routing
if (routedata.Values[_routeId] != null)
{
return routedata.Values[_routeId].ToString().ToLower();
}
// Attribute Routing
if (routedata.Values.ContainsKey("MS_SubRoutes"))
{
var msSubRoutes = routedata.Values["MS_SubRoutes"] as IEnumerable<IHttpRouteData>;
if (msSubRoutes != null && msSubRoutes.Any())
{
var subRoute = msSubRoutes.FirstOrDefault();
if (subRoute != null && subRoute.Values.ContainsKey(_routeId))
{
return (string)subRoute.Values
.Where(x => x.Key.Equals(_routeId))
.Select(x => x.Value)
.Single();
}
}
}
return string.Empty;
}
}
API action:
[Route("api/{tenantId}/Values/Get")]
[HttpGet]
public IEnumerable<string> Get()
{
_testService.DoDatabaseWork();
return new string[] { "value1", "value2" };
}
you need to create a factory class for Dynamic picking of connection string.
It is the responsibility of that class to give correct connectionString based on the certain Parameter.
I am using asp net core 1.0 and xunit.
I am trying to write a unit test for some code that uses IMemoryCache. However whenever I try to set a value in the IMemoryCache I get an Null reference error.
My unit test code is like this:
The IMemoryCache is injected into the class I want to test. However when I try to set a value in the cache in the test I get a null reference.
public Test GetSystemUnderTest()
{
var mockCache = new Mock<IMemoryCache>();
return new Test(mockCache.Object);
}
[Fact]
public void TestCache()
{
var sut = GetSystemUnderTest();
sut.SetCache("key", "value"); //NULL Reference thrown here
}
And this is the class Test...
public class Test
{
private readonly IMemoryCache _memoryCache;
public Test(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public void SetCache(string key, string value)
{
_memoryCache.Set(key, value, new MemoryCacheEntryOptions {SlidingExpiration = TimeSpan.FromHours(1)});
}
}
My question is...Do I need to setup the IMemoryCache somehow? Set a value for the DefaultValue? When IMemoryCache is Mocked what is the default value?
IMemoryCache.Set Is an extension method and thus cannot be mocked using Moq framework.
The code for the extension though is available here
public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, MemoryCacheEntryOptions options)
{
using (var entry = cache.CreateEntry(key))
{
if (options != null)
{
entry.SetOptions(options);
}
entry.Value = value;
}
return value;
}
For the test, a safe path would need to be mocked through the extension method to allow it to flow to completion. Within Set it also calls extension methods on the cache entry, so that will also have to be catered for. This can get complicated very quickly so I would suggest using a concrete implementation
//...
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
//...
public Test GetSystemUnderTest() {
var services = new ServiceCollection();
services.AddMemoryCache();
var serviceProvider = services.BuildServiceProvider();
var memoryCache = serviceProvider.GetService<IMemoryCache>();
return new Test(memoryCache);
}
[Fact]
public void TestCache() {
//Arrange
var sut = GetSystemUnderTest();
//Act
sut.SetCache("key", "value");
//Assert
//...
}
So now you have access to a fully functional memory cache.
TLDR
Scroll down to the code snippet to mock the cache setter indirectly (with a different expiry property)
/TLDR
While it's true that extension methods can't be mocked directly using Moq or most other mocking frameworks, often they can be mocked indirectly - and this is certainly the case for those built around IMemoryCache
As I have pointed out in this answer, fundamentally, all of the extension methods call one of the three interface methods somewhere in their execution.
Nkosi's answer raises very valid points: it can get complicated very quickly and you can use a concrete implementation to test things. This is a perfectly valid approach to use. However, strictly speaking, if you go down this path, your tests will depend on the implementation of third party code. In theory, it's possible that changes to this will break your test(s) - in this situation, this is highly unlikely to happen because the caching repository has been archived.
Furthermore there is the possibility that using a concrete implementation with a bunch of dependencies might involve a lot of overheads. If you're creating a clean set of dependencies each time and you have many tests this could add quite a load to your build server (I'm not saying that that's the case here, it would depend on a number of factors)
Finally you lose one other benefit: by investigating the source code yourself in order to mock the right things, you're more likely to learn about how the library you're using works. Consequently, you might learn how to use it better and you will almost certainly learn other things.
For the extension method you are calling, you should only need three setup calls with callbacks to assert on the invocation arguments. This might not be appropriate for you, depending on what you're trying to test.
[Fact]
public void TestMethod()
{
var expectedKey = "expectedKey";
var expectedValue = "expectedValue";
var expectedMilliseconds = 100;
var mockCache = new Mock<IMemoryCache>();
var mockCacheEntry = new Mock<ICacheEntry>();
string? keyPayload = null;
mockCache
.Setup(mc => mc.CreateEntry(It.IsAny<object>()))
.Callback((object k) => keyPayload = (string)k)
.Returns(mockCacheEntry.Object); // this should address your null reference exception
object? valuePayload = null;
mockCacheEntry
.SetupSet(mce => mce.Value = It.IsAny<object>())
.Callback<object>(v => valuePayload = v);
TimeSpan? expirationPayload = null;
mockCacheEntry
.SetupSet(mce => mce.AbsoluteExpirationRelativeToNow = It.IsAny<TimeSpan?>())
.Callback<TimeSpan?>(dto => expirationPayload = dto);
// Act
var success = _target.SetCacheValue(expectedKey, expectedValue,
new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMilliseconds(expectedMilliseconds)));
// Assert
Assert.True(success);
Assert.Equal("key", keyPayload);
Assert.Equal("expectedValue", valuePayload as string);
Assert.Equal(expirationPayload, TimeSpan.FromMilliseconds(expectedMilliseconds));
}
public sealed class NullMemoryCache : IMemoryCache
{
public ICacheEntry CreateEntry(object key)
{
return new NullCacheEntry() { Key = key };
}
public void Dispose()
{
}
public void Remove(object key)
{
}
public bool TryGetValue(object key, out object value)
{
value = null;
return false;
}
private sealed class NullCacheEntry : ICacheEntry
{
public DateTimeOffset? AbsoluteExpiration { get; set; }
public TimeSpan? AbsoluteExpirationRelativeToNow { get; set; }
public IList<IChangeToken> ExpirationTokens { get; set; }
public object Key { get; set; }
public IList<PostEvictionCallbackRegistration> PostEvictionCallbacks { get; set; }
public CacheItemPriority Priority { get; set; }
public long? Size { get; set; }
public TimeSpan? SlidingExpiration { get; set; }
public object Value { get; set; }
public void Dispose()
{
}
}
}
I had a similar issue but I want to disable caching for debugging occasionally as its a pain to keep having to clear the cache. Just mock/fake them yourself (using StructureMap dependency injection).
You could easily use them in you tests as well.
public class DefaultRegistry: Registry
{
public static IConfiguration Configuration = new ConfigurationBuilder()
.SetBasePath(HttpRuntime.AppDomainAppPath)
.AddJsonFile("appsettings.json")
.Build();
public DefaultRegistry()
{
For<IConfiguration>().Use(() => Configuration);
#if DEBUG && DISABLE_CACHE <-- compiler directives
For<IMemoryCache>().Use(
() => new MemoryCacheFake()
).Singleton();
#else
var memoryCacheOptions = new MemoryCacheOptions();
For<IMemoryCache>().Use(
() => new MemoryCache(Options.Create(memoryCacheOptions))
).Singleton();
#endif
For<SKiNDbContext>().Use(() => new SKiNDbContextFactory().CreateDbContext(Configuration));
Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.LookForRegistries();
});
}
}
public class MemoryCacheFake : IMemoryCache
{
public ICacheEntry CreateEntry(object key)
{
return new CacheEntryFake { Key = key };
}
public void Dispose()
{
}
public void Remove(object key)
{
}
public bool TryGetValue(object key, out object value)
{
value = null;
return false;
}
}
public class CacheEntryFake : ICacheEntry
{
public object Key {get; set;}
public object Value { get; set; }
public DateTimeOffset? AbsoluteExpiration { get; set; }
public TimeSpan? AbsoluteExpirationRelativeToNow { get; set; }
public TimeSpan? SlidingExpiration { get; set; }
public IList<IChangeToken> ExpirationTokens { get; set; }
public IList<PostEvictionCallbackRegistration> PostEvictionCallbacks { get; set; }
public CacheItemPriority Priority { get; set; }
public long? Size { get; set; }
public void Dispose()
{
}
}
I also came across this problem in a .Net 5 project and I solved it by wrapping the memory cache and only exposing the functionality that I need. This way I conform to the ISP and it's easier to work with my unit tests.
I created an interface
public interface IMemoryCacheWrapper
{
bool TryGetValue<T>(string Key, out T cache);
void Set<T>(string key, T cache);
}
Implemented the memory cache logic in my wrapper class, using MS dependency injection, so I'm not reliant on those implementation details in my class under test, plus it has the added benefit of adhering to the SRP.
public class MemoryCacheWrapper : IMemoryCacheWrapper
{
private readonly IMemoryCache _memoryCache;
public MemoryCacheWrapper(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public void Set<T>(string key, T cache)
{
_memoryCache.Set(key, cache);
}
public bool TryGetValue<T>(string Key, out T cache)
{
if (_memoryCache.TryGetValue(Key, out T cachedItem))
{
cache = cachedItem;
return true;
}
cache = default(T);
return false;
}
}
I added my memory cache wrapper to the dependency injection and I replaced the system memory cache in my code with the wrapper and that is what I mock out in my tests. All in all a relatively quick job and I think a better structure too.
In my test I then added this so that it mimics the cache updating.
_memoryCacheWrapperMock = new Mock<IMemoryCacheWrapper>();
_memoryCacheWrapperMock.Setup(s => s.Set(It.IsAny<string>(), It.IsAny<IEnumerable<IClientSettingsDto>>()))
.Callback<string, IEnumerable<IClientSettingsDto>>((key, cache) =>
{
_memoryCacheWrapperMock.Setup(s => s.TryGetValue(key, out cache))
.Returns(true);
});
This can be done by mocking the TryGetValue method for IMemoryCache instead of the Set method (Which as mentioned is an extension method and thus cannot be mocked).
var mockMemoryCache = Substitute.For<IMemoryCache>();
mockMemoryCache.TryGetValue(Arg.Is<string>(x => x.Equals(key)), out string expectedValue)
.Returns(x =>
{
x[1] = value;
return true;
});
var converter = new sut(mockMemoryCache);
I have a class say 'AllInvoices', the structure of which is as below :
public class ActiveInvoices
{
public string InvoiceId { get; set; }
public string InvoiceIssueDate { get; set; }
public string InvoiceTransactionDate { get; set; }
public string InvoiceExpiryDate { get; set; }
}
The mapping class for Entity ActiveInvoices is
public class ActiveInvoicesMap : ClassMap<ActiveInvoices>
{
public ActiveInvoicesMap()
{
Id(x => x.InvoiceId);
Map(x => x.InvoiceIssueDate);
Map(x => x.InvoiceTransactionDate);
Map(x => x.InvoiceExpiryDate);
}
}
Now with this entity I search for Active Invoices in database with the following class
public class SearchInvoices
{
public readonly IRepository<ActiveInvoices> latestActiveInvoicesRepository;
public SearchInvoices(IRepository<ActiveInvoices> activeInvoicesRepository)
{
latestActiveInvoicesRepository = activeInvoicesRepository;
}
public List<ActiveInvoices> GetActiveInvoices()
{
var listOfActiveInvoices = latestActiveInvoicesRepository.GetAll();
return listOfActiveInvoices;
}
}
To Search Active Invoices I call the Search Class method 'GetActiveInvoices()' from a workflow class which looks like below :
public class CurrentWorkFlow
{
public void GetActiveInvoices()
{
var invoiceSearch = new SearchInvoices(IRepository <ActiveInvoices> repository);
}
}
Now the issue in hand is that I need to make class 'SearchInvoices' generic to support all other possible types that i would create like 'ExpiredInvoices', 'ArchivedInvoices', 'FutureInvoices' etc and not just only for type 'ActiveInvoices'.
These new types may or may not have the same structure as 'ActiveInvoices'.
I have tried to use dynamic but thought of asking experts around here if they have any better ideas to implement the required functionality
in most optimized generic manner.
Regrets for being very detailed and lengthy in asking but i thought to include as many details as i can. Hope it goes well with you folks.
Couldn't you make a generic repository like this? -
interface IDomain{
}
class ExpiredInvoices: IDomain{
}
class ActiveInvoices: IDomain{
}
interface IRepository{
}
class Repsoitory: IRepository {
public static IList<T> Get<T>() where T: IDomain //default one
{
using (ISession session = OpenEngineSession())
{
return session.Query<T>().ToList();
}
}
public static IList<T> Get<T>(Expression<Func<T, bool>> expression) where T: IDomain // overloaded get with linq predicate
{
using (ISession session = OpenEngineSession())
{
return session.Query<T>().Where(expression).ToList();
}
}
}
Then use it like -
var repo = // get IRepository
var activeInvoices = repo.Get<ActiveInvoices>();
var expiredInvoices = repo.Get<ExpiredInvoices>();
EDIT: As Repository cannot be changed, suggested by OP
If you cannot change the repository, then I would suggest making the search service interface dependent, rather than concrete class -
interface IInvoice{
}
class ExpiredInvoices: IInvoice{
}
class ActiveInvoices: IInvoice{
}
public class SearchInvoices
{
public readonly IRepository<IInvoice> latestActiveInvoicesRepository;
public SearchInvoices(IRepository<IInvoice> activeInvoicesRepository)
{
latestInvoicesRepository = activeInvoicesRepository;
}
public List<T> GetActiveInvoices<T>() where T: IInvoice
{
var listOfActiveInvoices = latestActiveInvoicesRepository.GetAll();
return listOfActiveInvoices;
}
}
Then call like -
var ss = new SearchService(IRepository <ActiveInvoices> repository);
var items = ss.GetActiveInvoices<ActiveInvoices>();
Or,
public class SearchInvoices<T> where T: IInvoice
{
public readonly IRepository<T> latestActiveInvoicesRepository;
public SearchInvoices(IRepository<T> activeInvoicesRepository)
{
latestInvoicesRepository = activeInvoicesRepository;
}
public List<T> GetActiveInvoices()
{
var listOfActiveInvoices = latestActiveInvoicesRepository.GetAll();
return listOfActiveInvoices;
}
}
then call like -
var ss = new SearchService<ActiveInvoices>(IRepository <ActiveInvoices> repository);
var items = ss.GetActiveInvoices();
Whichever suits you.