AutoMapper ProjectTo<>() not finding map - c#

I have a ASP.NET 5 (running on 4.6.2, not Core) application. I wanted to use the ProjectTo<>() method of AutoMapper to project the results from the database to my viewmodels.
I've tried alot of tests, but it seems that the map solely cannot be found when using the ProjectTo<>(). Using mapper.Map<>() on different locations with the same model and viewmodel perfectly works.
I guess there is something wrong with how AutoMapper works with my DI (Autofac), but I can't figure out what.
Anyway, the code:
Startup.Cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
(...)
// Autofac DI
AutofacContainer = AutofacLoader.Configure(services).Build();
return AutofacContainer.Resolve<IServiceProvider>();
}
AutofacLoader.cs
public static ContainerBuilder Configure(IServiceCollection services)
{
var builder = new ContainerBuilder();
(...)
// AutoMapper
builder.RegisterModule<AutoMapperModule>();
if (services != null)
{
builder.Populate(services);
}
return builder;
}
AutoMapperModule.cs
public class AutoMapperModule : Module
{
protected override void Load(ContainerBuilder builder)
{
var mapping = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new Core.Mappings.AutoMapperProfileConfiguration());
cfg.AddProfile(new Dieet.Core.Mappings.AutoMapperProfileConfiguration());
});
builder.RegisterInstance(mapping.CreateMapper()).As<IMapper>().AutoActivate();
}
}
The test that fails with 'Missing map from Patient to PatientViewModel. Create using Mapper.CreateMap'.
[Fact]
public async void InfohosServiceReturnsPatientViewModels()
{
var db = _container.Resolve<IInfohosDb>();
var search = new PaginatedSearchBase();
search.OrderBy = "Naam";
var mapper = _container.Resolve<IMapper>();
var result = await search.PagedResultAsAsync<Patient,PatientViewModel >(null,db.Patienten,mapper);
}
PaginatedSearchBase
public class PaginatedSearchBase
{
public string OrderBy { get; set; }
public bool OrderDescending { get; set; }
public int Page { get; set; } = 1;
public int PageSize { get; set; } = 10;
}
And finally the extension that calls the ProjectTo
public static class PagedResultExtensions
{
public static async Task<PagedResult<T>> PagedResultAsync<T>(this PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context) where T : class
{
int totalCount;
var query = PrepareQuery(vm, whereCollection, context, out totalCount);
return new PagedResult<T>
{
Results = await query.ToListAsync(),
Page = vm.Page,
PageSize = vm.PageSize,
Total = totalCount
};
}
public static async Task<PagedResult<TAs>> PagedResultAsAsync<T, TAs>(this PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context, IMapper mapper) where T : class
{
int totalCount;
var query = PrepareQuery(vm, whereCollection, context, out totalCount);
return new PagedResult<TAs>
{
----------> Results = await query.ProjectTo<TAs>(mapper).ToListAsync(),
Page = vm.Page,
PageSize = vm.PageSize,
Total = totalCount
};
}
private static IQueryable<T> PrepareQuery<T>(PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context,
out int totalCount) where T : class
{
var query = context.AsQueryable();
if (whereCollection != null)
{
foreach (var w in whereCollection)
{
if (w != null)
{
query = query.Where(w);
}
}
}
// Order by
query = query.OrderBy($"{vm.OrderBy} {(vm.OrderDescending ? "DESC" : "ASC")}");
// Total rows
totalCount = query.Count();
// Paging
query = query.Skip((vm.Page - 1)*vm.PageSize).Take(vm.PageSize);
return query;
}
}
For information, I'm using versions:
"Autofac": "4.0.0-rc1-177"
"Autofac.Extensions.DependencyInjection": "4.0.0-rc1-177"
"AutoMapper": "4.2.1"
Edit:
A new test that I did to check if the mappings really work:
var mapper = _container.Resolve<IMapper>();
var p = new Patient();
p.Naam = "Test";
var vm = mapper.Map<PatientViewModel>(p);
vm.Naam.ShouldBeEquivalentTo("Test");
This test passes
Edit 2:
When I use the Map<> in a Select() instead, it works too, so it's really the ProjectTo<>() that fails:
var results = await query.ToListAsync();
return new PagedResult<TAs>
{
Results = results.Select(mapper.Map<TAs>).ToList(),
Page = vm.Page,
PageSize = vm.PageSize,
Total = totalCount
};
This works, but it requires the mapper to be included and not be injected and it doesn't use the ProjectTo that Automapper has for database access...

