【问题标题】:ASP.NET Generic method to mock EF6 DbSet模拟 EF6 DbSet 的 ASP.NET 通用方法
【发布时间】:2017-08-24 00:02:46
【问题描述】:

在我的 ASP.NET MVC5 Web 应用程序中,我使用 EF(模型优先)进行数据库通信。出于单元测试的目的,我创建了一个通用方法来生成 Mock 数据库集。不幸的是,我无法模拟所有方法,因为在我的测试代码中使用.Where().Any().Find() 时,会引发异常。谁可以帮我这个事?我的代码如下。 单元测试:

[TestClass()]
public class MessagingServiceTests
{
    Mock<BoatstersEntitiesContainer> _mockContext;
    MessagingService _service;
    string _connectionId;
    Guid _userId;

    [TestInitialize()]
    public void TestInitialize()
    {
        _userId = Guid.Parse("12345678-1234-1234-1234-123412344142");
        _connectionId = "abc123";


        // Setup entities
        User user = new User { Id = _userId, CustomerId = 1 };
        Customer customer = new Customer { Id = 1, User = user, FirstName = "TestFirstName" };
        user.Customer = customer;
        Customer boatOwnerCustomer = new Customer { Id = 2, FirstName = "BoatOwner" };
        Boat boat = new Boat { Id = 1, Customer = boatOwnerCustomer, CustomerId = boatOwnerCustomer.Id };
        boatOwnerCustomer.Boats.Add(boat);


        // Init mocksets
        var userMockSet = MockDbSet.Build(new List<User> { user });
        var customerMockSet = MockDbSet.Build(new List<Customer> { customer, boatOwnerCustomer });
        var conversationMockSet = MockDbSet.Build(new List<Conversation>());
        var messageMockSet = MockDbSet.Build(new List<Message>());
        var boatMockSet = MockDbSet.Build(new List<Boat> { boat });
        var messagingHubConnectionMockSet = MockDbSet.Build(new List<MessagingHubConnection>());


        // Setup mockcontext
        _mockContext = new Mock<BoatstersEntitiesContainer>();
        _mockContext.Setup(m => m.Users).Returns(userMockSet.Object);
        _mockContext.Setup(m => m.Customers).Returns(customerMockSet.Object);
        _mockContext.Setup(m => m.Conversations).Returns(conversationMockSet.Object);
        _mockContext.Setup(m => m.Messages).Returns(messageMockSet.Object);
        _mockContext.Setup(m => m.Boats).Returns(boatMockSet.Object);
        _mockContext.Setup(m => m.MessagingHubConnections).Returns(messagingHubConnectionMockSet.Object);


        // Start service
        _service = new MessagingService(_mockContext.Object, _userId);
    }

    [TestMethod()]
    public void When_PartnerConnected_IsTrue()
    {
        Conversation conversation = new Conversation {
            Id = 1,
            Boat = _mockContext.Object.Boats.First(b => b.Id.Equals(1)),
            BoatId = _mockContext.Object.Boats.First(b => b.Id.Equals(1)).Id
        };
        conversation.Customers.Add(_mockContext.Object.Customers.First(b => b.Id.Equals(1)));
        conversation.Customers.Add(_mockContext.Object.Customers.First(b => b.Id.Equals(2)));

        MessagingHubConnection connection = new MessagingHubConnection
        {
            Id = 1,
            Connected = true,
            ConnectionId  = "abc123",
            Conversation = conversation,
            ConversationId = 1,
            Customer = _mockContext.Object.Customers.First(b => b.Id.Equals(2)),
            CustomerId = 2
        };
        conversation.MessagingHubConnections.Add(connection);

        _mockContext.Object.MessagingHubConnections.Add(connection);
        _mockContext.Object.Conversations.Add(conversation);

        var result = _service.IsPartnerConnected();
        Assert.IsTrue(result);

        // Clean up
        _mockContext.Object.Conversations.RemoveRange(_mockContext.Object.Conversations);
        _mockContext.Object.MessagingHubConnections.RemoveRange(_mockContext.Object.MessagingHubConnections);
    }
}

通用模拟集创建者:

