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.
Related
I have a generic Repository with the Unit of Work pattern setup to utilize the Specification Pattern which works great but now I am trying to Unit test it and it seems my setup isn't returning anything when specifying what to return.
Repository Snippet
internal class Repository<T> : IRepository<T> where T : BaseEntity
{
protected readonly ApplicationDbContext _context;
public Repository(ApplicationDbContext context)
{
_context = context;
}
public IEnumerable<T> Find(ISpecification<T> specification = null)
{
return ApplySpecification(specification);
}
private IQueryable<T> ApplySpecification(ISpecification<T> spec)
{
return SpecificationEvaluator<T>.GetQuery(_context.Set<T>().AsQueryable(), spec);
}
}
}
Snippet of Specification
public class GetStocksForCurrentDaySpecification : BaseSpecification<Stock>
{
public GetStocksForCurrentDaySpecification() : base(x => x.DateInserted.Day == DateTime.Today.Day) { }
}
Snippet of me calling it
_unitOfWork.Repository<Stock>().Find(new GetStocksForCurrentDaySpecification());
This all works perfectly when running my code.
Here is my Unit Test where I do setup.
[Fact]
public void Job_Should_Execute()
{
var stockList = new List<Stock>();
stockList.Add(new Stock
{
Id = Guid.NewGuid(),
DateInserted = DateTime.Now,
Exchange = "ASX",
LongName = "Long Name",
MarketOpenPrice = 1.26M,
MarketPreviousClosePrice = 1.56M,
MarketPrice = 1.3M,
ShortName = "Short Name",
Symbol = "LGN"
});
var stockRepositoryMock = new Mock<IRepository<Stock>>();
stockRepositoryMock.Setup(m => m.Find(new GetStocksForCurrentDaySpecification())).Returns(stockList.ToArray());
var unitOfWorkMock = new Mock<IUnitOfWork>();
unitOfWorkMock.Setup(m => m.Repository<Stock>()).Returns(stockRepositoryMock.Object).Verifiable();
var job = new YahooStockMarketJob(new HttpClient(), unitOfWorkMock.Object, new EventPublisher(_listeners), _configuration);
job.Execute();
unitOfWorkMock.Verify();
}
When debugging, the following line returns an empty array instead of an array with one item it.
// Returns empty array
var stockRepositoryMock = new Mock<IRepository<Stock>>();
stockRepositoryMock.Setup(m => m.Find(new GetStocksForCurrentDaySpecification())).Returns(stockList.ToArray());
In your mock object you are setting up the Find method with a specific instance of GetStocksForCurrentDaySpecification. During your test you are passing a different instance of GetStocksForCurrentDaySpecification and since those are not the same it won't use that setup.
If you want your setup to work with your test you either need to use the same instance or allow any instance of the object by using It.IsAny<T>.
E.g.
// Returns empty array
var stockRepositoryMock = new Mock<IRepository<Stock>>();
stockRepositoryMock
.Setup(m => m.Find(It.IsAny<GetStocksForCurrentDaySpecification>()))
.Returns(stockList.ToArray());
Main Goal : Automatic Moq an entire DBContext
Current Problem: Setup donĀ“t work. It runs and adds the Setup but when you try to access the Data you get an "NotImplemented Exception"
The member 'IQueryable.Provider' has not been implemented on type 'DbSet 1Proxy_4' which inherits from 'DbSet 1'. Test doubles for 'DbSet 1' must provide implementations of methods and properties that are used.
My class
public abstract class MoqBase<T> where T : DbContext
{
public Mock<T> MockContext { get; private set; }
public void Mock()
{
MockContext = new Mock<T> { DefaultValue = DefaultValue.Mock };
MockContext.SetupAllProperties();
PrepareContext();
}
private void PrepareContext()
{
var propertyList = MockContext.Object.GetType().GetProperties();
var tablePropertyList = propertyList.Where(x => x.PropertyType.FullName.Contains("DbSet")).ToList();
foreach (var tableProperty in tablePropertyList)
{
var proxy = tableProperty.GetValue(MockContext.Object);
dynamic mockSet = ((dynamic)proxy).Mock;
AddTableSetup(mockSet);
}
}
public void AddTableSetup<TTable>(Mock<DbSet<TTable>> Mockset) where TTable : class
{
var list = new List<TTable>();
var data = list.AsQueryable();
Mockset.As<IQueryable<TTable>>().Setup(m => m.Provider).Returns(data.Provider);
Mockset.As<IQueryable<TTable>>().Setup(m => m.Expression).Returns(data.Expression);
Mockset.As<IQueryable<TTable>>().Setup(m => m.ElementType).Returns(data.ElementType);
Mockset.As<IQueryable<TTable>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator);
}
}
TestMoq which implements MoqBase
public class TestMoq : MoqBase<NetConPortalContext>//NetConPortalContext is my dbContext
{
public TestMoq() => Mock();
}
Try to access
static void Main()
{
var moq = new TestMoq();
var context = moq.MockContext.Object;
var test = context.User.FirstOrDefault();
}
Picture of Error and Setup in Debug
I'm trying to get a property from an object by reflection.
public class CosmosDbSet<TEntity> : DbSet<TEntity> where TEntity : class, IEntity<string>
{
public string Name { get; }
//...
);
}
public class SKCosmosDbContext : CosmosDBContext
{
public CosmosDbSet<Item> Items { get; }
public SKCosmosDbContext ()
{
Items = new CosmosDbSet<Item>(
this,
"Items"
);
}
//...
}
public abstract class CosmosDBContext : DbContext
{
public async Task EnsureContainersExistAsync()
{
var sets = GetType().GetProperties()
.Where(pi => pi.PropertyType.IsGenericType
&& pi.PropertyType.GetGenericTypeDefinition().Equals(typeof(CosmosDbSet<>))
);
foreach (var set in sets)
{
var value = set.GetValue(this, null); // => value is always null
//...
}
}
}
public static class DbInitializer
{
public async static Task InitializeAsync(IServiceProvider services, ILogger logger)
{
var dbContext = services.GetRequiredService<SKCosmosDbContext>();
await dbContext.EnsureContainersExistAsync();
}
}
As you can see, the property Items from SKCosmosDbContext has been found but, I can't have access to it.
How to have access to the property using reflection?
So basically I see problem in using .GetGenericTypeDefinition() call.
If you do more detailed debug you could see that it returns enumerable with next content:
To get what you want you could use pi.PropertyType.GetGenericArguments()[0] and than use its return value to equal it in your linq query.
ex.
I used dummy types just for sake of example
Your problem could be related also with this one: Get type of generic list
TL;DR Example works after changing query to:
var sets = db.GetType().GetProperties()
.Where(pi => pi.PropertyType.IsGenericType
&& pi.PropertyType.GetGenericArguments()[0].Equals(typeof(...))
);
I'm trying to test a service method but in order to do that I have to mock my ReportRepository. Everything works fine except the call to the Include method makes the mock return null.
The following returns the expected report:
var report = new Report();
var reportRepoMock = Substitute.For<IReportRepository>();
reportRepoMock.Query()
.ByReportType(ReportType.DELQ)
.ToEntityAsync()
.Returns(Task.FromResult(report));
But the service actually does the following:
var report = await reportRepo.Query()
.ByReportType(ReportType.DELQ)
.Include(m => m.Fields)
.ToEntityAsync();
The problem is that when I include the 'Include' method in my mock, null is returned instead of the expected report so my test breaks with a NullReferenceException:
var reportRepoMock = Substitue.For<IReportRepository>();
reportRepoMock.Query()
.ByReportType(ReportType.DELQ)
.Include(m => m.Fields)
.ToEntityAsync()
.Returns(Task.FromResult(report));
So how can I include the 'Include' method in my mock?
I'm experimenting with fluent repositories so they're set up a little different. A lot of the following code happens in abstract, generic classes but I cut it out to keep the question length down.
public class ReportRepository : IReportRepository
{
private readonly IDbSet _dbSet;
public ReportRepository(IDbContext context) {
_dbSet = context.Set<Report>();
}
void Add(Report report) { ... }
void Remove(Report report) { ... }
...
public IReportQueryBuilder Query() {
return new ReportQueryBuilder(_dbSet.AsQueryable());
}
}
public class ReportQueryBuilder : IReportQueryBuilder
{
private IQueryable<Report> _query;
public ReportQueryBuilder(IQueryable<Report> query) {
_query = query;
}
public IReportQueryBuilder ByReportType(ReportType reportType) {
_query = _query.Where(m => m.ReportType == reportType);
return this;
}
public IReportQueryBuilder Include<T>(Expression<Func<Report, T>> property) {
_query = _query.Include(property);
return this;
}
public async Task<Report> ToEntityAsync() {
return await _query.FirstOrDefaultAsync();
}
}
Figured this one out. Instead of setting up the mock as:
reportRepoMock.Query()
.ByReportType(ReportType.DELQ)
.Include(m => m.Fields)
.ToEntityAsync()
.Returns(Task.FromResult(report));
I went with:
reportRepoMock.Query()
.ByReportType(ReportType.DELQ)
.Include(Arg.Any<Expression<Func<Report, Collection<ReportField>>>>())
.ToEntityAsync()
.Returns(Task.FromResult(report));
I can't get my unit test to work properly.
It works in a integration test I have where it will actually hit the Azure Table Storage.
The problem I guess is the mocking of the property QueryableEntities which returns a Queryable<Word> from the mock but it returns a DataServiceQuery from the ServiceContext class. Is it possible to create a stub of type DataServiceQuery which returns a Queryable?
This is my code:
Test
[TestMethod]
public void GetAExistingWordInStorageShouldReturnCorrectWord()
{
Word expected = new Word(Dictionaries.Swedish.ToString(), "Word", "Word");
List<Word> Words = new List<Word>();
Words.Add(new Word(Dictionaries.Swedish.ToString(), "Word", "Word"));
IQueryable<Word> WordQueryable = Words.AsQueryable<Word>();
var mock = new Mock<IServiceContext<Word>>();
mock.Setup(x => x.QueryableEntities).Returns(WordQueryable);
DictionaryRepository dr = new DictionaryRepository(Models.Dictionaries.Swedish, "testdictionaries");
dr.Context = mock.Object;
Word result = dr.GetWord(expected.Text, false);
Assert.AreEqual(expected, result);
}
IServiceContect interface
public interface IServiceContext<TEntity>
{
IQueryable<TEntity> QueryableEntities {get;}
}
ServiceContext Class
public class ServiceContext<TEntity> : TableServiceContext, IServiceContext<TEntity> where TEntity : TableServiceEntity
{
private readonly string tableName;
public ServiceContext(CloudStorageAccount account, String tableName)
: base(account.TableEndpoint.ToString(), account.Credentials)
{
this.tableName = tableName;
this.IgnoreResourceNotFoundException = true;
}
public IQueryable<TEntity> QueryableEntities
{
get
{
return CreateQuery<TEntity>(tableName);
}
}
}
Dictionary Repository
public class DictionaryRepository : IDictionaryRepository
{
public Dictionaries Dictionary { get; set; }
public String TableName;
public IServiceContext<Word> Context;
public DictionaryRepository(Dictionaries dictionary)
: this(dictionary, "dictionaries")
{
}
public DictionaryRepository(Dictionaries dictionary, String tableName)
{
Dictionary = dictionary;
this.TableName = tableName;
CloudStorageAccount account = CloudStorageAccount.Parse(***);
Context = new ServiceContext<Word>(account, this.TableName);
}
public List<Tile> GetValidTiles()
{
throw new NotImplementedException();
}
public Type ResolveEntityType(String name)
{
return typeof(Word);
}
public Word GetWord(string word, Boolean useCache = false)
{
var q = this.Context.QueryableEntities.Where(x => x.PartitionKey == Dictionary.ToString() && x.RowKey == word).AsTableServiceQuery();
Word result = q.Execute().SingleOrDefault();
if (result == null)
return null;
return result;
}}
I'm getting the following error
Error:
ArgumentNullException was unhandled by user code
Value cannot be null.
Parameter name: query
I get the error when calling .AsTableServiceQuery() on the following line in DictionaryRepository class:
var q = this.Context.QueryableEntities.Where(x => x.PartitionKey == Dictionary.ToString() && x.RowKey == word).AsTableServiceQuery();
You haven't mentioned the error you're getting, but since the QueryableEntities is a readonly property try using mock.SetupGet instead of mock.Setup.
EDIT:
Looking into it further the problem is that the .AsTableServiceQuery() extension method attempts to cast the IQueryable<T> to a DataServiceQuery<T>, which fails causing the null exception.
There's a post by Frederic Boerr about how to do unit testing with table storage that should help you out. Windows Azure Storage: TDD and mocks
I know you specifically asked how to do this using Moq, and I don't have an answer to that, but I figured out how to do something similar using Fakes.
http://azurator.blogspot.com/2013/07/unit-testing-azure-table-storage-queries.html
Essentially you can create a Shim on CloudTableQuery<T> that reads the Expression object the query is using and applies that same logic to your IEnumerable using code like this:
[TestMethod]
public void here_is_my_test()
{
IEnumerable<MyEntityType> fakeResults = GetFakeResults();
using (ShimsContext.Create())
{
InterceptCloudTableQueryExecute<MyEntityType>(fakeResults);
DoQuery();
AssertStuff();
}
}
public void InterceptCloudTableQueryExecute<T>(IEnumerable<T> result)
{
var query = result.AsQueryable();
ShimCloudTableQuery<T>.AllInstances.Execute = (instance) =>
{
// Get the expression evaluator.
MethodCallExpression ex = (MethodCallExpression)instance.Expression;
// Depending on how I called CreateQuery, sometimes the objects
// I need are nested one level deep.
if (ex.Arguments[0] is MethodCallExpression)
{
ex = (MethodCallExpression)ex.Arguments[0];
}
UnaryExpression ue = ex.Arguments[1] as UnaryExpression;
// Get the lambda expression
Expression<Func<T, bool>> le = ue.Operand as Expression<Func<T, bool>>;
query = query.Where(le);
return query;
};
}