I ran into the same issue but managed to get it working. This is happening due to the recent move, by Automapper, away from having the entire API use static methods. Now that everything is instance-based, the static extension methods no longer know about the mapping configuration and they now have to be passed into the method. I ended up registering an instance of the MapperConfiguration as IConfigurationProvider (I'm using Unity)
container.RegisterInstance(typeof (IConfigurationProvider), config);
This is injected into my query handler:
[Dependency]
public IConfigurationProvider MapperConfigurationProvider { get; set; }
Finally, the MapperConfigurationProvider is passed to the call to ProjectTo:
.ProjectTo<Payment>(MapperConfigurationProvider);
Hope this helps.

You don't have to specifically add ConfigurationProvider to DI. If you already added the IMapper to DI, than you can read ConfigurationProvider from the Mapper itself. Example: I had the same problem and created a base class containing IMapper that got injected:
public abstract class ServiceBase
{
public IMapper Mapper { get; set; }
}
This class was inherited in all my Services that used AutoMapper. Now every time any of my services needed to Map something, they did so:
return context.SomeEntity
.Where(e => e.Id == filter.Id)
.ProjectTo<EntityDto>(Mapper.ConfigurationProvider).ToList();
With Mapper being injected.
As long as you put the completely configured Mapper in DI, you're ok.
container.Register(Component.For<IMapper>().UsingFactoryMethod(x =>
{
return new AutoMapperConfig().ConfigureMapper();
})
);

Related

NSubstitute, DbContext and a strange issue when trying to write a test

I have a nice extension method for mocking a DbSet:
public static class DbSetExtensions
{
public static DbSet<T> ToDbSet<T>(this IEnumerable<T> data) where T : class
{
var queryData = data.AsQueryable();
var dbSet = Substitute.For<DbSet<T>, IQueryable<T>>();
((IQueryable<T>)dbSet).Provider.Returns(queryData.Provider);
((IQueryable<T>)dbSet).Expression.Returns(queryData.Expression);
((IQueryable<T>)dbSet).ElementType.Returns(queryData.ElementType);
((IQueryable<T>)dbSet).GetEnumerator().Returns(queryData.GetEnumerator());
return dbSet;
}
}
Which I am trying to use in a context file like this:
public class DatabaseContextContext<T> where T: DatabaseContextContext<T>
{
public DatabaseContext DatabaseContext;
protected DatabaseContextContext()
{
DatabaseContext = Substitute.For<DatabaseContext>();
}
public T WhenListSucceeds<TEntity>(IList<TEntity> data) where TEntity : class
{
var dbSet = data.ToDbSet();
DatabaseContext.Set<TEntity>().Returns(dbSet);
return (T)this;
}
public T WhenGetSucceeds<TEntity>(TEntity entity) where TEntity : class
{
var dbSet = new List<TEntity> { entity }.ToDbSet();
DatabaseContext.Set<TEntity>().Returns(dbSet);
return (T)this;
}
}
When I run my test on this method, it fails:
public ActionResult<List<Formula>> ListFormulas(int id) =>
Ok(_databaseContext.Formulas.Where(m => m.AttributeId.Equals(id)).ToList());
with this error message:
System.InvalidCastException : Unable to cast object of type 'Castle.Proxies.ObjectProxy_3' to type 'Microsoft.EntityFrameworkCore.Metadata.Internal.Model'.
So I tried to break it down a bit.
First, I changed my method to this:
public ActionResult<List<Formula>> ListFormulas(int id)
{
var s = _databaseContext.Formulas;
var x = _databaseContext.Formulas.ToList();
var t = _databaseContext.Formulas.Where(m => m.AttributeId.Equals(id)).ToList();
return Ok(t);
}
But when debugging, the code was not getting past the ToList() method. I was still getting the same issue. So I have changed my code to this:
public ActionResult<List<Formula>> ListFormulas(int id)
{
var p = _databaseContext.Set<Formula>();
var q = p.ToList();
var s = _databaseContext.Formulas;
var x = _databaseContext.Formulas.ToList();
var t = _databaseContext.Formulas.Where(m => m.AttributeId.Equals(id)).ToList();
return Ok(t);
}
The first 3 lines of code work, but as soon as it get's to the line var x = _databaseContext.Formulas.ToList(); it fails.
Does anyone have any idea why?
Here is the test:
[TestFixture]
public class ListShould
{
[Test]
public void ReturnList()
{
// Assemble
var services = GenericOrderProviderContext.GivenServices();
var provider = services.WhenCreateOrderProvider();
services.DatabaseContext.Attributes = new List<Attribute>().ToDbSet();
services.DatabaseContext.Set<Attribute>().ReturnsForAnyArgs(_ => new List<Attribute>().ToDbSet());
// Act
var result = provider.List();
// Assert
result.Failure.Should().BeFalse();
result.Result.Count().Should().Be(0);
}
}
I was able to reproduce your error when the db context .Formulas property wasn't configured. If you're using both .Set<Formula>() and .Formulas you'll need to configure both.
I did notice that your set up for the db set enumerator
((IQueryable<T>)dbSet).GetEnumerator().Returns(queryData.GetEnumerator());
cause some behaviour that I've seen before where only the first ToList() invocation returns a result. If you get that, you may need to reset the enumerator or use the Func<CallInfo, IEnumerator<Formula>> Returns overload.

Map a property to list of object using AutoMapper CreateMap

First of all, sorry my English :)
I am using AutoMapper to map between classes.
I have structure of classes like the below.
public class OrderDto {
int Id { get; set; }
}
public class OrderDtoList {
OrderDto[] Orders { get; set; }
}
public class Order {
int Id { get; set; }
}
My issue is that, I want to map OrderDtoList object to List using AutoMapping Profile.
public class OrderMappingProfile : Profile {
public OrderMappingProfile() {
CreateMap<OrderDto, Order>();
CreateMap<OrderDtoList, List<Order>(); // <<<<<<<< I cannot figure out this.
}
}
But I cannot figure out how to write CreateMap<>() function.
Please help.
You can create a class which implements ITypeConverter< OrderDtoList, List > and create the mapping using ConvertUsing method.
public class OrderDtoListMapper : ITypeConverter<OrderDtoList, List<Order>>
{
public List<Order> Convert(OrderDtoList source, List<Order> destination, ResolutionContext context)
{
return context.Mapper.Map<List<Order>>(source.Orders);
}
}
Then, you can create your mapping like this:
public class MapperProfile : Profile
{
public MapperProfile()
{
CreateMap<OrderDto, Order>();
CreateMap<OrderDtoList, List<Order>>().ConvertUsing<OrderDtoListMapper>();
}
}
Hope this is what you were looking for!
It's a little difficult to get it from scratch, but it is possible. You have to create a ITypeConverter<,> and apply this to be used for the conversion.
Be aware, that the ConvertUsing() method has also an overload to simply add an inline function, but you need to have access to the mapper itself to call it for mapping the inner objects to the desired result objects and that's only available with the type converter interface.
public class OrderMappingProfile : Profile
{
public OrderMappingProfile()
{
CreateMap<OrderDto, Order>();
CreateMap<OrderDtoList, List<Order>>().ConvertUsing<CustomConverter>();
}
}
public class CustomConverter : ITypeConverter<OrderDtoList, List<Order>>
{
public List<Order> Convert(OrderDtoList source, List<Order> destination, ResolutionContext context)
{
return context.Mapper.Map<List<Order>>(source.Orders);
}
}
With this in place, you can create the desired list right from the DTO:
public static class Program
{
public static void Main()
{
var config = new MapperConfiguration(cfg => cfg.AddProfile<OrderMappingProfile>());
var mapper = config.CreateMapper();
var orderList = new OrderDtoList { Orders = Enumerable.Range(1, 10).Select(i => new OrderDto { Id = i }).ToArray() };
var orders = mapper.Map<List<Order>>(orderList);
}
}
As Lucian mentioned, there is an overload of ConvertUsing() that provides the context. So you could also inline this, instead of using an own class:
// Use lambda method
CreateMap<OrderDtoList, List<Order>>()
.ConvertUsing((source, _, context) => context.Mapper.Map<List<Order>>(source.Orders));
// Use static method
CreateMap<OrderDtoList, List<Order>>().ConvertUsing(ListConverter);
private static List<Order> ListConverter(OrderDtoList source, List<Order> destination, ResolutionContext context)
{
return context.Mapper.Map<List<Order>>(source.Orders);
}
below is a generic extension, if you have not used an extension before please observe that the GenericExtensions class is a static class
public static class GenericExtensions {
public static object Map<T>(this T source)
{
var fullName = source.GetType().FullName;
var sourceType = source.GetType();
var baseType = ObjectContext.GetObjectType(source.GetType());
var config = new MapperConfiguration(cfg =>
cfg.CreateMap(sourceType, baseType));
var mapper = config.CreateMapper();
var entity = mapper.Map(source, sourceType, baseType);
return entity;
}
}
public static List<T> Map<T>(this List<T> original)
{
var config = new MapperConfiguration(cfg =>
cfg.CreateMap(typeof(T), typeof(T)));
var mapper = config.CreateMapper();
return original.Select(mapper.Map<T, T>).ToList();
}
Usage:
for single entity:
var originalObject = new Order();
originalObject.Id = 4;
var clonedObject = originalObject.Map();
for list of entities:
var objectList = db.ORDERS.ToList();
var clonedList = objectList.Map();
Hope this helps!

