How to mock EF with NSubstitute - c#

I have been tasked at work to create a test script that will (using entity framework) look-up a value in a table if existing.
The code I have to work with has this constructor:
public PostProductHelper(
Func<IMachineDBContext> contextFactory )
{
_contextFactory = contextFactory;
}
My method to unit test could be something like this:
public string CheckAndRemoveProductNameFileExtIfExists(
string productName )
{
using ( var ctx = CreateContext() )
{
return ctx.Products.FirstOrDefault( d => d.Name == productName);
}
}
so, going by the examples when Googling I am supposed to do this:
MockProductRepository = Substitute.For<IProductRepository>();
MockMessagePublicationService = Substitute.For<IMessagePublicationService>();
MockMachineDBContext = Substitute.For<IMachineDBContext>(););
var Products = new List<Product>
{
new Product { Name = "BBB" },
new Product { Name = "ZZZ" },
new Product { Name = "AAA" },
}.AsQueryable();
MockMachineDBContext.Products.AddRange( Products );
But in order to pass to my constructor I have to modify this to:
MockProductRepository = Substitute.For<IProductRepository>();
MockMessagePublicationService = Substitute.For<IMessagePublicationService>();
MockMachineDBContext = Substitute.For<Func<IMachineDBContext>>();
var Products = new List<Product>
{
new Product { Name = "BBB" },
new Product { Name = "ZZZ" },
new Product { Name = "AAA" },
}.AsQueryable();
MockMachineDBContext.Products.AddRange( Products );
which errors on the last line saying 'cannot resolve symbol 'Products'.
I am not allowed to change this constructor and I appreciate I may be making some mistakes.

You are missing () after MockMachineDBContext in MockMachineDBContext().Products.AddRange( Products );
MockMachineDBContext is delegate.
For usage also see Substituting for delegates in NSubstitute.

Related

how to Moq Fluent interface / chain methods

I'm using the moq framework by Daniel Cazzulino, kzu Version 4.10.1.
I want to moq so i can test a particular part of functionality (below is the simplistic version of the Code i could extract)
The fluent/chain method so are designed so you can get object by an Id and include any additional information if required.
i'm having some trouble fetching the correct object when the function is calling the moq'ed method, which is currently returning the last moq'ed object which is wrong
/*My current Moq setup*/
class Program
{
static void Main(string[] args)
{
var mock = new Mock<IFluent>();
var c1 = new ClassA() { Id = 1, Records = new List<int>() { 5, 2, 1, 10 }, MetaData = new List<string>() };
var c2 = new ClassA() { Id = 2, Records = new List<int>(), MetaData = new List<string>() { "X", "Y", "Z" } };
mock.Setup(x => x.GetById(1).IncludeRecords().IncludeMetaData().Get()).Returns (c1);
mock.Setup(x => x.GetById(2).IncludeRecords().IncludeMetaData().Get()).Returns(c2);
var result = new ComputeClass().ComputeStuff(mock.Object);
Console.WriteLine(result);
Console.ReadLine();
}
}
/*Fluent interface and object returned*/
public interface IFluent
{
IFluent GetById(int id);
IFluent IncludeRecords();
IFluent IncludeMetaData();
ClassA Get();
}
public class ClassA
{
public int Id { get; set; }
public ICollection<int> Records { get; set; }
public ICollection<string> MetaData { get; set; }
}
/*the method which is doing the work*/
public class ComputeClass
{
public string ComputeStuff(IFluent fluent)
{
var ids = new List<int>() { 1, 2 };
var result = new StringBuilder();
foreach (var id in ids)
{
var resClass = fluent.GetById(id).IncludeRecords().IncludeMetaData().Get();
result.Append($"Id : {id}, Records: {resClass.Records.Count}, MetaData: {resClass.MetaData.Count}{Environment.NewLine}");
}
return result.ToString();
}
}
Current incorrect result
/*Id : 1, Records: 0, MetaData: 3
Id : 2, Records: 0, MetaData: 3*/
Expected Result
/*Id : 1, Records: 3, MetaData: 0
Id : 2, Records: 0, MetaData: 3*/
The easiest way would be to split out each setup:
var mock = new Mock<IFluent>();
var mock1 = new Mock<IFluent>();
var mock2 = new Mock<IFluent>();
mock.Setup(x => x.GetById(1)).Returns(mock1.Object);
mock1.Setup(x => x.IncludeRecords()).Returns(mock1.Object);
mock1.Setup(x => x.IncludeMetaData()).Returns(mock1.Object);
mock1.Setup(x => x.Get()).Returns(c1);
mock.Setup(x => x.GetById(2)).Returns(mock2.Object);
mock2.Setup(x => x.IncludeRecords()).Returns(mock2.Object);
mock2.Setup(x => x.IncludeMetaData()).Returns(mock2.Object);
mock2.Setup(x => x.Get()).Returns(c2);
var result = new ComputeClass().ComputeStuff(mock.Object);
You could create an extension/utility to handle this all for you if you wanted something a bit more complex, take a look at this blog post: https://www.codemunki.es/2014/11/20/mocking-a-fluent-interface-automatically-in-moq/
Just an addition to the already existing answer. For mocking fluent API there is one usefull option within the moq, it is SetReturnsDefault, it could save some mocking especially if you have huge fluent API, e.g.
var mock = new Mock<IFluent>();
var mock1 = new Mock<IFluent>();
var mock2 = new Mock<IFluent>();
mock.Setup(x => x.GetById(1)).Returns(mock1.Object);
mock1.SetReturnsDefault(mock1.Object);
mock1.Setup(x => x.Get()).Returns(a);
mock.Setup(x => x.GetById(2)).Returns(mock2.Object);
mock2.SetReturnsDefault(mock2.Object);
mock2.Setup(x => x.Get()).Returns(b);
var aa = mock.Object.IncludeMetaData().GetById(1).IncludeMetaData().Get();
var bb = mock.Object.IncludeMetaData.GetById(2).IncludeMetaData.Get();
With this approach you actually have to mock only method which differ but not the all methods from fluent API.