public static class MockDbSet
{
    public static Mock<DbSet<TEntity>> Build<TEntity>(List<TEntity> data) where TEntity : class
    {
        var queryable = data.AsQueryable();
        var mockSet = new Mock<DbSet<TEntity>>();
        mockSet.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(queryable.Provider);
        mockSet.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(queryable.Expression);
        mockSet.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
        mockSet.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
        mockSet.Setup(m => m.Add(It.IsAny<TEntity>())).Callback<TEntity>(data.Add);

        return mockSet;
    }
}

消息服务(正在测试中)

public class MessagingService : BaseRepository<Conversation>
{
    private readonly Customer _customer;
    private MessagingHubConnection _connection;

    public MessagingService(BoatstersEntitiesContainer context, Guid userId) : base(context)
    {
        Context = context;
        _customer = Context.Customers.First(c => c.User.Id == userId);
    }


    public bool IsPartnerConnected()
    {
        // Check if partner is connected
        return Context.MessagingHubConnections.Any(c => c.ConversationId.Equals(_connection.ConversationId) && c.Customer.Id != _customer.Id && c.Connected);
    }
}

在MessagingService.IsPartnerConnected()中,抛出如下异常:

测试名称:When_PartnerConnected_IsTrue 测试全名:Boatsters.Sunshine.UnitTests.MessagingServiceTests.When_PartnerConnected_IsTrue 测试源:C:\Users\Jelle\Source\Repos\Boatsters.Sunshine\Boatsters.Sunshine.UnitTests\MessagingServiceUnitTest.cs:第 94 行 测试结果:失败 测试时间:0:00:00,0289022 结果堆栈跟踪:
bij lambda_method(闭包,MessagingHubConnection) bij System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2 谓词) bij lambda_method(闭包) bij System.Linq.EnumerableExecutor1.Execute() bij System.Linq.EnumerableQuery1.System.Linq.IQueryProvider.Execute[S](表达式表达式) bij System.Linq.Queryable.Any[TSource](IQueryable1 source, Expression1 谓词) bij Boatsters.Services.MessagingService.IsPartnerConnected() 在 C:\Users\Jelle\Source\Repos\Boatsters.Sunshine\Boatsters.Services\MessagingService.cs:line 156 bij xxx.MessagingServiceTests.When_PartnerConnected_IsTrue() 在 C:\xxx\MessagingServiceUnitTest.cs:line 118 结果消息: 测试方法 xxx.UnitTests.MessagingServiceTests.When_PartnerConnected_IsTrue 抛出异常: System.NullReferenceException:对象引用未设置为对象的实例

【问题讨论】:

  • 我删除了asp.net 标签(因为最后它不是asp.net 问题)。我添加了moq 标签(因为你应该使用它,对吧?Mock&lt;&gt; 类来自moq 我认为)
  • 有什么异常。提供一个minimal reproducible example 可用于重现您的问题以及所需的行为。您如何将上面的代码应用于您要测试的内容。
  • 基于对MessagingService 的审查,没有看到_connection 的赋值位置。当基于堆栈跟踪lambda_method(Closure , MessagingHubConnection ) 调用被测方法时,看起来这就是null
  • @Nkosi 哇,谢谢!这实际上很愚蠢..我可能需要进行一些重构..
  • @JelleKerkstra 我已更新答案以反映问题。

标签: c# entity-framework unit-testing generics moq


【解决方案1】:

根据对MessagingService 的审查,_connection 的赋值位置并不明显。当基于堆栈跟踪lambda_method(Closure , MessagingHubConnection )调用被测方法时,看起来这就是null

根据过去使用 Moq 和 DbSet&lt;&gt; 的经验,需要更新此行,以便可以对数据源进行多次调用。

mockSet.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());

更改 .Returns(queryable.GetEnumerator()) 以返回 Func

mockSet.As<IQueryable<TEntity>>()
    .Setup(m => m.GetEnumerator())
    .Returns(() => queryable.GetEnumerator()); //<-- Note change here.

原始版本将为每个调用返回相同的枚举器,该枚举器只能枚举一次,可能会导致问题。使用Func 将允许在每次调用时返回一个新的枚举器,以允许对数据源进行多次传递。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-06
    • 2012-11-08
    相关资源
    最近更新 更多