模拟的DbSet不返回对象 [英] Mocked DbSet not returning an object

查看:162
本文介绍了模拟的DbSet不返回对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图通过使用Moq嘲笑数据来测试更新功能。我正在使用实体框架6。



我可以打印出一个 DbSet 的计数,这是预期的。但是,当它尝试选择一个对象时,它会引发异常, NullReferenceException:Object引用未设置为对象的实例。



这是我的测试类,它设置了嘲笑的 DbSets DbContext

  [TestFixture] 
public class ProductControllerTest
{
private ProductController controller;
私人IProductRepository productRepo;
private IUnitOfWork unitOfWork;
私人IBrandRepository brandRepo;
private ICategoryRepository categoryRepo;
private ISegmentRepository segmentRepo;
private ITypeRepository typeRepo;
private IEnumerable< Product>产品列表;

[SetUp]
public void Init()
{
IEnumerable<品牌> brandList = new List< Brand> {
new Brand {
Id = 1,
Name =Unknown
},
new品牌{
Id = 2,
Name =Clorox
},
new品牌{
Id = 3,
Name =Glad
}
};
var brandData = brandList.AsQueryable();

productList = new List< Product> {
new Product {
Id =0000000001,
ParentAsin =0000000010,
Title = Mocked Product#1,
ReleaseDate = DateTime.Now,
BrandId = 1,
CategoryId = 1,
SegmentId = 1,
TypeId = 1,
Brand = brandList.ElementAt(0)
},
new Product {
Id =0000000002,
ParentAsin =0000000010,
Title = Mocked Product#2,
ReleaseDate = DateTime.Now,
BrandId = 1,
CategoryId = 1,
SegmentId = 1,
TypeId = 1,
品牌= brandList.ElementAt(0)
},
新产品{
Id =0000000003,
ParentAsin =0000000010,
Title =Mocked Product#3,
ReleaseDate = DateTime.Now,
BrandId = 2,
CategoryId = 3,
SegmentId = 3,
TypeId = 2,
品牌= brandList.ElementAt(1)
}
};
var productData = productList.AsQueryable();

brandList.ElementAt(1).Products.Add(productList.ElementAt< Product>(2));

var mockProductSet = new Mock< DbSet< Product>>();
mockProductSet.As< IQueryable< Product>>()。Setup(m => m.Provider).Returns(productData.Provider);
mockProductSet.As< IQueryable< Product>>()。Setup(m => m.Expression).Returns(productData.Expression);
mockProductSet.As< IQueryable< Product>>()。Setup(m => m.ElementType).Returns(productData.ElementType);
mockProductSet.As< IQueryable< Product>>()。Setup(m => m.GetEnumerator())。Returns(productData.GetEnumerator());

var mockBrandSet = new Mock< DbSet<品牌>>();
mockBrandSet.As< IQueryable< Brand>>()。Setup(m => m.Provider).Returns(brandData.Provider);
mockBrandSet.As< IQueryable< Brand>>()。Setup(m => m.Expression).Returns(brandData.Expression);
mockBrandSet.As< IQueryable< Brand>>()。Setup(m => m.ElementType).Returns(brandData.ElementType);
mockBrandSet.As< IQueryable< Brand>>()。Setup(m => m.GetEnumerator())。Returns(brandData.GetEnumerator());

var mockContext = new Mock< ApplicationDbContext>(){CallBase = true};
mockContext.Setup(m => m.Set< Product>())。Returns(mockProductSet.Object);
mockContext.Setup(m => m.Set< Brand>())。Returns(mockBrandSet.Object);

unitOfWork = new UnitOfWork(mockContext.Object);
brandRepo = new BrandRepository(mockContext.Object);
productRepo = new ProductRepository(mockContext.Object);
controller = new ProductController(productRepo,unitOfWork,brandRepo,categoryRepo,segmentRepo,typeRepo);
}

[Test]
public void TestReturnEditedModel()
{
Product product = productList.ElementAt< Product>(1);
product.BrandId = 3;
product.CategoryId = 2;
product.SegmentId = 2;
product.TypeId = 3;

controller.Edit(product,Return value);

产品结果= productRepo.Get(product.Id);
Assert.AreEqual(product.Id,result.Id);
Assert.AreEqual(3,result.BrandId);
Assert.AreEqual(2,result.CategoryId);
Assert.AreEqual(2,result.SegmentId);
Assert.AreEqual(3,result.TypeId);
}
}

我只提供了失败的测试。 p>

这是控制器函数调用

  [HttpPost] 