Is this deprecated way of coding? ASP.Net MVC

I am trying to understand how ASP.NET MVC works and I have been recommended to follow the MVC Music Store Tutorial, however I am having problems since I am using different (more current) software.
According to this page I should add a List of genres to my action method inside my StoreController.cs. However according to Visual studio, this piece of code seems to be incorrect or not recognized. The error says: Identifier expected;'new' is a keyword. Should I use different code or do this in my model class instead in some way?
public ActionResult Index()
{
var genres = new List<Genre>
{
new Genre = { Name = "Disco" },
new Genre = { Name = "Jazz" },
new Genre = { Name = "Rock" }
};
return View();
}
Shouldn't something like this do the job?:
public ActionResult Index()
{
//var genres = new List<Genre>
//{
// new Genre = { Name = "Disco" },
// new Genre = { Name = "Jazz" },
// new Genre = { Name = "Rock" }
//};
var genres = new List<Genre>();
genres.Add(new Genre() { Name = "Disco" });
genres.Add(new Genre() { Name = "Jazz" });
genres.Add(new Genre() { Name = "Rock" });
return View();
}
And doesn't this constantly add the genres everytime I would run through the Index action method?
Your syntax is incorrect. Your first snippet is an object initializer and is no different than your second block of code where you create a new instance of Genre and assign its Name property, only in the first case you are attempting to assign { Name = "Disco" } to new Genre().
Read more on Object Initializer
public ActionResult Index()
{
var genres = new List<Genre>
{
new Genre { Name = "Disco" },
new Genre { Name = "Jazz" },
new Genre { Name = "Rock" }
};
return View();
}
To answer your second question, yes, that list (which is entirely in memory) will get created and then discarded every time your action runs. It's only meant to be sample code, if your application is more complicated you will probably get lists like this from somewhere else. Also, the code doesn't seem to do anything with this list, but I assume that's left out - you probably want it as part of the model or viewbag etc.

Unit Testing - updating model

