Is it possible to use a list of enums in an project using EF Core to store the data?
My enum:
public enum AudienceType
{
Child,
Teen,
[Display(Name ="Young Adult")]
YoungAdult,
Adult,
Elderly
}
Class using enum:
public class Restaurant
{
public int Id { get; set; }
[Required, StringLength(80)]
public string Name { get; set; }
[Display(Name="Select the target audiences")]
public List<AudienceType> AudienceTypes { get; set; }
}
But when I run add-migration AudienceType, I get the following error:
PM> add-migration AudienceType
Build started...
Build succeeded.
System.InvalidOperationException: The property 'Restaurant.AudienceTypes' could not be mapped, because it is of type 'List<AudienceType>' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidatePropertyMapping(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.SqlServer.Internal.SqlServerModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.ValidatingConvention.ProcessModelFinalized(IConventionModelBuilder modelBuilder, IConventionContext`1 context)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalized(IConventionModelBuilder modelBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalized(IConventionModelBuilder modelBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel()
at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel()
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder)
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_3(IServiceProvider p)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The property 'Restaurant.AudienceTypes' could not be mapped, because it is of type 'List<AudienceType>' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Update:
I see how you can use Value Conversions to handle enums, like below:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var converter = new EnumToStringConverter<AudienceType>();
modelBuilder
.Entity<Restaurant>()
.Property(e => e.AudienceTypes)
.HasConversion(converter);
}
But how do you handle a list of enums?
To represent list of enum values as comma separated string you can use Value Conversions.
Simplest "inline" option:
// In DbContext class
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Restaurant>()
.Property(e => e.AudienceTypes)
.HasConversion(
v => string.Join(",", v.Select(e => e.ToString("D")).ToArray()),
v => v.Split(new[] { ',' })
.Select(e => Enum.Parse(typeof(AudienceType), e))
.Cast<AudienceType>()
.ToList()
);
}
I found this post helpful, which led to the following solution:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var converter = new EnumCollectionJsonValueConverter<AudienceType>();
var comparer = new CollectionValueComparer<AudienceType>();
modelBuilder.Entity<Restaurant>()
.Property(e => e.AudienceTypes)
.HasConversion(converter)
.Metadata.SetValueComparer(comparer);
}
which uses the following two classes:
public class EnumCollectionJsonValueConverter<T> : ValueConverter<ICollection<T>, string> where T : Enum
{
public EnumCollectionJsonValueConverter() : base(
v => JsonConvert
.SerializeObject(v.Select(e => e.ToString()).ToList()),
v => JsonConvert
.DeserializeObject<ICollection<string>>(v)
.Select(e => (T)Enum.Parse(typeof(T), e)).ToList())
{
}
}
public class CollectionValueComparer<T> : ValueComparer<ICollection<T>>
{
public CollectionValueComparer() : base((c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), c => (ICollection<T>)c.ToHashSet())
{
}
}
Because enum is not a class, you need to write a new class to encapsulate it.
You can create a new class :
public class Audience
{
public int Id { get; set; }
public AudienceType AudienceType { get; set; }
}
Then in your Restaurant:
public class Restaurant
{
public int Id { get; set; }
[Required, StringLength(80)]
public string Name { get; set; }
[Display(Name="Select the target audiences")]
public List<Audience> Audiences{ get; set; }
}
If you need to convert it,you can convert in dbcontext.
modelBuilder.Entity<Audience>().Property(b => b.AudienceType).HasConversion(
v => v.ToString(), v => (AudienceType)Enum.Parse(typeof(AudienceType), v));
Then migration and update database.You will get a one-to-many relationship between them.
Related
I am using Entity Framework 6 and Moq framework. Currently I am writing a few Unit Tests where in each test I need to set up a dataset with the appropriate type for each test.
One of the unit tests looks like this:
[TestMethod]
public async Task GetAllCaseCategories_WithEmptyDataset_ReturnsEmpty()
{
var data = new List<DataAccessLayer.Tables.CaseCategory>(){};
var mockContext = GetMockContextWithCaseCategoryDataSetAsync(data);
var mockLogger = new Mock<IDatabaseContextLogging>();
DatabaseTablesAccess databaseAccess = new DatabaseTablesAccess(mockDatabaseContext.Object, mockLogger.Object);
List<OneCaseCategory> caseCategories = await databaseAccess.GetAllCaseCategories();
Assert.IsTrue(caseCategories.Count().Equals(0), "caseCategories should not contain any items.");
}
The method that generates the database context mock object is contain in the method called "GetMockContextWithCaseCategoryDataSetAsync". Here it is:
public Mock<Context> GetMockContextWithCaseCategoryDataSetAsync(List<CaseCategory> data)
{
var mockCaseCategorySet = new Mock<DbSet<CaseCategory>>() { };
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.Provider).Returns(new TestDbAsyncQueryProvider<CaseCategory>(data.AsQueryable().Provider));
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.Expression).Returns(data.AsQueryable().Expression);
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.ElementType).Returns(data.AsQueryable().ElementType);
mockCaseCategorySet.As<IDbAsyncEnumerable<CaseCategory>>().Setup(x => x.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<CaseCategory>(data.AsQueryable().GetEnumerator()));
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.GetEnumerator()).Returns(data.AsQueryable().GetEnumerator());
Mock<Context> m = new Mock<Context>();
m.Setup(x => x.CaseCategories).Returns(mockCaseCategorySet.Object);
return m;
}
I have multiple tables in the data access object and I have written a method for each one that returns the data access object with the appropriate type of data bound to the correct table in the mock object. I would like to generalize this and here is my attempt:
public Mock<Context> GetMockContextWithCaseCategoryDataSetAsync<T>(List<T> data) where T : class
{
var mockCaseCategorySet = new Mock<DbSet<T>>() { };
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.Provider).Returns(new TestDbAsyncQueryProvider<T>(data.AsQueryable().Provider));
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.Expression).Returns(data.AsQueryable().Expression);
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(data.AsQueryable().ElementType);
mockCaseCategorySet.As<IDbAsyncEnumerable<T>>().Setup(x => x.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<T>(data.AsQueryable().GetEnumerator()));
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(data.AsQueryable().GetEnumerator());
Mock<Context> m = new Mock<Context>();
m.Setup(x => x.?).Returns(mockCaseCategorySet.Object);
return m;
}
Everything is good up until that second last line in the generic version of the method where I am setting up which table returns the mocked object. I am lost as to how (if even possible) I control which table is used based on the generic passed in.
I would like to use it like this:
var context = GetMockContextWithCaseCategoryDataSetAsync<CaseCategory>(data);
I don't see a way to do what you're trying to do unless you also access the DbSets by the generic Set<TEntity> method (like _dbContext.Set<Salary>().Select(x => x.Id)).
An alternative setup that I have been using and that is sort of generic is to create a "MockedDbContext" class that I reuse in all tests accessing the database. This usually looks something like this;
public class MockedDbContext
{
public MockedDbContext() => MockContextProperties();
public Mock<MyDbContext> ContextMock { get; } = new Mock<MyDbContext>(MockBehavior.Loose);
public HashSet<Employee> Employees { get; } = new HashSet<Employee>();
public HashSet<Salary> Salaries { get; } = new HashSet<Salary>();
// etc...
private void MockContextProperties()
{
SimulateGet(x => x.Employees, Employees);
SimulateGet(x => x.Salaries, Salaries);
// etc...
}
private void SimulateGet<TModel>(
Expression<Func<MyDbContext, DbSet<TModel>>> dbSetExpression,
HashSet<TModel> mockedData) where TModel : class
{
ContextMock.SetupGet(dbSetExpression).Returns(new InMemoryDbSet<TModel>(mockedData));
}
public void VerifyThatSaveWasCalled() => ContextMock.Verify(x => x.SaveChanges());
public void VerifyThatSaveWasNotCalled() => ContextMock.Verify(x => x.SaveChanges(), Times.Never);
}
I use it like this;
[TestFixture]
public class When_getting_some_data
{
private readonly MockedDbContext _mockedDbContext = new MockedDbContext();
private readonly Fixture _fixture = new Fixture();
private string _organizationNumber = "yyyyyy-xxx";
private readonly DateTime _created = new DateTime(2022, 2, 2);
private SomeInformation _result;
[OneTimeSetUp]
public void Initialize()
{
var salary = _fixture.Build<Salary>().With(o => o.Created, _created).Create();
_mockedDbContext.Salaries.Add(salary);
_result = new SomeClass(_mockedDbContext.ContextMock.Object).GetSomeInformation(_organizationNumber);
}
[Test]
public void Then_returned_data_is_as_expected()
{
_result.Should().NotBeNull();
_result.Created.Should().Be(_created);
}
}
And It internally uses an InMemoryDbSet<T> that looks like this;
/// <summary>
/// The in-memory database set, taken from Microsoft's online example (http://msdn.microsoft.com/en-us/ff714955.aspx)
/// and modified to be based on DbSet instead of ObjectSet.
/// </summary>
/// <typeparam name="T">The type of DbSet.</typeparam>
public class InMemoryDbSet<T> : DbSet<T>, IQueryable<T> where T : class
{
private static readonly HashSet<T> StaticData = new HashSet<T>();
private readonly HashSet<T> _nonStaticData;
/// <summary>
/// Creates an instance of the InMemoryDbSet using the default static backing store.This means
/// that data persists between test runs, like it would do with a database unless you
/// cleared it down.
/// </summary>
public InMemoryDbSet() : this(true) { }
/// <summary>
/// This constructor allows you to pass in your own data store, instead of using
/// the static backing store.
/// </summary>
/// <param name="data">A place to store data.</param>
public InMemoryDbSet(HashSet<T> data) => _nonStaticData = data;
/// <summary>
/// Creates an instance of the InMemoryDbSet using the default static backing store.This means
/// that data persists between test runs, like it would do with a database unless you
/// cleared it down.
/// </summary>
/// <param name="clearDownExistingData">True to clear existing data</param>
public InMemoryDbSet(bool clearDownExistingData)
{
if (clearDownExistingData)
Clear();
}
public Func<IEnumerable<T>, object[], T> FindFunction { get; set; }
public Type ElementType => Data.AsQueryable().ElementType;
public Expression Expression => Data.AsQueryable().Expression;
public IQueryProvider Provider => Data.AsQueryable().Provider;
IQueryProvider IQueryable.Provider => Data.AsQueryable().Provider;
public new ObservableCollection<T> Local => new ObservableCollection<T>(Data);
/// <summary>
/// The non static backing store data for the InMemoryDbSet.
/// </summary>
private HashSet<T> Data => _nonStaticData ?? StaticData;
public void Clear() => Data.Clear();
public override EntityEntry<T> Add(T entity)
{
Data.Add(entity);
return null;
}
public new T Attach(T entity)
{
Data.Add(entity);
return entity;
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T => Activator.CreateInstance<TDerivedEntity>();
public T Create() => Activator.CreateInstance<T>();
public new virtual T Find(params object[] keyValues)
{
if (FindFunction != null)
{
return FindFunction(Data, keyValues);
}
throw new NotImplementedException("Derive from InMemoryDbSet and override Find, or provide a FindFunction.");
}
public new T Remove(T entity)
{
Data.Remove(entity);
return entity;
}
public IEnumerator<T> GetEnumerator() => Data.GetEnumerator();
}
This saves a lot of work when writing unit tests and is kind of a one time setup which is what I think you were looking for.
I am using the following:
EntityFrameworkCore 3.1.2
.Net Standard Library 2.1
Automapper 9.0.0
Automapper.Extension.Microsoft.DependencyInjection 7.0.0
My DI for AutoMapper looks like this:
services.AddAutoMapper(Assembly.GetExecutingAssembly());
I have an IMapFrom with default implementation:
using AutoMapper;
namespace Application.Common.Mappings
{
public interface IMapFrom<T>
{
void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
}
}
I have a DTO class as follows:
public class ServiceRegistrationDto : IMapFrom<ServiceRegistration>
{
public string ServiceName { get; set; }
public string MessageType { get; set; }
public string ServiceURL { get; set; }
public void Mapping(Profile profile)
{
profile.CreateMap<ServiceRegistration, ServiceRegistrationDto>();
}
}
In my Handler I am doing the following:
public async Task<ServiceRegistrationViewModel> Handle(GetServiceRegistrationListQuery request, CancellationToken cancellationToken)
{
var serviceRegistrations = await _context.ServiceRegistrations
.Where(s => s.MessageType.Equals(request.MessageType, StringComparison.InvariantCultureIgnoreCase))
.ProjectTo<ServiceRegistrationDto>(_mapper.ConfigurationProvider)
.ToListAsync(cancellationToken);
return null;
}
The issue is that the "ProjectTo" is not a definition of IQueryable. I was under the impression that Automapper had Queryable Extensions. Am I missing a NuGet Package? I am following examples from Northwind Traders and I can't figure out what I have different.
I can get to "ProjectTo" using IMapper like this:
var x = _mapper.ProjectTo<ServiceRegistrationDto>(serviceRegistrations);
But I would prefer to do it as part of the IQueryable like you should be able to.
I took a step back from the code and realized I was missing the
using AutoMapper.QueryableExtensions;
DOH!!!
Having some issues getting my repository to retrieve information - keeps coming back null. Any Thoughts would be appreciated - new to this and teaching myself.
Repository:
public class CustomerRepository : ICustomerRepository
{
private masterContext context;
public CustomerRepository(masterContext context)
{
this.context = context;
}
public IEnumerable<Customer> GetCustomers()
{
return context.Customer.ToList();
}
public Customer GetCustomerById(int customerId)
{
var result = (from c in context.Customer where c.CustomerId == customerId select c).FirstOrDefault();
return result;
}
public void Save()
{
context.SaveChanges();
}
Controller:
public class CustomerController : Controller
{
private readonly ICustomerRepository _repository = null;
public ActionResult Index()
{
var model = (List<Customer>)_repository.GetCustomers();
return View(model);
}
public ActionResult New()
{
return View();
}
}
MasterContext which i had efc make:
public partial class masterContext : DbContext
{
public masterContext(DbContextOptions<masterContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>(entity =>
{
entity.Property(e => e.CustomerName).IsRequired();
});
}
public virtual DbSet<Customer> Customer { get; set; }
public virtual DbSet<Order> Order { get; set; }
}
I think you need to create instances of you Context and your Repository. So in your Controller you need to something like this:
private masterContext context = new masterContext();
private ICustomerRepository repository = new CustomerRepository(context);
I assume that you're not using Dependency injection ... if so you just need to create a Constructor for your Controller that takes CustomerRepository as argument:
public CustomerController(ICustomerRepository _repository) {
repository = _repository;
}
If you did not configure your database context, look here: https://docs.efproject.net/en/latest/platforms/aspnetcore/new-db.html
This will than enable you the dependency injection. Everything you than need to do for the Repository is to use
services.AddScoped<ICustomerRepository,
CustomerRepository>();
And I think it could be good to remove the ToList() in the Repository class and remove the Cast List<Customer> in your Controller and use ToList() instead, if it's really needed. Because if you're using it in the View the ienumerable could also work.
In my MVC5 application, I have a Log entity, that is used to log any call to any controler. This log uses: HttpContext.Current.User.Identity.GetUserId() to determine the identity of the user accessing the controller.
public class Log
{
public Log()
{
TS = DateTime.Now;
UserId = HttpContext.Current.User.Identity.GetUserId();
}
[Required]
public Int32 Id { get; set; }
public string UserId { get; set; }
[Display(Name = "TS", ResourceType = typeof(Resources.Models.Log.Log))]
[Required(ErrorMessageResourceType = typeof(Resources.Models.Log.Log), ErrorMessageResourceName = "RequiredTS")]
public DateTime TS { get; set; }
[Required]
public short LogTypeId { get; set; }
[Display(Name = "LogText", ResourceType = typeof(Resources.Models.Log.Log))]
public string LogText { get; set; }
public ApplicationUser User { get; set; }
}
When I try to unit test a controller and crating an instance of the log class I get this error:
threw exception: System.NullReferenceException: Object reference not
set to an instance of an object.
at DASU.Core.Models.Log..ctor()
I know this is because the context is not set.
So my question is how do I set the context, or how do I mock the context, so I can create the Log for my test?
You should avoid coupling to HttpContext. Like suggested in the comments you could simplify your log my injecting the UserId into the dependent Log class
public class Log
{
public Log(string userId)
{
TS = DateTime.Now;
UserId = userId;
}
//...other code removed for brevity
}
or abstracting away the calls to HttpContext so that you can mock your abstract and inject that instead of trying and mock HttpContext
public interface IUserProvider {
string GetUserId();
}
You production implementations can wrap calls to HttpContext and you can easily create mock implementations for your unit tests.
public class Log
{
public Log(IUserProvider userProvider)
{
TS = DateTime.Now;
UserId = userProvider.GetUserId();
}
//...other code removed for brevity
}
I'm new to Xunit and AutoFixture, and writing a theory that looks like:
[Theory, AutoData]
public void Some_Unit_Test(List<MyClass> data)
{
// Test stuff
}
MyClass looks like:
public class MyClass
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
This causes AutoFixture to create a list of items with random values for each property. This is great, but I would like the IsActive property to always be true.
I could set it to true at the start of every test but I'm guessing there is a smarter way. I looked at InlineData, ClassData, PropertyData, even Inject() but nothing quite seemed to fit.
How can I improve this?
Here is one way to do this:
public class Test
{
[Theory, TestConventions]
public void ATestMethod(List<MyClass> data)
{
Assert.True(data.All(x => x.IsActive));
}
}
The TestConventionsAttribute is defined as:
internal class TestConventionsAttribute : AutoDataAttribute
{
internal TestConventionsAttribute()
: base(new Fixture().Customize(new TestConventions()))
{
}
private class TestConventions : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<MyClass>(c => c.With(x => x.IsActive, true));
}
}
}
Thank you, Nikos. This is very useful. Just a small update, now after a while and couple of versions after there is a small change with regards to the base constructor which should be called, the one above is now obsolete. It should look something like this:
private static readonly Func<IFixture> fixtureFactory = () =>
{
return new Fixture().Customize(new TestConventions());
};
public TestConventionsAttribute()
: base(fixtureFactory)
{
}
private class TestConventions : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<MyClass>(c => c.With(x => x.IsActive, true));
}
}