public ActionResult Edit([Bind(Include =Id,Title,ParentAsin,ReleaseDate,BrandId,CategoryId,SegmentId,TypeId)]产品型号,string returnAction)
{
if(!ModelState.IsValid )
{
字典< string,int> selectedIds = new Dictionary< string,int>();
selectedIds.Add(BrandId,model.BrandId);
selectedIds.Add(CategoryId,model.CategoryId);
selectedIds.Add(SegmentId,model.SegmentId);
selectedIds.Add(TypeId,model.TypeId);
PopulateAllDropDownLists(selectedIds);
return View(model);
}

model.Brand = _brandRepo.Get(model.BrandId);
model.Category = _categoryRepo.Get(model.CategoryId);
model.Segment = _segmentRepo.Get(model.SegmentId);
model.Type = _typeRepo.Get(model.TypeId);
_repository.Update(model);
_unitOfWork.SaveChanges();
return RedirectToAction(returnAction);
}

_brandRepo 键入 IBrandRepository ,并且在所有实现和继承之后,函数 Get()在通用存储库类中。 p>

这是正在调用的Get函数。

  public virtual TEntity Get(TId id)
{
return this.DbSet.Single(x =>(object)x.Id ==(object)id);
}

返回行是什么是抛出错误。



由于这是一个测试,我在嘲笑数据,我知道传入的Id是正确的。它起始于 int Id 品牌也是一个 int ,但为了使这个通用,该属性的类型为 TId 是一种通用类型的



这是TEntity

  public interface IEntity< TId> 
{
///< summary>
///获取或设置唯一标识符。
///< / summary>
///< value>唯一标识符< / value>
TId Id {get;组; }
}

我不知道这是一个嘲弄的问题还是一个问题使用通用类型。有人可以帮助这个。

解决方案

如果在任何时候你尝试通过他们的属性访问DbSets,而不是通过设置<> 如果没有设置,会导致该问题。虽然在原始示例中调用base是true,但是 DbContext 将在内部尝试发现 DbSets 并初始化它们将失败当嘲笑DbContext。这是他们必须在模拟中设置来覆盖默认行为。

  var mockContext = new Mock< ApplicationDbContext>( ); 
mockContext.Setup(m => m.Set< Product>())。Returns(mockProductSet.Object);
mockContext.Setup(m => m.Set< Brand>())。Returns(mockBrandSet.Object);
mockContext.Setup(m => m.Products).Returns(mockProductSet.Object);
mockContext.Setup(m => m.Brands).Returns(mockBrandSet.Object);

同时设置 GetEnumerator() ,使用一个函数来允许多次调用



例如

  mockProductSet 。< IQueryable< Product>>()
.Setup(m => m.GetEnumerator())
.Returns(()=> productData.GetEnumerator());


I am trying to test an update function by mocking the data using Moq. I am using Entity Framework 6.

I can print out a count of the DbSet and it is the expected amount. However, when it tries to select an object, it throws an exception, NullReferenceException: Object reference not set to an instance of an object.

Here is my test class which sets up the mocked DbSets and DbContext

[TestFixture]
public class ProductControllerTest
{
    private ProductController controller;
    private IProductRepository productRepo;
    private IUnitOfWork unitOfWork;
    private IBrandRepository brandRepo;
    private ICategoryRepository categoryRepo;
    private ISegmentRepository segmentRepo;
    private ITypeRepository typeRepo;
    private IEnumerable<Product> productList;

    [SetUp]
    public void Init()
    {
        IEnumerable<Brand> brandList = new List<Brand>{
            new Brand{
                Id = 1,
                Name = "Unknown"
            },
            new Brand{
                Id = 2,
                Name = "Clorox"
            },
            new Brand{
                Id = 3,
                Name = "Glad"
            }
        };
        var brandData = brandList.AsQueryable();

        productList = new List<Product>{
            new Product{
                Id = "0000000001",
                ParentAsin = "0000000010",
                Title = "Mocked Product #1",
                ReleaseDate = DateTime.Now,
                BrandId = 1,
                CategoryId = 1,
                SegmentId = 1,
                TypeId = 1,
                Brand = brandList.ElementAt(0)
            }, 
            new Product{
                Id = "0000000002",
                ParentAsin = "0000000010",
                Title = "Mocked Product #2",
                ReleaseDate = DateTime.Now,
                BrandId = 1,
                CategoryId = 1,
                SegmentId = 1,
                TypeId = 1,
                Brand = brandList.ElementAt(0)
            },
            new Product{
                Id = "0000000003",
                ParentAsin = "0000000010",
                Title = "Mocked Product #3",
                ReleaseDate = DateTime.Now,
                BrandId = 2,
                CategoryId = 3,
                SegmentId = 3,
                TypeId = 2,
                Brand = brandList.ElementAt(1)
            }
        };
        var productData = productList.AsQueryable();

        brandList.ElementAt(1).Products.Add(productList.ElementAt<Product>(2));

        var mockProductSet = new Mock<DbSet<Product>>();
        mockProductSet.As<IQueryable<Product>>().Setup(m => m.Provider).Returns(productData.Provider);
        mockProductSet.As<IQueryable<Product>>().Setup(m => m.Expression).Returns(productData.Expression);
        mockProductSet.As<IQueryable<Product>>().Setup(m => m.ElementType).Returns(productData.ElementType);
        mockProductSet.As<IQueryable<Product>>().Setup(m => m.GetEnumerator()).Returns(productData.GetEnumerator());

        var mockBrandSet = new Mock<DbSet<Brand>>();
        mockBrandSet.As<IQueryable<Brand>>().Setup(m => m.Provider).Returns(brandData.Provider);
        mockBrandSet.As<IQueryable<Brand>>().Setup(m => m.Expression).Returns(brandData.Expression);
        mockBrandSet.As<IQueryable<Brand>>().Setup(m => m.ElementType).Returns(brandData.ElementType);
        mockBrandSet.As<IQueryable<Brand>>().Setup(m => m.GetEnumerator()).Returns(brandData.GetEnumerator());

        var mockContext = new Mock<ApplicationDbContext>() { CallBase = true };
        mockContext.Setup(m => m.Set<Product>()).Returns(mockProductSet.Object);
        mockContext.Setup(m => m.Set<Brand>()).Returns(mockBrandSet.Object);

        unitOfWork = new UnitOfWork(mockContext.Object);
        brandRepo = new BrandRepository(mockContext.Object);
        productRepo = new ProductRepository(mockContext.Object);
        controller = new ProductController(productRepo, unitOfWork, brandRepo, categoryRepo, segmentRepo, typeRepo);
    }

