嘲笑DbSet没有返回一个对象(Mocked DbSet not returning an obje

2019-10-21 01:09发布

我试图通过使用嘲讽起订量数据,以测试更新功能。 我使用实体框架6。

我可以打印出的计数DbSet ,这是预期的量。 然而,当它试图选择一个对象,它抛出一个异常, NullReferenceException: Object reference not set to an instance of an object.

下面是我的测试类设置的嘲笑DbSetsDbContext

[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);
    }
}

我只提供了失败的考验。

这里是在控制器被调用函数

[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的类型是IBrandRepository和所有的实现和继承后,该功能Get()是一个通用库类。

这里是正在调用get函数。

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

返回行是什么引发错误。

由于这是一个测试,我嘲笑的数据,我知道所传递的ID中是正确的。 它开始作为一个intIdBrand是一个int为好,但让这个普通的,物业类型的TId是一个通用型的接口TEntity所有车型实现。

这里是TEntity

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

我不知道这是否是一个嘲讽的问题或使用泛型类型的问题。 有人可以帮助这一点。

Answer 1:

这看起来的方式复杂,你可以只使用一个通用的方法来创建任何DbSet一个模拟...

public static class DbSetMock
{
    public static Mock<DbSet<T>> CreateFrom<T>(List<T> list) where T : class
    {
        var internalQueryable = list.AsQueryable();
        var mock = new Mock<DbSet<T>>();
        mock.As<IQueryable<T>>().Setup(x => x.Provider).Returns(internalQueryable.Provider);
        mock.As<IQueryable<T>>().Setup(x => x.Expression).Returns(internalQueryable.Expression);
        mock.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(internalQueryable.ElementType);
        mock.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(()=> internalQueryable.GetEnumerator());
        mock.As<IDbSet<T>>().Setup(x => x.Add(It.IsAny<T>())).Callback<T>(element => list.Add(element));
        mock.As<IDbSet<T>>().Setup(x => x.Remove(It.IsAny<T>())).Callback<T>(element => list.Remove(element));
        return mock;
    }
}

然后你可以使用它像:

 var mockBrandSet = DbSetMock.CreateFrom(brandList);

由于列表和DbSet内的数据是一样的,你可以检查列表,坚持你的操作。



Answer 2:

如果在任何时候你尝试通过他们的性质,而不是通过访问DbSets Set<>是什么会,如果他们不是设置导致该问题。 虽然呼叫基地,在最初的例子是真, 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.As<IQueryable<Product>>()
    .Setup(m => m.GetEnumerator())
    .Returns(() => productData.GetEnumerator());


文章来源: Mocked DbSet not returning an object