I have a class like:
public class Store
{
public Store()
{
Products = new List<Product>();
}
public IList<Product> Products {get; private set;}
public void AddProduct(int id, string productCode)
{
Product p = new Product();
p.Id = id;
p.ProductCode = productCode;
//Validate before adding
Products.Add(p); //How can i verify that this was called
}
}
Using Moq how can i verify that the Add method of the Products List was called? Can someone provide a simple example?
Well you can't really mock anything at the moment as the Products list is set up in the default constructor?
The easiest thing to do would be to assert against your products collection manually (i.e. just verify there's a product in there with the specified ID and Code) then you don't have to worry about mocking at all.
If you really want to use Moq to test this you need to provide a means to inject your Mock and get around your constructor, as an example you can provide two constructors
public class Store {
public Store() : this(new List<Product>()) {
}
public Store(IList<Product> productList) {
Products = productList
}
//Implementation
}
You can then write a test against your add method as follows
[Test]
public AddProduct_WithIdAndProductCode_AddsProductToCollection() {
int productId = 0;
string productCode = "a";
var productListMock = new Mock<IList<Product>>();
Store store = new Store(productListMock.Object);
store.AddProduct(productId, productCode);
productListMock.Verify(pl =>
pl.Add(It.Is<Product>(p =>
p.Id == productId && p.ProductCode == productCode)));
}
Do you need to test that add was called or that the list now has the expected number of items?
Assert.True(store.Products.Count == 1);
Related
class Topic {
public int TopicId {get;set;}
public virtual ICollection<Post> Posts { get; set; }
public Post FirstPost {
get {
return this.Posts.OrderBy(p=> p.PostedDate).FirstOrDefault();
}
}
}
class Post {
public int PostId {get;set; }
public int TopicId {get;set;}
public DateTime PostedDate {get;set;}
public virtual Topic Topic {get;set;}
}
var query = Database.Forums.Where(p=> p.Id == id).Select(p=> new {
p.Title,
Topics = p.Topics.OrderByDescending(p=> p.LastPostedDate).Select(t=> new {
t.TopicId,
t.FirstPost.PostId
})
}).ToList();
When I run this query, t.FirstPost is null even though the topic does have posts in the database. Is there a way to do this using navigational properties instead of using query syntax and joins?
I think you need to update code from this.Post to this.Posts like this
public Post FirstPost {
get {
return this.Posts.OrderBy(p=> p.PostedDate).FirstOrDefault();
}
}
In general avoid using not mapped properties in LINQ to Entities query. They cannot be translated to SQL, and even that EF Core supports client side evaluation, accessing the navigation properties is problematic since they are not loaded yet at the time evaluation occurs.
You can use navigations properties inside LINQ to Entities query (which is actually preferable over explicit joins), but with explicit expression, i.e. not hidden behind unmapped property:
var query = Database.Forums.Where(f => f.Id == id).Select(f => new
{
f.Title,
Topics = f.Topics.OrderByDescending(t => t.LastPostedDate).Select(t => new
{
t.TopicId,
FirstPostId = Posts.OrderBy(p => p.PostedDate).Select(p => (int?)p.PostId).FirstOrDefault(),
})
}).ToList();
(not sure what LastPostedDate is - it's not shown in the posted model, and hope is not another unmapped property. But you get the idea).
I have a class with properties in it that are populated via a loader class. Quick example:
class Employee : IEmployee
{
public string EmpFirstName {get; set}
public string EmpLastName {get; set}
}
public class EmpLoader(int employeeID)
{
public void Load(IEmployee emp)
{
emp.EmpFirstName = //lookup the employee using the EmployeeID
//...
}
}
I'm wondering how to go about arranging things so that a mocked EmpLoader's Load() method fills in particular values in the Employee. Something like:
Employee myEmp = new Employee();
_empLoader = new Mock<EmpLoader>();
_empLoader.Setup(empL => empL.Load(myEmp)).Sets_myEmp_Properties_Somehow();
I've used Moq's Setup() method before when deciding what sort of return values come back, but wasn't sure if I can use it or another method to set properties in one class via a third party class. Could be I'm way off here; I'm not an expert in Moq and am open to suggestions.
You can use the Callback() method on the Setup() to load the data:
Employee myEmp = new Employee();
Mock<EmpLoader> _empLoader = new Mock<EmpLoader>();
_empLoader.Setup(empL => empL.Load(myEmp)).Callback<IEmployee>((emp) => {
emp.EmpFirstName = "Steve";
// ... Load all properties
}
I'm new to Mocking, but this must be something really basic that I'm missing:
The test code below produces an exception:
Expected invocation on the mock at least once, but was never performed: x => x.DeleteProducts(._products)\r\n\r\nConfigured setups:\r\nx => x.DeleteProducts(._products), Times.Never\r\n\r\nPerformed invocations:\r\nIProductRepository.DeleteProducts(System.Collections.Generic.List`1[WebApiDemo.DataAccessLayer.Product])
I step through the controller method and it does seem to call the DeleteProducts method...
// Arrange
IEnumerable<Product> _products = Helpers.ProductHelpers.CreateProducts(_numberProducts);
Mock<IProductRepository> _productRepository = new Mock<IProductRepository>();
_productRepository.Setup(x => x.DeleteProducts(_products));
ProductsController controller = new ProductsController(_productRepository.Object);
// Act
controller.Destroy(_productViewModels); // Destroy calls DeleteProducts
// Assert
_productRepository.Verify(x => x.DeleteProducts(_products));
Does DeleteProducts(_products); return void? I assume it does, so you need to put the .Verifiable() at the end of the .Setup() for it.
With that in place, it should run through ok, although I'm not sure why you have the Times.Never() instead of Times.Once() ??
I would also advocate on the Setup call using It.IsAny<T> rather than a specific collection, such as:
MyMock.Setup(x => x.MyMethod(It.IsAny<IEnumerable<Widget>>)).Verifiable()
Unless you have the mock behaviour set to strict there is no need for the setup. You are not returning anything from the Delete. The call to Verify will suffice.
Some things are not totally obvious from the code.
The repository deletes products but the controller destroys productviewmodels.
In Moq 4 the test should work if
You have a product view model for every product in _products
The Controller.Destroy method gets products from the viewmodels in the same order as _products
I would check that _productViewModels is a 1:1 match to the _products and check how Destroy() extracts the products from the viewmodels before calling Delete()
I would not go with the IsAny>() because you want to check that these specific products were deleted not any other ones.
[TestClass]
public class Verifying {
public interface IProductRepository {
void Delete(IEnumerable<Product> products);
}
public class ProductController {
private IProductRepository _repository;
public ProductController(IProductRepository repository) {
_repository = repository;
}
public void Destroy(IEnumerable<Product> products) {
_repository.Delete(products);
}
public void Destroy(IEnumerable<ProductViewModel> productViewModels) {
_repository.Delete(productViewModels.Select(vm => vm.Product));
}
}
public class Product {
}
public class ProductViewModel {
public Product Product { get; set;}
}
static Verifying() {
sProducts = new List<Product> { new Product(), new Product(), new Product() };
sProductViewModels = new List<ProductViewModel>(sProducts.Select(p => new ProductViewModel { Product = p }));
}
private static List<Product> sProducts;
private static List<ProductViewModel> sProductViewModels;
private Mock<IProductRepository> _mockRepository;
private ProductController CreateController() {
_mockRepository = new Mock<IProductRepository>();
return new ProductController(_mockRepository.Object);
}
[TestMethod]
public void DestroyingProducts() {
var controller = CreateController();
controller.Destroy(sProducts);
_mockRepository.Verify(mk => mk.Delete(sProducts));
}
[TestMethod]
public void DestroyingProductViewModels() {
var controller = CreateController();
controller.Destroy(sProductViewModels);
_mockRepository.Verify(mk => mk.Delete(sProducts));
}
}
I want to update a "Post" and change relationships with "Categories" that already created before. Post entity has ICollection of Categories. But categories are not changed. It seems, that EF does not track entity relations. By the way I have no problem with creating of new Posts with assigning of Categories.
There are two models:
public class Post
{
public virtual int PostId { get; set; }
...
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public virtual int CategoryId { get; set; }
...
public virtual ICollection<Post> Posts { get; set; }
}
The Add controller, that works as expected:
public ActionResult Create(Post model)
{
var c = Request.Form["CategoryID"].Split(',');
model.Categories = c.Select ... .ToList(); //here I assign relationships with attached objects
_repository.Add(model);
_repository.SaveChanges();
...
}
Repository Add method:
T IRepository.Add<T>(T entity)
{
return Set<T>().Add(entity);
}
The Edit controller does not save changed categories, only post props.
public ActionResult Edit(Post model)
{
var c = Request.Form["CategoryID"].Split(',');
model.Categories = c.Select ... .ToList(); //here I update relationships with attached objects
_repository.Attach(model);
_repository.SaveChanges();
...
}
Repository Edit method:
T IRepository.Attach<T>(T entity)
{
var entry = Entry(entity);
entry.State = System.Data.EntityState.Modified;
return entity;
}
Am I doing something wrong?
Thanks in advance
Solution:
public ActionResult Edit(Post model)
{
model = _repository.Attach(model);
var post = _repository.Posts.Include(p => p.Categories).Single(s => s.PostId == model.PostId);
post.Categories.Clear();
model.Categories = GetCategories();
_repository.SaveChanges();
}
First Attach the object (EntityState.Modified)
Query the object with Include or other method for Loading Related Entities
Clear the existent relations. I don like this, but I cannot find another way
Assign new relations from the view and SaveChanges.
Entity Framework won't track relationship changes this way. It only tracks the states of objects, so the proper way would be to load the "Post" that you want with all categories and then to modify the loaded collection - this way changes are going to be reflected in all objects' states.
Manipulate category collection i.e. (add, remove, edit) in post class. If you are using same DbContext then changes will be tracked. It should be worked.
add a category
_post.Categories.Add(category1);
delete category
_post.Categories.Remove(category1);
edit category
_post.Categories[0].Name = "TEST Name";
udapte a object
string ImgID = CompCert.CertID.ToString() + "ComID" + CompId + ext;
CompCert.CertImageFile = ImgID;
db.ObjectStateManager.ChangeObjectState(CompCert, EntityState.Modified);
db.SaveChanges();
I am trying to test a property that is nested in a child class.
I always get an error.
Am I missing something?
Is it possible to test a child property in moq.
I have the following
[Test]
public void Should_be_able_to_test_orderCollection()
{
var orderViewMock = new Mock<IOrderView>();
orderViewMock.SetupGet(o => o.Customer.OrderDataCollection.Count).Returns(2);
orderViewMock.SetupSet(o => o.Customer.OrderDataCollection[1].OrderId = 1);
orderViewMock.VerifySet(o => o.Customer.OrderDataCollection[1].OrderId=1);
}
public class CustomerTestHelper
{
public static CustomerInfo GetCustomer()
{
return new CustomerInfo
{
OrderDataCollection = new OrderCollection
{
new Order {OrderId = 1},
new Order {OrderId = 2}
}
};
}
}
public class CustomerInfo
{
public OrderCollection OrderDataCollection { get; set; }
}
public class OrderCollection:List<Order>
{
}
public class Order
{
public int OrderId { get; set; }
}
public interface IOrderView
{
CustomerInfo Customer { get; set; }
}
You can't mock the OrderDataCollection property of CustomerInfo because it's a non-virtual property on a concrete class.
The best way to fix this would be to extract an interface from CustomerInfo and let IOrderView return that instead:
public interface IOrderView
{
ICustomerInfo Customer { get; set; }
}
It is definitely possible if you have the right abstractions. You need to mock your Customer and its children too, for your example to work, like:
var customerMock = new Mock<ICustomer>();
orderViewMock.SetupGet(o => o.Customer).Returns(customerMock.Object);
etc. for the entire hierarchy of child objects you want to control with mocks. Hope it makes sense.
/Klaus
You will get a runtime error, as you've found:
System.ArgumentException: Invalid setup on a non-overridable member:
o => o.Customer.OrderDataCollection.Count
at Moq.Mock.ThrowIfCantOverride(Expression setup, MethodInfo methodInfo)
You can mock the IOrderView and return any CustomerInfo instance you want, but you're also trying to mock CustomerInfo and OrderCollection. As Mark Seemann mentioned, you can only mock interfaces and virtual properties/methods. This will hold true for almost any mocking/isolation framework except for Typemock (commercial).
As others have already stated, one way to solve the problem is to return an interface for the customer.