    [Test]
    public void TestReturnEditedModel()
    {
        Product product = productList.ElementAt<Product>(1);
        product.BrandId = 3;
        product.CategoryId = 2;
        product.SegmentId = 2;
        product.TypeId = 3;

        controller.Edit(product, "Return value");

        Product result = productRepo.Get(product.Id);
        Assert.AreEqual(product.Id, result.Id);
        Assert.AreEqual(3, result.BrandId);
        Assert.AreEqual(2, result.CategoryId);
        Assert.AreEqual(2, result.SegmentId);
        Assert.AreEqual(3, result.TypeId);
    }
}

I provided only the test that is failing.

Here is the controller function being called

[HttpPost]
public ActionResult Edit([Bind(Include = "Id,Title,ParentAsin,ReleaseDate,BrandId,CategoryId,SegmentId,TypeId")]Product model, string returnAction)
{
    if(!ModelState.IsValid)
    {
        Dictionary<string, int> selectedIds = new Dictionary<string, int>();
        selectedIds.Add("BrandId", model.BrandId);
        selectedIds.Add("CategoryId", model.CategoryId);
        selectedIds.Add("SegmentId", model.SegmentId);
        selectedIds.Add("TypeId", model.TypeId);
        PopulateAllDropDownLists(selectedIds);
        return View(model);
    }

    model.Brand = _brandRepo.Get(model.BrandId);
    model.Category = _categoryRepo.Get(model.CategoryId);
    model.Segment = _segmentRepo.Get(model.SegmentId);
    model.Type = _typeRepo.Get(model.TypeId);
    _repository.Update(model);
    _unitOfWork.SaveChanges();
    return RedirectToAction(returnAction);
}

_brandRepo is of type IBrandRepository and after all the implementations and inheritance, the function Get() is in a Generic repository class.

Here is the Get function that is being called.

public virtual TEntity Get(TId id)
{
    return this.DbSet.Single(x => (object)x.Id == (object)id);
}

The return line is the what is throwing the error.

Since this is a test and I'm mocking the data, I know that the Id being passed in is correct. It starts off as an int and the Id of Brand is an int as well, but to make this generic, the property is of type TId is a generic type of the interface TEntity which all models implement.

Here is TEntity

public interface IEntity<TId>
{
    /// <summary>
    /// Gets or sets the unique identifier.
    /// </summary>
    /// <value>The unique identifier.</value>
    TId Id { get; set; }
}

I'm not sure if this is a mocking issue or an issue with using generic types. Can someone help with this.

解决方案

If at any point you try to access the DbSets via their properties and not via Set<> is what would cause that problem if they were not setup. Although call base was true in the original example, the DbContext would internally try to discover DbSets and initializes them which would fail when mocking the DbContext. This is what they would have to setup in the mock to override the default behavior.

var mockContext = new Mock<ApplicationDbContext>();
mockContext.Setup(m => m.Set<Product>()).Returns(mockProductSet.Object);
mockContext.Setup(m => m.Set<Brand>()).Returns(mockBrandSet.Object);
mockContext.Setup(m => m.Products).Returns(mockProductSet.Object);
mockContext.Setup(m => m.Brands).Returns(mockBrandSet.Object);

Also when setting up the GetEnumerator(), use a function to allow for multiple call

eg

mockProductSet.As<IQueryable<Product>>()
    .Setup(m => m.GetEnumerator())
    .Returns(() => productData.GetEnumerator());

这篇关于模拟的DbSet不返回对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