Inject different DbContexts into generic repository based on Domain class - Autofac

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.

How to get reference to mapper in type converter?

Is there a way to access a mapper in type converter. Right now, which is bad, I have to do ObjectFactory.GetInstance to get instance of the session from StructureMap. If I was able to get reference to mapper I could use that to get instance of the entity, like mapper.Map and avoid having static method call in type converter.
So this code
public class AddBaseProductCommandConverter : TypeConverter<AddBaseProductService.AddBaseProduct, AddBaseProductCommand>
{
protected override AddBaseProductCommand ConvertCore(AddBaseProductService.AddBaseProduct source)
{
var session = ObjectFactory.GetNamedInstance<ISession>("PRDProd");
var marketingPlan = session.Get<MarketingPlan>(source.BaseProductInfo.MarketingPlanId);
var baseProductTemplate = session.Get<BaseProductTemplate>(source.BaseProductInfo.Code);
var benefitPeriod = session.Get<BenefitPeriod>(source.BaseProductInfo.BenefitPeriodCode);
var insuranceServiceType = session.Get<InsuranceServiceType>(source.BaseProductInfo.ServiceTypeCode);
var command = new AddBaseProductCommand
{
MarketingPlan = marketingPlan,
BaseProductTemplate = baseProductTemplate,
BenefitPeriod = benefitPeriod,
InsuranceServiceType = insuranceServiceType
};
return command;
}
}
Could be rewritten like this (I build a map for each persistent type that NHIbernate recognizes, during configuration phase)
public class AddBaseProductCommandConverter : TypeConverter<AddBaseProductService.AddBaseProduct, AddBaseProductCommand>
{
protected override AddBaseProductCommand ConvertCore(AddBaseProductService.AddBaseProduct source)
{
var mapper = ????
var marketingPlan = mapper.Map<int,MarketingPlan(source.BaseProductInfo.MarketingPlanId);
var baseProductTemplate = mapper.Map<string,BaseProductTemplate>(source.BaseProductInfo.Code);
... and so on ...
return command;
}
}
You can implement ITypeConverter instead of TypeConverter, then use the argument context to get the mapper using context.Engine.Mapper, something like this:
public class AddBaseProductCommandConverter : ITypeConverter<AddBaseProductService.AddBaseProduct, AddBaseProductCommand>
{
public AddBaseProductCommand Convert(ResolutionContext context)
{
if (context.SourceValue == null || !(context.SourceValue is AddBaseProductService.AddBaseProduct))
throw new AutoMapperMappingException();
var mapper = context.Engine.Mapper;
...
}
}
For anyone stumbling on this who is using a a more recent version of Automapper (e.g. 8), the ITypeConverter and ResolutionContext definitions have changed a bit from the previous answer. You can access the mapper through the Mapper member on the context. e.g.
public class AddBaseProductCommandConverter : ITypeConverter<AddBaseProductService.AddBaseProduct, AddBaseProductCommand>
{
public AddBaseProductCommand Convert(AddBaseProductService.AddBaseProduct source, AddBaseProductCommand destination, ResolutionContext context)
{
var mapper = context.Mapper;
...
}
}

