NSubstitute DbSet / IQueryable

后端 未结 5 1456
心在旅途
心在旅途 2020-12-02 12:12

So EntityFramework 6 is a lot better testable then previous versions. And there are some nice examples on the internet for frameworks like Moq, but the case is, I prefer usi

5条回答
  •  暗喜
    暗喜 (楼主)
    2020-12-02 13:02

    Thanks to Kevin, I've found the problem in my code translation.

    The unittest code samples are mocking DbSet, but NSubstitute requires the interface implementation. So the equivalent of Moqs new Mock>() for NSubstitute is Substitute.For>(). You're not always required to provide the Interface, so that's why I was confused. But in this specific case, it turned out to be crucial.

    It also turned out that we don't have to cast to Queryable when using the interface IDbSet.

    So the working test code:

    public void GetAllBlogs_orders_by_name()
    {
      // Arrange
      var data = new List
      {
        new Blog { Name = "BBB" },
        new Blog { Name = "ZZZ" },
        new Blog { Name = "AAA" },
      }.AsQueryable();
    
      var mockSet = Substitute.For>();
      mockSet.Provider.Returns(data.Provider);
      mockSet.Expression.Returns(data.Expression);
      mockSet.ElementType.Returns(data.ElementType);
      mockSet.GetEnumerator().Returns(data.GetEnumerator());
    
      var mockContext = Substitute.For();
      mockContext.Blogs.Returns(mockSet);
    
      // Act and Assert ...
    }
    

    I've written a small extention method to cleanup the Arrange section of the unit tests.

    public static class ExtentionMethods
    {
        public static IDbSet Initialize(this IDbSet dbSet, IQueryable data) where T : class
        {
            dbSet.Provider.Returns(data.Provider);
            dbSet.Expression.Returns(data.Expression);
            dbSet.ElementType.Returns(data.ElementType);
            dbSet.GetEnumerator().Returns(data.GetEnumerator());
            return dbSet;
        }
    }
    
    // usage like:
    var mockSet = Substitute.For>().Initialize(data);
    

    Not the question, but in case you also need to be able to support async operations:

    public static IDbSet Initialize(this IDbSet dbSet, IQueryable data) where T : class
    {
      dbSet.Provider.Returns(data.Provider);
      dbSet.Expression.Returns(data.Expression);
      dbSet.ElementType.Returns(data.ElementType);
      dbSet.GetEnumerator().Returns(data.GetEnumerator());
    
      if (dbSet is IDbAsyncEnumerable)
      {
        ((IDbAsyncEnumerable) dbSet).GetAsyncEnumerator()
          .Returns(new TestDbAsyncEnumerator(data.GetEnumerator()));
        dbSet.Provider.Returns(new TestDbAsyncQueryProvider(data.Provider));
      }
    
      return dbSet;
    }
    
    // create substitution with async
    var mockSet = Substitute.For, IDbAsyncEnumerable>().Initialize(data);
    // create substitution without async
    var mockSet = Substitute.For>().Initialize(data);
    

提交回复
热议问题