I'm at a lost for how to write a unit test for my web api GET by id method.
Here is what I have:
public void GetProduct_ShouldReturnSameID()
{
var context = new TestModelContext();
context.Products.Add(GetDemoProduct());
var controller = new ProductsController(context);
var result = controller.GetProduct(3) as OkNegotiatedContentResult<Product>;
Assert.IsNotNull(result);
Assert.AreEqual(3, result.Content.Id);
}
And my controller method I'm trying to test
public IHttpActionResult GetProduct(int id)
{
var product = (from t in db.Products.Include(t => t.Reviews)
.Where(t => t.Id == id)
select t);
if (product == null || product.Count() == 0)
{
return NotFound();
}
return Ok(product);
}
My test works find with my other controllers, but just not this one. I was wondering what is wrong with this? My test fails with an
"Expected: not null But was: null"
public class TestModelContext : IModelContext {
public TestModelContext() {
this.Products = new TestProductDbSet();
}
public DbSet<Product> Products { get; set; }
public int SaveChanges() {
return 0;
}
public void MarkAsModified(Product item) { }
public void Dispose() { }
}
public class TestProductDbSet : TestDbSet<Product> {
public override Product Find(params object[] keyValues) {
return this.SingleOrDefault(product => product.Id == (int)keyValues.Single());
}
}
public class TestDbSet<T> : DbSet<T>, IQueryable, IEnumerable<T>
where T : class
{
ObservableCollection<T> _data;
IQueryable _query;
public TestDbSet()
{
_data = new ObservableCollection<T>();
_query = _data.AsQueryable();
}
// ...
public override T Create()
{
return Activator.CreateInstance<T>();
}
public override TDerivedEntity Create<TDerivedEntity>()
{
return Activator.CreateInstance<TDerivedEntity>();
}
public override ObservableCollection<T> Local
{
get { return new ObservableCollection<T>(_data); }
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
GetDemoProduct:
Product GetDemoProduct()
{
return new Product() { Id = 3, Name = "Name", Reviews = null };
}
According to the sample code there is no code path that would result in the method under test returning null.
It's either going to return a NotFoundResult or a OkNegotiatedContentResult<Product>
Given that it is possible for the method under test to return a NotFoundResult , if the if (product == null || product.Count() == 0) condition is met and the method does indeed return not found result, then the following in your test
...as OkNegotiatedContentResult<Product>;
trying to cast NotFoundResult as OkNegotiatedContentResult<Product> will cause the result to be null.
You should recheck your setup/configuration of your TestModelContext as your linq call is causing the product variable to be null which in turn causes the NotFoundResult to be returned.
UPDATE:
Ok was able to start testing it based on your updated details and found an issue i should have noticed earlier.
First I was getting an error when added fake Product to list and had to update the TestDbSet base class to include added entities. I'll assume it was omitted in the sample code.
public override T Add(T entity) {
_data.Add(entity);
return entity;
}
next in the method under test, given the name the method and the expectation in the test, it should be returning a single Product. You were returning the query which would also result in null when you did the cast in the test.
public IHttpActionResult GetProduct(int id) {
var product = (from t in db.Products.Include(t => t.Reviews)
.Where(t => t.Id == id)
select t);
if (product == null || product.Count() == 0) {
return NotFound();
}
return Ok(product.First());
}
When the above two changes were made, the test passed as expected.
It looks like the controller is returning a null as the result. Most likely the Products table and/or the Reviews table does not include a value of 3 that you are passing in as the parameter to your web method.
This is what I suspect. Your GetDemoProduct() method which returns the product you are adding does not have Reviews. Hence, your query:
var product = (from t in db.Products.Include(t => t.Reviews)
.Where(t => t.Id == id)
select t);
did not return any records. Just a thought. If you can show us your GetDemoProduct() then we can tell.
Related
I am creating a general repository class as part of my Entity Framework Code First data layer. To get the single row by id, if all the entities have id name as "ID" it will work as shown by the following:
public T GetSingle(int id)
{
return GetAll().FirstOrDefault(x => x.ID == id);
}
But I would like to name my entity primarykey as "EnityName"+Id, such as AddressId or ApplicantId etc. Is there any way to have the code:
return GetAll().FirstOrDefault(x => x.<EntityName>Id == id);
to make to work?
Thanks
You can do it easier only create method "Get single" and pass predicate
public virtual T GetSingle(Expression<Func<T, bool>> predicate)
{
if (predicate != null)
{
return context.Set<T>().Where(predicate).SingleOrDefault();
}
else
{
throw new ArgumentNullException("Predicate value is null");
}
}
And call it:
yourContext.GetSingle(g => g.Id == id);
you can use Attributes for this and read the Property which has the right Attribute.
public abstract class MyIdAttribute: Attribute
{
}
public class MyClass
{
[MyId]
WhatEverName {get; set;}
}
and in your Method
public T GetSingle(int id)
{
var propertyInfos = typeof(T).GetProperties().Where(property => property.GetCustomAttributes<MyIdAttribute>().Count() > 0).ToList();
if (propertyInfos.Any())
{
foreach(T current in GetAll())
{
object value = propertyInfos[0].GetValue(current);
if(value == id)
{
return current;
}
}
}
}
Cheers
Thomas
I have two view models, PublishedSong and RadioStation where I want them to have IncrementViews(int id) function.
Instead of copying and pasting the function in to both controllers I wanted to make a generic helper class.
Helper:
public class CustomDBHelper<T>
{
public static IEnumerable<T> GetElements(ApplicationDbContext db, T type)
{
if (type.Equals(typeof(SongsController)))
return (IEnumerable<T>)db.PublishedSongs;
else if (type.Equals(typeof(RadioStationsController)))
return (IEnumerable<T>)db.RadioStations;
else
throw new Exception("Controller not found, DBHelper");
}
}
Controller:
public class MusicBaseController : Controller
{
public JsonResult IncrementViews(int id)
{
using (ApplicationDbContext db = new ApplicationDbContext())
{
db.PublishedSongs.Single(x => x.Id == id);
var element = CustomDBHelper<T>.GetElements(db, this.GetType()).Single(x => x.Id == id);
element.UniquePlayCounts++;
db.SaveChanges();
return Json(new { UniquePlayCounts = element.UniquePlayCounts }, JsonRequestBehavior.AllowGet);
}
}
}
I am having trouble with this line: var element = CustomDBHelper<T>.GetElements(db, this.GetType()).Single(x => x.Id == id);
The <T> is invalid. I am new to generics, I think it expects a class but because the class could either be PublishedSong or RadioStation I don't know how to do it.
The following implementation will work for you:
public class CustomDBHelper
{
public static IEnumerable GetElements(ApplicationDbContext db, Type type)
{
if (type.Equals(typeof(SongsController)))
{
return db.PublishedSongs;
}
else if (type.Equals(typeof(RadioStationsController)))
{
return db.RadioStations;
}
else
{
throw new Exception("Controller not found, DBHelper");
}
}
}
And you will need to cast the result of this function to the required type.
On a side note, type generics are used, well, for being type generic. Since you pass the type as a parameter it's not necessary.
I'm trying to create a unit test for my service with a mocked DbContext. I created an interface IDbContext with the following functions:
public interface IDbContext : IDisposable
{
IDbSet<T> Set<T>() where T : class;
DbEntityEntry<T> Entry<T>(T entity) where T : class;
int SaveChanges();
}
My real context implements this interface IDbContext and DbContext.
Now I'm trying to mock the IDbSet<T> in the context, so it returns a List<User> instead.
[TestMethod]
public void TestGetAllUsers()
{
// Arrange
var mock = new Mock<IDbContext>();
mock.Setup(x => x.Set<User>())
.Returns(new List<User>
{
new User { ID = 1 }
});
UserService userService = new UserService(mock.Object);
// Act
var allUsers = userService.GetAllUsers();
// Assert
Assert.AreEqual(1, allUsers.Count());
}
I always get this error on .Returns:
The best overloaded method match for
'Moq.Language.IReturns<AuthAPI.Repositories.IDbContext,System.Data.Entity.IDbSet<AuthAPI.Models.Entities.User>>.Returns(System.Func<System.Data.Entity.IDbSet<AuthAPI.Models.Entities.User>>)'
has some invalid arguments
I managed to solve it by creating a FakeDbSet<T> class that implements IDbSet<T>
public class FakeDbSet<T> : IDbSet<T> where T : class
{
ObservableCollection<T> _data;
IQueryable _query;
public FakeDbSet()
{
_data = new ObservableCollection<T>();
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public T Add(T item)
{
_data.Add(item);
return item;
}
public T Remove(T item)
{
_data.Remove(item);
return item;
}
public T Attach(T item)
{
_data.Add(item);
return item;
}
public T Detach(T item)
{
_data.Remove(item);
return item;
}
public T Create()
{
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}
public ObservableCollection<T> Local
{
get { return _data; }
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
Now my test looks like this:
[TestMethod]
public void TestGetAllUsers()
{
//Arrange
var mock = new Mock<IDbContext>();
mock.Setup(x => x.Set<User>())
.Returns(new FakeDbSet<User>
{
new User { ID = 1 }
});
UserService userService = new UserService(mock.Object);
// Act
var allUsers = userService.GetAllUsers();
// Assert
Assert.AreEqual(1, allUsers.Count());
}
In case anyone is still interested, I was having the same problem and found this article very helpful:
Entity Framework Testing with a Mocking Framework (EF6 onwards)
It only applies to Entity Framework 6 or newer, but it covers everything from simple SaveChanges tests to async query testing all using Moq (and a few of manual classes).
Thank you Gaui for your great idea =)
I did add some improvements to your solution and want to share it.
My FakeDbSet also inherents from DbSet to get additional methods
like AddRange()
I replaced the ObservableCollection<T> with List<T> to pass all
the already implemented methods in List<> up to my FakeDbSet
My FakeDbSet:
public class FakeDbSet<T> : DbSet<T>, IDbSet<T> where T : class {
List<T> _data;
public FakeDbSet() {
_data = new List<T>();
}
public override T Find(params object[] keyValues) {
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public override T Add(T item) {
_data.Add(item);
return item;
}
public override T Remove(T item) {
_data.Remove(item);
return item;
}
public override T Attach(T item) {
return null;
}
public T Detach(T item) {
_data.Remove(item);
return item;
}
public override T Create() {
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T {
return Activator.CreateInstance<TDerivedEntity>();
}
public List<T> Local {
get { return _data; }
}
public override IEnumerable<T> AddRange(IEnumerable<T> entities) {
_data.AddRange(entities);
return _data;
}
public override IEnumerable<T> RemoveRange(IEnumerable<T> entities) {
for (int i = entities.Count() - 1; i >= 0; i--) {
T entity = entities.ElementAt(i);
if (_data.Contains(entity)) {
Remove(entity);
}
}
return this;
}
Type IQueryable.ElementType {
get { return _data.AsQueryable().ElementType; }
}
Expression IQueryable.Expression {
get { return _data.AsQueryable().Expression; }
}
IQueryProvider IQueryable.Provider {
get { return _data.AsQueryable().Provider; }
}
IEnumerator IEnumerable.GetEnumerator() {
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator() {
return _data.GetEnumerator();
}
}
It is very easy to modify the dbSet and Mock the EF Context Object:
var userDbSet = new FakeDbSet<User>();
userDbSet.Add(new User());
userDbSet.Add(new User());
var contextMock = new Mock<MySuperCoolDbContext>();
contextMock.Setup(dbContext => dbContext.Users).Returns(userDbSet);
Now it is possible to execute Linq queries, but be a aware that foreign key references may not be created automatically:
var user = contextMock.Object.Users.SingeOrDefault(userItem => userItem.Id == 42);
Because the context object is mocked the Context.SaveChanges() won't do anything and property changes of your entites might not be populated to your dbSet. I solved this by mocking my SetModifed() method to populate the changes.
Based on this MSDN article, I've created my own libraries for mocking DbContext and DbSet:
EntityFrameworkMock - GitHub
EntityFrameworkMockCore - GitHub
Both available on NuGet and GitHub.
The reason I've created these libraries is because I wanted to emulate the SaveChanges behavior, throw a DbUpdateException when inserting models with the same primary key and support multi-column/auto-increment primary keys in the models.
In addition, since both DbSetMock and DbContextMock inherit from Mock<DbSet> and Mock<DbContext>, you can use all features of the Moq framework.
Next to Moq, there also is an NSubstitute implementation.
Usage with the Moq version looks like this:
public class User
{
[Key, Column(Order = 0)]
public Guid Id { get; set; }
public string FullName { get; set; }
}
public class TestDbContext : DbContext
{
public TestDbContext(string connectionString)
: base(connectionString)
{
}
public virtual DbSet<User> Users { get; set; }
}
[TestFixture]
public class MyTests
{
var initialEntities = new[]
{
new User { Id = Guid.NewGuid(), FullName = "Eric Cartoon" },
new User { Id = Guid.NewGuid(), FullName = "Billy Jewel" },
};
var dbContextMock = new DbContextMock<TestDbContext>("fake connectionstring");
var usersDbSetMock = dbContextMock.CreateDbSetMock(x => x.Users, initialEntities);
// Pass dbContextMock.Object to the class/method you want to test
// Query dbContextMock.Object.Users to see if certain users were added or removed
// or use Mock Verify functionality to verify if certain methods were called: usersDbSetMock.Verify(x => x.Add(...), Times.Once);
}
If anyone is still looking for answers I've implemented a small library to allow mocking DbContext.
step 1
Install Coderful.EntityFramework.Testing nuget package:
Install-Package Coderful.EntityFramework.Testing
step 2
Then create a class like this:
internal static class MyMoqUtilities
{
public static MockedDbContext<MyDbContext> MockDbContext(
IList<Contract> contracts = null,
IList<User> users = null)
{
var mockContext = new Mock<MyDbContext>();
// Create the DbSet objects.
var dbSets = new object[]
{
MoqUtilities.MockDbSet(contracts, (objects, contract) => contract.ContractId == (int)objects[0] && contract.AmendmentId == (int)objects[1]),
MoqUtilities.MockDbSet(users, (objects, user) => user.Id == (int)objects[0])
};
return new MockedDbContext<SourcingDbContext>(mockContext, dbSets);
}
}
step 3
Now you can create mocks super easily:
// Create test data.
var contracts = new List<Contract>
{
new Contract("#1"),
new Contract("#2")
};
var users = new List<User>
{
new User("John"),
new User("Jane")
};
// Create DbContext with the predefined test data.
var dbContext = MyMoqUtilities.MockDbContext(
contracts: contracts,
users: users).DbContext.Object;
And then use your mock:
// Create.
var newUser = dbContext.Users.Create();
// Add.
dbContext.Users.Add(newUser);
// Remove.
dbContext.Users.Remove(someUser);
// Query.
var john = dbContext.Users.Where(u => u.Name == "John");
// Save changes won't actually do anything, since all the data is kept in memory.
// This should be ideal for unit-testing purposes.
dbContext.SaveChanges();
Full article: http://www.22bugs.co/post/Mocking-DbContext/
I'm late, but found this article helpful: Testing with InMemory (MSDN Docs).
It explains how to use an in memory DB context (which is not a database) with the benefit of very little coding and the opportunity to actually test your DBContext implementation.
I have the following static class:
public static class SortFilter
{
public enum SortDirection { Ascending, Descending }
public static IEnumerable<TEntity> Sort<TEntity, TKey>(IEnumerable<TEntity> entities, Func<TEntity, TKey> sorter, SortDirection d)
{
if (SortDirection.Ascending == d)
{
return entities.OrderBy(sorter);
}
else
{
return entities.OrderByDescending(sorter);
}
}
public static IEnumerable<TEntity> Sort<TEntity>(IEnumerable<TEntity> entities, string sortParams)
{
string[] parameters = sortParams.Split('_');
if (parameters.Count() == 1 || parameters.Count() > 2)
{
return entities;
}
else
{
if (parameters[1] == "ascending")
{
return Sort(entities, x => GetPropertyValue(x, parameters[0]), SortDirection.Ascending);
}
else if (parameters[1] == "descending")
{
return Sort(entities, x => GetPropertyValue(x, parameters[0]), SortDirection.Descending);
}
else
{
return entities;
}
}
}
public static object GetPropertyValue(object obj, string name)
{
return obj == null ? null : obj.GetType()
.GetProperty(name)
.GetValue(obj, null);
}
}
I can call the Sort method on a List like this (the User class has Id and Name properties):
users = SortFilter.Sort(users, "Name_ascending");
users = SortFilter.Sort(users, "Id_ascending");
So far, so good: that will work just fine.
However, let's say my User class also has a UserGroup property and that I want to sort my User list by the Name of that UserGroup. This call will obviously fail:
users = SortFilter.Sort(users, "UserGroup.Name_ascending");
given that UserGroup.Name is not itself a Type (therefore GetPropertyValue method will throw an exception).
Is there a way to make a generic enough Sort function that will take a collection and sort it by any number of nested properties sent as an argument? I have several Views with table data that should be sorted by any column the user clicks, in ascending or descending order. Having sorting and filtering code within the controller seems very dirty.
Like always for string parameters for LINQ use DynamicLINQ:
using System.Linq.Dynamic;
//..
if (parameters[1] == "ascending" || parameters[1] == "descending")
{
return entities.AsQueryable()
.OrderBy(string.Format("{0} {1}", parameters[0], parameters[1]))
}
else
{
return entities;
}
Docs can be found here in LinqSamples/DynamicQuery/Dynamic Expressions.html.
Nested properties are supported of course.
I am currently working through a .NET Web API tutorial located here. In the example, we have an interface defined in a model class like so:
namespace ProductStore.Models
{
public interface IProductRepository
{
IEnumerable<Product> GetAll();
Product Get(int id);
Product Add(Product item);
void Remove(int id);
bool Update(Product item);
}
}
Then, later in the tutorial, we implement this interface like so:
namespace ProductStore.Models
{
public class ProductRepository : IProductRepository
{
private List<Product> products = new List<Product>();
private int _nextId = 1;
public ProductRepository()
{
Add(new Product { Name = "Tomato soup", Category = "Groceries", Price = 1.39M });
Add(new Product { Name = "Yo-yo", Category = "Toys", Price = 3.75M });
Add(new Product { Name = "Hammer", Category = "Hardware", Price = 16.99M });
}
public IEnumerable<Product> GetAll()
{
return products;
}
public Product Get(int id)
{
return products.Find(p => p.Id == id);
}
public Product Add(Product item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
item.Id = _nextId++;
products.Add(item);
return item;
}
public void Remove(int id)
{
products.RemoveAll(p => p.Id == id);
}
public bool Update(Product item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
int index = products.FindIndex(p => p.Id == item.Id);
if (index == -1)
{
return false;
}
products.RemoveAt(index);
products.Add(item);
return true;
}
}
}
My question is, why is the interface written so that GetAll() returns IEnumerable<Product> if the implementation is going to be specifically operating on a List<Product>?
I am assuming this is to facilitate reuse so that some other IProductRepository implementation could use a different IEnumerable<Product> (though an example of this would be nice, because I can't think of one, not that I doubt it exists, I'm just not ultra experienced).
Assuming reuse is the goal indeed, then why is the implementation written like this:
public IEnumerable<Product> GetAll()
{
return products;
}
Instead of like this:
public List<Product> GetAll()
{
return products;
}
Does the framework or compiler not have the ability to see that public List<Product> GetAll() is derivable from public IEnumerable<Product> GetAll()?
Two main reasons:
this hides the actual implementation so that the consumer doesn't do anything they shouldn't with the data (e.g. add or remove items directly from the list). Obviously, the consumer can cast the result to List<T>, but they would be relying on an undocumented implementation detail, so if you change it later and it breaks their code, it will be their fault.
if someday you need to change the implementation to use something other than a List<T>, you can do it without any visible change for the consumer.
You want changes to your List<T> to operate through your repository methods and not give the user direct access to the List<T> - thus, you return an IEnumerable<T> so that they cannot change the underlying collection