Creating a hybrid of a mock and an anonymous object using e.g. Moq and AutoFixture?

I encountered a class during my work that looks like this:
public class MyObject
{
public int? A {get; set;}
public int? B {get; set;}
public int? C {get; set;}
public virtual int? GetSomeValue()
{
//simplified behavior:
return A ?? B ?? C;
}
}
The issue is that I have some code that accesses A, B and C and calls the GetSomeValue() method (now, I'd say this is not a good design, but sometimes my hands are tied ;-)). I want to create a mock of this object, which, at the same time, has A, B and C set to some values. So, when I use moq as such:
var m = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock };
lets me setup a result on GetSomeValue() method, but all the properties are set to null (and setting up all of them using Setup() is quite cumbersome, since the real object is a nasty data object and has more properties than in above simplified example).
So on the other hand, using AutoFixture like this:
var fixture = new Fixture();
var anyMyObject = fixture.CreateAnonymous<MyObject>();
Leaves me without the ability to stup a call to GetSomeValue() method.
Is there any way to combine the two, to have anonymous values and the ability to setup call results?
Edit
Based on nemesv's answer, I derived the following utility method (hope I got it right):
public static Mock<T> AnonymousMock<T>() where T : class
{
var mock = new Mock<T>();
fixture.Customize<T>(c => c.FromFactory(() => mock.Object));
fixture.CreateAnonymous<T>();
fixture.Customizations.RemoveAt(0);
return mock;
}
This is actually possible to do with AutoFixture, but it does require a bit of tweaking. The extensibility points are all there, but I admit that in this case, the solution isn't particularly discoverable.
It becomes even harder if you want it to work with nested/complex types.
Given the MyObject class above, as well as this MyParent class:
public class MyParent
{
public MyObject Object { get; set; }
public string Text { get; set; }
}
these unit tests all pass:
public class Scenario
{
[Fact]
public void CreateMyObject()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyObject>();
Assert.NotNull(actual.A);
Assert.NotNull(actual.B);
Assert.NotNull(actual.C);
}
[Fact]
public void MyObjectIsMock()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyObject>();
Assert.NotNull(Mock.Get(actual));
}
[Fact]
public void CreateMyParent()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyParent>();
Assert.NotNull(actual.Object);
Assert.NotNull(actual.Text);
Assert.NotNull(Mock.Get(actual.Object));
}
[Fact]
public void MyParentIsMock()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyParent>();
Assert.NotNull(Mock.Get(actual));
}
}
What's in MockHybridCustomization? This:
public class MockHybridCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(
new MockPostprocessor(
new MethodInvoker(
new MockConstructorQuery())));
fixture.Customizations.Add(
new Postprocessor(
new MockRelay(t =>
t == typeof(MyObject) || t == typeof(MyParent)),
new AutoExceptMoqPropertiesCommand().Execute,
new AnyTypeSpecification()));
}
}
The MockPostprocessor, MockConstructorQuery and MockRelay classes are defined in the AutoMoq extension to AutoFixture, so you'll need to add a reference to this library. However, note that it's not required to add the AutoMoqCustomization.
The AutoExceptMoqPropertiesCommand class is also custom-built for the occasion:
public class AutoExceptMoqPropertiesCommand : AutoPropertiesCommand<object>
{
public AutoExceptMoqPropertiesCommand()
: base(new NoInterceptorsSpecification())
{
}
protected override Type GetSpecimenType(object specimen)
{
return specimen.GetType();
}
private class NoInterceptorsSpecification : IRequestSpecification
{
public bool IsSatisfiedBy(object request)
{
var fi = request as FieldInfo;
if (fi != null)
{
if (fi.Name == "__interceptors")
return false;
}
return true;
}
}
}
This solution provides a general solution to the question. However, it hasn't been extensively tested, so I'd love to get feedback on it.
Probably there is a better why, but this works:
var fixture = new Fixture();
var moq = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock };
moq.Setup(m => m.GetSomeValue()).Returns(3);
fixture.Customize<MyObject>(c => c.FromFactory(() => moq.Object));
var anyMyObject = fixture.CreateAnonymous<MyObject>();
Assert.AreEqual(3, anyMyObject.GetSomeValue());
Assert.IsNotNull(anyMyObject.A);
//...
Initially I tried to use fixture.Register(() => moq.Object); instead of fixture.Customize but it registers the creator function with OmitAutoProperties() so it wouldn't work for you case.
As of 3.20.0, you can use AutoConfiguredMoqCustomization. This will automatically configure all mocks so that their members' return values are generated by AutoFixture.
var fixture = new Fixture().Customize(new AutoConfiguredMoqCustomization());
var mock = fixture.Create<Mock<MyObject>>();
Assert.NotNull(mock.Object.A);
Assert.NotNull(mock.Object.B);
Assert.NotNull(mock.Object.C);

Categories

Resources