I have some code that selects a property using reflection. It works fine but I can't unit test it. Apparently, the mock that it creates does not have any properties.
Can anyone tell me how to get the property from a Mocked object using reflection? (Or explain why it would not be possible?)
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
I'm using this for a copy class which allows to copy data for any entity type and expects a UnitOfWork that contains a IRepository where T is the type that you want to copy.
public class TableCopier<T> : ICopier where T : EntityBase
{
public IUnitOfWork From { get; set; }
public IUnitOfWork To { get; set; }
public virtual int Copy(IProgressReporter progres = null)
{
lock (this)
{
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
//...
}
}
}
The Unit of Work looks like this:
public interface IUnitOfWork
{
IRepository<Parameter> ParameterRepository { get; }
IRepository<Company> CompanyRepository { get; }
IRepository<CompanyAccountancynumber> CompanyAccountancynumberRepository { get; }
//...
And finally the unit test:
[Test]
public void DefaultCopy()
{
// (removed some code)
var to = MockRepository.GenerateStrictMock<IUnitOfWork>();
var target = MockRepository.GenerateStrictMock<IRepository<CompanyDataset>>();
to.Stub(x => x.CompanyDatasetRepository).Return(target);
var inserted = new List<CompanyDataset>();
target.Stub(x => x.BulkInsert(null))
.IgnoreArguments()
.WhenCalled(x => inserted.AddRange(x.Arguments[0] as IEnumerable<CompanyDataset>));
//ACT
var copier = new TableCopier<CompanyDataset>() as ICopier;
copier.Copy(from, to, map, log, null, CancellationToken.None );
I found the problem:
The properties are available as explicit properties.
And to access these kind of properties you need the interface map.
This means I should change this:
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
To this:
var target = To.GetType().GetInterfaceMap(typeof(IUnitOfWork))
.InterfaceMethods
.Single(x => (typeof(IRepository<T>).IsAssignableFrom(x.ReturnType)))
.Invoke(To, null) as IRepository<T>;
My own implementations of IUnitOfWork used 'implicit' properties, so that explains why that worked and the unit test failed. Also using the map is generally better (/more correct) (-> no chance on problems if I would ever make another implementation of the interface with explicit props). So I suppose it was a good unit test :)
Regards
Related
I am writing a project that takes a JSON file to deserialize it and Build the data and process them then save it to the database. Now I want to write a unit test using Mock for this class but I don't know how to do this because in this method I only equalize the field in my DTO and database
this is my order Dto
public class OrderDto
{
public int Code { get; set; }
public int CustomerCode { get; set; }
public int StoreCode { get; set; }
public string OrderDate { get; set; }
public string OrderStatus { get; set; }
public string DeliveryDate { get; set; }
}
This is my order builder class
public class OrderBuilder
{
static PracticeEntities _context;
public OrderBuilder(PracticeEntities4 context)
{
_context = context;
}
public static CustomersOrder OrderBuild(OrderDto dto)
{
//using (var context = new PracticeEntities4())
//{
var oldStoreId = _context.Stores.FirstOrDefault(e => e.Code == dto.StoreCode).Id;
var oldCustomerId = _context.Customers.FirstOrDefault(e => e.Code == dto.CustomerCode).Id;
return new CustomersOrder()
{
OrderDate = Convert.ToDateTime(dto.OrderDate),
OrderStatus = dto.OrderStatus,
DeliveryDate = Convert.ToDateTime(dto.DeliveryDate),
CustomerId = oldCustomerId,
StoreId = oldStoreId,
Code = dto.Code
};
//};
}
}
UPDATED
First thing to note is that a static class can't be unit tested. So the method to be tested shall be modified as such below shown. Also, the dbcontext i.e. IPracticeEntities4 interface which is implemented in the PracticeEntities4 class need to be injected so that it can be mocked.
public class PracticeEntities4:IPracticeEntities4, DbContext
{
....
}
public class ClassMethod2BTested
{
IPracticeEntities4 _context; //declare the context
public ClassMethod2BTested(IPracticeEntities4 context) // inject the context
{
_context=context; // set the context to local variable
}
public CustomersOrder OrderBuild(OrderDto dto)
{
//using (var context = new PracticeEntities4()) // remove this
{
var oldStoreId = _context.Stores.FirstOrDefault(e => e.Code == dto.StoreCode).Id;
var oldCustomerId = _context.Customers.FirstOrDefault(e => e.Code dto.CustomerCode).Id;
return new CustomersOrder()
{
OrderDate = Convert.ToDateTime(dto.OrderDate),
OrderStatus = dto.OrderStatus,
DeliveryDate = Convert.ToDateTime(dto.DeliveryDate),
CustomerId = oldCustomerId,
StoreId = oldStoreId,
Code = dto.Code
};
};
}
}
Now the above method can be united tested.
For unit test, Please check the sample here: Mocking EF DbContext with Moq
Added
Please check this code:
github
Since you are using entity framework, I think this article will help you: https://learn.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking
Create your own test doubles – This approach involves writing your own in-memory implementation of your context and DbSets. This gives you a lot of control over how the classes behave but can involve writing and owning a reasonable amount of code.
Use a mocking framework to create test doubles – Using a mocking framework (such as Moq) you can have the in-memory implementations of your context and sets created dynamically at runtime for you.
So with the first way you can simulate a db call with specific data and make sure the result returns successfully.
These tests, while not securing business errors protect your code from missing null checks and runtime errors like that.
I have implemented a Repository Pattern using an onion Architecture and Entity Framework for Data Access, and now I want to test it using Moq. I just asked a question on SO and with the answer I am now more confused (the answer was good but I have very poor comprehension of how to mock even after reading doc). What I want to do is test Repository method Get(long id). My repository constructor takes a DbContext as parameter(called PrincipalServerContext, so I was suggested to mock the context in order to test my Repository. Let's say this is my Repository:
public class PrincipalServerContext : DbContext
{
public DbSet<Web_Documents> WebDoc { get; set; }
public PrincipalServerContext()
: base("name=PrincipalServerDB")
{
Database.SetInitializer(new Initializer());
}
}
Now one of my POCOs Web_Documents (EF entity):
public class Web_Documents
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long IDDocument { get; set; }
[Required]
[MaxLength(255)]
public string NomDocument { get; set; }
[Required]
public long IDCategorie { get; set; }
[ForeignKey("IDCategorie")]
public Web_Categories cat { get; set; }
[Required]
[MaxLength(255)]
public string Lien { get; set; }
[MaxLength(50)]
public string Type { get; set; }
public virtual ICollection<Web_Profils> Profils { get; set; }
}
And finally my Repository method (knowing that repository is generic, I use POCOs as Generic Types):
public T Get(long id)
{
ObjectContext objContext = ((IObjectContextAdapter)context).ObjectContext;
ObjectSet<T> set = objContext.CreateObjectSet<T>();
IEnumerable<string> keyNames = set.EntitySet.ElementType
.KeyMembers
.Select(k => k.Name);
if (keyNames.Count() > 1)
return null;
else
{
string idName = keyNames.ElementAt(0); // For Document would be IDDocument
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, idName);
var idValue = Expression.Constant(id, id.GetType());
var equal = Expression.Equal(property, idValue);
var predicate = Expression.Lambda<Func<T, bool>>(equal, parameter);
return entities.SingleOrDefault(predicate);
//Returns the corresponding entity to 'id' and 'T'
}
}
This builds an expression with appropriate ID names because every table has a different ID Name (company's policy).
From what I was told here Should this case of Assert.AreSame return true? I understand that I have to build a return type for the Mock object, but my context class is so thin I don't have any methods or anything, just a DbSet.
So I tried this as a test but it probably makes no sense since it failed (I'm just really lost and don't understand it):
Mock<PrincipalServerContext> moqContext;
public void IdExists(){
moqContext = new Mock<PrincipalServerContext>();
var set = new Mock<DbSet<Web_Documents>>();
moqContext.Setup(c => c.Set<Web_Documents>()).Returns(set.Object);
repoDoc = new Repository<Web_Documents>(moqContext.Object);
var testDoc = repoDoc.Get(1L);
Assert.AreEqual(testDoc.NomDocument, "Ajouter une catégorie");
}
Say I would want to make a simple test to find if the ID searched corresponds to my DB entry, how should I set the moqContext object I am trying to define? In examples I see they usually have methods for their mocked objects, but here none, so I found this Mocking DbContext for TDD Repository which made me try this test.
Thanks for your help!
Here's an example of using the in-memory database.
First you create an request an instance of the mock unit of work.
[TestMethod]
public async Task ExampleTest() {
//arrange
Mock<IUnitOfWork> mockUow = MockUowFactory.Get(nameof(ExampleTest));
//act
using (var app = YOURAPP(mockUow.Object)){
app.METHODUNDERTEST();
}
//assert
...
}
Then you build up the mock unit of work. According to what I've read the separate context is needed (one for seeding, one for testing). The MockEntityFactory just returns an array of dummy data which is used to populate the dbsets in our InMemoryDatabase.
public class MockUowFactory {
public static Mock<IUnitOfWork> Get(string dbName) {
DbContextOptions<YOUR CONTEXT> options = new DbContextOptionsBuilder<YOUR CONTEXT>()
.UseInMemoryDatabase(databaseName: dbName)
.Options;
using (var seedContext = new YOURCONTEXT(options)) {
seedContext.YOURENTITY.AddRange(MockEntityFactory.YOURENTITY);
seedContext.SaveChanges();
}
var context = new YOURCONTEXT(options);
var mockUow = new Mock<IUnitOfWork>();
mockUow.Setup(m => m.Context).Returns(context);
mockUow.Setup(m => m.Save()).Returns(() => context.SaveChanges().ToString());
return mockUow;
}
}
I then pass this unit of work through the necessary layers and don't need to do anything special to test my production code.
I've been able to mock DbSet's from entity framework with Moq using this link.
However, I would now like to know how I could mock the call to SqlQuery. Not sure if this is possible or how as it relies on the mocked db context knowing what "query" is being called.
Below is what I am trying to mock.
var myObjects = DbContext.Database
.SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", "some_value")
.ToList();
I currently haven't tried anything as did not know how to start mocking this example.
The mocking of the DbSet is below and to re-iterate, I can correctly mock returning a DbSet of MyObject's but now am trying to mock a SqlQuery that returns a list of MyObject's.
var dbContext = new Mock<MyDbContext>();
dbContext.Setup(m => m.MyObjects).Returns(mockObjects.Object);
dbContext.Setup(m => m.Database.SqlQuery... something along these lines
Database.SqlQuery<T> is not marked as virtual, but Set<T>.SqlQuery is marked as virtual.
Based on Database.SqlQuery<T> documentation
The results of this query are never tracked by the context even if the
type of object returned is an entity type. Use the 'SqlQuery(String,
Object[])' method to return entities that are tracked by the
context.
and Set<T>.SqlQuery documentation
By default, the entities returned are tracked by the context; this can
be changed by calling AsNoTracking on the DbRawSqlQuery returned.
then the Database.SqlQuery<T>(String, Object[]) should be equivalent with Set<T>.SqlQuery(String, Object[]).AsNoTracking() (only if T is EF entity, not a DTO / VM).
So if you can replace the implementation into:
var myObjects = DbContext
.Set<MyObject>()
.SqlQuery("exec [dbo].[my_sproc] {0}", "some_value")
.AsNoTracking()
.ToList();
you can mock it as follow
var list = new[]
{
new MyObject { Property = "some_value" },
new MyObject { Property = "some_value" },
new MyObject { Property = "another_value" }
};
var setMock = new Mock<DbSet<MyObject>>();
setMock.Setup(m => m.SqlQuery(It.IsAny<string>(), It.IsAny<object[]>()))
.Returns<string, object[]>((sql, param) =>
{
// Filters by property.
var filteredList = param.Length == 1
? list.Where(x => x.Property == param[0] as string)
: list;
var sqlQueryMock = new Mock<DbSqlQuery<MyObject>>();
sqlQueryMock.Setup(m => m.AsNoTracking())
.Returns(sqlQueryMock.Object);
sqlQueryMock.Setup(m => m.GetEnumerator())
.Returns(filteredList.GetEnumerator());
return sqlQueryMock.Object;
});
var contextMock = new Mock<MyDbContext>();
contextMock.Setup(m => m.Set<MyObject>()).Returns(setMock.Object);
You can add a virtual method to your database context that you can override in unit tests:
public partial class MyDatabaseContext : DbContext
{
/// <summary>
/// Allows you to override queries that use the Database property
/// </summary>
public virtual List<T> SqlQueryVirtual<T>(string query)
{
return this.Database.SqlQuery<T>(query).ToList();
}
}
The Database property and SqlQuery method are not marked as virtual so they can't be mocked (using Moq; you could use a different library that can account for this but that may be more inertia than you'd like).
You'd need to use some sort of abstraction to get around this, such as by wrapping the entire query of the database in a helper class:
public interface IQueryHelper
{
IList<MyObject> DoYourQuery(string value);
}
public class QueryHelper : IQueryHelper
{
readonly MyDbContext myDbContext;
public QueryHelper(MyDbContext myDbContext)
{
this.myDbContext = myDbContext;
}
public IList<MyObject> DoYourQuery(string value)
{
return myDbContext.Database.SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", value).ToList();
}
}
Now the method you are testing becomes:
public void YourMethod()
{
var myObjects = queryHelper.DoYourQuery("some_value");
}
Then you'd inject the IQueryHelper in the constructor of the class you're testing and mock that.
You're going to be missing test coverage on DoYourQuery, but the now the query is so simple there are obviously no deficiencies.
Should anyone come across this. I solved this with a few approaches. Just another way to address this.
My context is abstracted through an interface. I only need a few of the methods:
public interface IDatabaseContext
{
DbSet<T> Set<T>() where T : class;
DbEntityEntry<T> Entry<T>(T entity) where T : class;
int SaveChanges();
Task<int> SaveChangesAsync();
void AddOrUpdateEntity<TEntity>(params TEntity[] entities) where TEntity : class;
}
All of my database access is through async methods. Which brings up a whole new set of problems when trying to mock it. Fortunately - it has been answered here. The exception you get is related to the missing mock for IDbAsyncEnumerable. Using the solution provided - I just extended it a bit more so that I had a helper to return a Mock> object that mocked all of the properties expected.
public static Mock<DbSqlQuery<TEntity>> CreateDbSqlQuery<TEntity>(IList<TEntity> data)
where TEntity : class, new()
{
var source = data.AsQueryable();
var mock = new Mock<DbSqlQuery<TEntity>>() {CallBase = true};
mock.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(source.Expression);
mock.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(source.ElementType);
mock.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator());
mock.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(source.Provider));
mock.As<IDbAsyncEnumerable<TEntity>>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator()));
mock.As<IDbSet<TEntity>>().Setup(m => m.Create()).Returns(new TEntity());
mock.As<IDbSet<TEntity>>().Setup(m => m.Add(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Add(i); return i; });
mock.As<IDbSet<TEntity>>().Setup(m => m.Remove(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Remove(i); return i; });
return mock;
}
Finally - using the solution provided by #Yulium Chandra - my testing of raw SQL with mocked context looks like:
public Mock<DbSet<TestModel>> MockDbSet { get; }
....
MockDbSet.Setup(x => x.SqlQuery(It.IsAny<string>))
.Returns<string,object[]>
((sql, param) =>
{
var sqlQueryMock = MockHelper.CreateDbSqlQuery(Models);
sqlQueryMock.Setup(x => x.AsNoTracking())
.Returns(sqlQueryMock.Object);
return sqlQueryMock.Object;
});
Before asking here I read all post relative to the subject but I can't find any solution.
I removed all my domain complexity and I'm down to the following simple issue:
I have 2 class (in a domain file Domain.cs) :
public abstract class BaseClass
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
}
public class ASubClass : BaseClass
{
public ASubClass() {}
public virtual string prop { get; set; }
}
My Mapping is (In another file Mapping.cs):
public class BaseClassMap : ClassMap<BaseClass>
{
public BaseClassMap ()
{
Id(x => x.ID).GeneratedBy.HiLo("1000");
Map(x => x.Name);
DiscriminateSubClassesOnColumn("Type");
}
}
public class ASubClassMap : SubclassMap<ASubClass>
{
public ASubClassMap ()
{
Map(x => x.prop);
DiscriminatorValue(1);
}
}
First I let NHibernate create the DB for me :
var cfg = new NHibernate.Cfg.Configuration();
Fluently.Configure(cfg)
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(connectionstring)
.ShowSql())
.Mappings(m => m
.FluentMappings.AddFromAssemblyOf<BaseClass>()).BuildConfiguration();
new SchemaExport(cfg).Execute(false,true,false);
This works great, then my unit test is the following :
I build a session Factory within a Repository and use the Session/Transaction to try to save an empty SubClass:
var contract = new ASubClass();
IRepository<ASubClass> repository = new Repository<ASubClass>();
repository.Add(contract); >>> BUG HERE
This in fact bug on the line(inside the repo) : session.Save(contract);
I tried to copy/paste my Mapping.cs into Domain.cs or change the attribute of
.FluentMappings.AddFromAssemblyOf<BaseClass> to .FluentMappings.AddFromAssemblyOf<BaseClassMap>
I also tried to change the hierarchy of subclass by removing the Discriminator and the
DiscriminateSubClassesOnColumn keyword.
Didnt work so far.
Thanks.
You code actually works fine for me.
There must be something wrong with your configuration or your repository or how you use the configuration+repository within your unit tests. I mean, you create a new Repository, does this use the configuration you posted? 100% sure?
Are you using your cfg to build the ISessionFactory to be used by your repository? e.g.
var factory = cfg.BuildSessionFactory();
Maybe check that all mappings are in the same assembly, otherwise add both assemblies.
I have a services that is calling another services. Both of the services are using "the same classes". The classes are named same and have the same properties but has different namespace so I need to use AutoMapper to map from one of the type to the other type.
No it's pretty simple since all I have to do is the CreateMap<>, but the problem is that we have around hundreds of classes that I manually needs to write the CreateMap<> from, and it's works wired to me. Isn't there any Auto CreateMap function. So if I say CreateMap() then AutoMapper workes thru Organisation and finds all classes and automatically does the CreateMap for these Classes and it's subclasses etc etc…
Hope for a simple solution, or I guess some reflection can fix it...
Just set CreateMissingTypeMaps to true in the options:
var dto = Mapper.Map<FooDTO>
(foo, opts => opts.CreateMissingTypeMaps = true);
If you need to use it often, store the lambda in a delegate field:
static readonly Action<IMappingOperationOptions> _mapperOptions =
opts => opts.CreateMissingTypeMaps = true;
...
var dto = Mapper.Map<FooDTO>(foo, _mapperOptions);
UPDATE:
The approach described above no longer works in recent versions of AutoMapper.
Instead, you should create a mapper configuration with CreateMissingTypeMaps set to true and create a mapper instance from this configuration:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = true;
// other configurations
});
var mapper = config.CreateMapper();
If you want to keep using the old static API (no longer recommended), you can also do this:
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
// other configurations
});
UPDATE 2 - Automapper 9 and later:
Starting from Automapper version 9.0, the CreateMissingTypeMaps API was removed. Automapper documentation now suggests to explicitly configure maps, manually or using reflection.
https://docs.automapper.org/en/stable/9.0-Upgrade-Guide.html#automapper-no-longer-creates-maps-automatically-createmissingtypemaps-and-conventions
CreateMissingTypeMaps can be set within your profile. It's however recommended to explicitly use CreateMap for each mapping and call AssertConfigurationIsValid in your unit tests for each profile to prevent silent errors.
public class MyProfile : Profile {
CreateMissingTypeMaps = true;
// Mappings...
}
AutoMapper has a DynamicMap method which you might be able to use: here's an example unit test illustrating it.
[TestClass]
public class AutoMapper_Example
{
[TestMethod]
public void AutoMapper_DynamicMap()
{
Source source = new Source {Id = 1, Name = "Mr FooBar"};
Target target = Mapper.DynamicMap<Target>(source);
Assert.AreEqual(1, target.Id);
Assert.AreEqual("Mr FooBar", target.Name);
}
private class Target
{
public int Id { get; set; }
public string Name { get; set; }
}
private class Source
{
public int Id { get; set; }
public string Name { get; set; }
}
}
Set CreateMissingTypeMaps option to true. This is package AutoMapper.Extensions.Microsoft.DependencyInjection's example for ASP.NET Core:
public class Startup {
//...
public void ConfigureServices(IServiceCollection services) {
//...
services.AddAutoMapper(cfg => { cfg.CreateMissingTypeMaps = true; });
//...
}
//...
}
In case someone is still interested in this topic, I've created a NuGet package that gives the automatic mapping functionality since AutoMapper removed it in a certain version.
It's available under wakiter.AutoMapper.Extensions name.
To use it, invoke the CreateAutoMap extension method and it'll do the work for you.
Today I needed this in some generic code as well. I tried something like this:
private static IMapper CreateMapper<T1, T2>()
{
return new MapperConfiguration(cfg => FillMapperConfig(cfg, typeof(T1), typeof(T2)))
.CreateMapper();
}
private static void FillMapperConfig(IMapperConfigurationExpression cfg, Type T1, Type T2)
{
if (T1 == T2)
{
return;
}
cfg.CreateMap(T1, T2);
foreach (PropertyInfo propertyInfo in T1.GetProperties())
{
PropertyInfo correspondingProperty =
T2.GetProperties()
.FirstOrDefault(p =>
p.Name == propertyInfo.Name);
if (correspondingProperty != null)
{
if (propertyInfo.PropertyType.IsGenericType &&
correspondingProperty.PropertyType.IsGenericType)
{
FillMapperConfig(
cfg,
propertyInfo.PropertyType.GetGenericArguments()[0],
correspondingProperty.PropertyType.GetGenericArguments()[0]);
}
else if (propertyInfo.PropertyType.IsClass &&
correspondingProperty.PropertyType.IsClass)
{
FillMapperConfig(
cfg,
propertyInfo.PropertyType,
correspondingProperty.PropertyType);
}
}
}
}
Then I can do something like this:
IMapper mapper = CreateMapper<ClassA, ClassB>();
Which creates a map from ClassA to ClassB with all sub properties of ClassA and ClassB if they have the same name and recursively for sub sub properties.
Example:
public class ClassA {
public int IntProperty { get; set; }
public ClassASubProperty SubProperty { get; set; }
public List<ClassAListItem> ListItems { get; set; }
}
public class ClassB {
public int IntProperty { get; set; }
public ClassBSubProperty SubProperty { get; set; }
public List<ClassBListItem> ListItems { get; set; }
}
This should result in the IMapper equivalent:
new MapperConfiguration(cfg => {
cfg.CreateMap<ClassA, ClassB>();
cfg.CreateMap<ClassASubProperty, ClassBSubProperty>();
cfg.CreateMap<ClassAListItem, ClassBListItem>()
}).CreateMapper();