I am new to unit testing and I am trying to write a test to verify that when I update my user object the correct fields are being updated. My unit test looks like:
[Test]
public void ShouldUpdateExistingEmployee()
{
var employees = new Employee[]
{
new Employee()
{
EmployeeId = 1,
FirstName = "Johhn",
LastName = "Smiths",
Email = "John.Smith1#Illinois.gov",
IsActive = true
}
};
var mockContext = new Mock<SqlContext>();
mockContext.Setup(e => e.Employees).ReturnsDbSet(employees);
mockContext.Setup(m => m.Employees.Find(It.IsAny<object[]>()))
.Returns<object[]>(
ids => employees.FirstOrDefault(d => d.EmployeeId == (int)ids[0]));
var sut = new EmployeeRepository(mockContext.Object);
var employeeToUpdate = new Employee
{
EmployeeId = 1,
FirstName = "John",
LastName = "Smith",
Email = "John.Smith#Illinois.gov",
IsActive = true
};
sut.Save(employeeToUpdate);
Assert.That(employees.First().FirstName, Is.EqualTo(employeeToUpdate.FirstName));
Assert.That(employees.First().LastName, Is.EqualTo(employeeToUpdate.LastName));
Assert.That(employees.First().Email, Is.EqualTo(employeeToUpdate.Email));
}
My repository looks like:
public void Save(Employee employee)
{
if (employee.EmployeeId > 0)
{
Employee dbEmployee = Context.Employees.Find(employee.EmployeeId);
Context.Entry(dbEmployee).CurrentValues.SetValues(employee);
}
else
{
Context.Employees.Add(employee);
}
Context.SaveChanges();
}
The problem is when I get to Context.Entry(dbEmployee).CurrentValues.SetValues(employee); in my repository I get the following error: Member 'CurrentValues' cannot be called for the entity of type 'Employee' because the entity does not exist in the context. To add an entity to the context call the Add or Attach method of DbSet<Employee>.
Any help would be appreciated!
Based on this article, you should change:
Employee dbEmployee = Context.Employees.Find(employee.EmployeeId);
Context.Entry(dbEmployee).CurrentValues.SetValues(employee);
to:
Context.Employees.Attach(employee)
Then you should change your assert to verify that the Attach method was called with employeeToUpdate.(you hide the DBSet<> in the method ReturnsDbSet so I couldn't add an example...)
One more thing, I think you should take a look in this code snippet which shows the right way to mock DBContext and DBSet<> using Moq. Or read this article

I have to make a method public and virtual to using Moq Setup

I have a repository that has a few private methods in to help in some general stuff that needs to be done within that repository (don't feel you need to read all the code):
public class EFBlogRepository : EFGenericRepository<Blog>, IBlogRepository
{
public EFBlogRepository( EFDbContext context )
: base( context )
{
this.context = context;
}
// Problematic method
private IQueryable<Blog> PrepareAllBlogsQuery( int page, int amount, string sort, string order, ISearchCriteria searchCriteria )
{
var query = context.Blogs
.Where( x => x.DeletedAt == null );
...
return query;
}
public IEnumerable<Blog> GetAllBlogs( int page, int amount, string sort, string order, ISearchCriteria searchCriteria )
{
return this.PrepareAllBlogsQuery( page, amount, sort, order, searchCriteria )
.Skip( ( page - 1 ) * amount )
.Take( amount );
}
public int CountAllBlogs( int page, int amount, string sort, string order, ISearchCriteria searchCriteria )
{
return this.PrepareAllBlogsQuery( page, amount, sort, order, searchCriteria )
.Count();
}
The problem comes when I try and unit test this...
I have had to make the PrepareAllBlogsQuery public and virtual to get this to work (You just need to read the commented bit):
// Arrange
DateTime now = DateTime.Now;
var mockDbContext = new Mock<EFDbContext>();
var blogRepository = new Mock<EFBlogRepository>(mockDbContext.Object);
IDbSet<Blog> blogDbSet = new FakeDbSet<Blog>();
List<Blog> blogs = new List<Blog> {
new Blog { BlogID = 1, Description = "1", Status = true, PublishDate = now },
new Blog { BlogID = 2, Description = "2", Status = true, PublishDate = now },
new Blog { BlogID = 3, Description = "3", Status = true, PublishDate = now },
new Blog { BlogID = 4, Description = "4", Status = true, PublishDate = now },
new Blog { BlogID = 5, Description = "5", Status = true, PublishDate = now },
new Blog { BlogID = 6, Description = "6", Status = true, PublishDate = now },
};
IQueryable<Blog> blogsQueryable = blogs.AsQueryable();
mockDbContext.SetupGet(c => c.Blogs).Returns(blogDbSet);
// This Setup requires my method to be public and virtual :(
blogRepository.SetupGet(c => c.PrepareAllBlogsQuery(2, 2, SortDirection.DESC, null, null)).Returns(blogsQueryable);
// Act
List<Blog> result = blogRepository.Object.GetAllBlogs(2, 2, SortDirection.DESC, null, null).ToList();
// Assert
Assert.AreEqual("3", result[0].Description);
Is there any way around this?
It's not like I even want to test the PrepareAllBlogsQuery method, I just need the mock framework to know what that method returns regardless of its content.
Yes, you should create an interface for your repository which is the thing you mock in your unit test:
public interface IEFBlogRepository
{
IEnumerable<Blog> GetAllBlogs( int page, int amount, string sort, string order, ISearchCriteria searchCriteria )
}
public class EFBlogRepository : IEFBlogRepository
{
...
}
Then in your unit test, you can mock the IEFBlogRepository and you don't need to go anywhere near EF.
var mockRepository = new Mock<IEFBlogRepository>();
mockRepository.Setup(r => r.....);
var thing = new Thing(mockRepository.Object);
thing.DoSomeStuffWhichCallsRepository();
Update
Since it seems you are trying to test EFBlogRepository, you shouldn't be mocking that class, you should use EFBlogRepository itself and just mock its dependencies. You should be able to do something like this to get the correct DB set although I don't know what your FakeDbSet<Blog> actually is:
var blogs = new List<Blog> { ... };
var blogDbSet = new FakeDbSet<Blog>(blogs.AsQueryable());
mockDbContext.SetupGet(c => c.Blogs).Returns(blogDbSet);
The reason Blogs is null for you is because blogDbSet isn't actually configured to return the blogsQueryable
You can have the method declared as internal and expose internals to specific Assembly in your case unit test project's output assembly.
Below attributes needs to be mentioned in the AssemblyInfo.cs of the project being tested.
[assembly:InternalsVisibleToAttribute("UnitTestAssemblyName")]
In case of strongly named
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("BoardEx_BusinessObjects.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fb3a2")]
there's a similar thread answering the same click here

MVC error working with a List getting Object not set to an instance of an object

I am getting an error I can't figure out how to fix? It claims to be happening on:
var respondents = RespondentRepository.GetRespondents(UserSession, fieldsToInclude).Where(r=>r.Email.Contains(query)).ToList();
When I remove all the .Where stuff it appears to work fine. I am not really use to this error so I am not sure where the best start would be?
Error:
System.NullReferenceException: Object reference not set to an instance of an object.
Code:
MakeSafeRequest(() =>
{
var respondents = RespondentRepository.GetRespondents(UserSession, fieldsToInclude).Where(r=>r.Email.Contains(query)).ToList();
model = new RespondentSearchViewModel
{
Respondents = respondents,
TableData = respondents.SelectToListOrEmpty(r =>
{
return new TableRowViewModel
{
Id = r.Id,
Data = new Dictionary<string, string>
{
{ "FirstName", r.FirstName },
{ "LastName", r.LastName },
{ "EmailAddress", r.Email },
{ "Project", r.ProjectId.ToString() },
{ "Status", r.RecruitingStatus.ToIconHtml() },
{ "ViewHistory", "<div class=\"btnBlue btSml\"><a class=\"closeMe\" href=\"/RespondentSearch/RespondentDetails/?respondentId="+r.Id +"\" >view detail</a></div>"}
//{ "StatusDate", r.LastActionDate.Value.ToLocalTime().ToString() },
}
};
})
};
});
MODEL:
public class RespondentSearchViewModel:ListPageViewModel
{
public List<Respondent> Respondents { get; set; }
}
r.Email might be null. Try changing your lambda to:
r => (r.Email == null ? false : r.Email.Contains(query))

Categories