使用IMongoQueryable进行单元测试.System.InvalidCastException:无法将类型为EnumerableQuery的对象转换为IOrderedMongoQueryable类型 [英] Unit Testing with IMongoQueryable. System.InvalidCastException: Unable to cast object of type EnumerableQuery to type IOrderedMongoQueryable

查看:68
本文介绍了使用IMongoQueryable进行单元测试.System.InvalidCastException:无法将类型为EnumerableQuery的对象转换为IOrderedMongoQueryable类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用方法GetAll()跟踪服务,并编写了测试此方法的测试.

I have following Service with method GetAll() and I wrote test testing this method.

public partial class DocumentTypeService : IDocumentTypeService
    {
        private readonly IRepository<DocumentType> _documentTypeRepository;
        private readonly IMediator _mediator;

        public DocumentTypeService(IRepository<DocumentType> documentTypeRepository, IMediator mediator)
        {
            _documentTypeRepository = documentTypeRepository;
            _mediator = mediator;
        }

        public virtual async Task<IList<DocumentType>> GetAll()
        {
            var query = from t in _documentTypeRepository.Table
                        orderby t.DisplayOrder
                        select t;
            return await query.ToListAsync();
        }
       
    }

这是我的测试方法GetAllDocumentTypes():

This my test method GetAllDocumentTypes():

[TestClass()]
    public class DocumentTypeServiceTests
    {
        private Mock<IRepository<DocumentType>> _documentTypeRepositoryMock;
        private DocumentTypeService _documentTypeService;
        private Mock<IMediator> _mediatorMock;
        private Mock<IMongoQueryable<DocumentType>> _mongoQueryableMock;
        private List<DocumentType> _expected;
        private IQueryable<DocumentType> _expectedQueryable; 

        [TestInitialize()]
        public void Init()
        {
            _mediatorMock = new Mock<IMediator>();
            _documentTypeRepositoryMock = new Mock<IRepository<DocumentType>>();
            _mongoQueryableMock = new Mock<IMongoQueryable<DocumentType>>();
            _expected =  new List<DocumentType>
            {
                new DocumentType() {Name = "name1", Description = "t1", DisplayOrder = 0},
                new DocumentType() {Name = "name2", Description = "t2", DisplayOrder = 1}
            };
            _expectedQueryable = _expected.AsQueryable();
            _mongoQueryableMock.Setup(x => x.ElementType).Returns(_expectedQueryable.ElementType);
            _mongoQueryableMock.Setup(x => x.Expression).Returns(_expectedQueryable.Expression);
            _mongoQueryableMock.Setup(x => x.Provider).Returns(_expectedQueryable.Provider);
            _mongoQueryableMock.Setup(x => x.GetEnumerator()).Returns(_expectedQueryable.GetEnumerator());
                                  
            _documentTypeRepositoryMock.Setup(x => x.Table).Returns(_mongoQueryableMock.Object);
            _documentTypeService = new DocumentTypeService(_documentTypeRepositoryMock.Object, _mediatorMock.Object);
        }

        
        [TestMethod()]
        public async Task GetAllDocumentTypes()
        {
            var actual = await _documentTypeService.GetAll();
            Assert.AreEqual(_expected.Count, actual.Count);
        }
    }

获取错误:

Message: 
    Test method Grand.Services.Tests.Documents.DocumentTypeServiceTests.GetAllDocumentTypes threw exception: 
    System.InvalidCastException: Unable to cast object of type 'System.Linq.EnumerableQuery`1[Grand.Domain.Documents.DocumentType]' to type 'MongoDB.Driver.Linq.IOrderedMongoQueryable`1[Grand.Domain.Documents.DocumentType]'.
  Stack Trace: 
    MongoQueryable.OrderBy[TSource,TKey](IMongoQueryable`1 source, Expression`1 keySelector)
    DocumentTypeService.GetAll() line 38
    DocumentTypeServiceTests.GetAllDocumentTypes() line 101
    ThreadOperations.ExecuteWithAbortSafety(Action action)

能否请您解释一下为什么类型不是IOrderedMongoQueryable以及如何解决此问题?谢谢

Could you please explain me why type is not IOrderedMongoQueryable and how solve this issue? Thank you

推荐答案

新(2020-09-15)

我尽可能地重现了您的努力.有两个问题.

I reproduced your effort as nearly as I could. There are two problems.

首先,您的 GetAll()方法包括一个 orderby ,这正是迫使 IMongoQueryable 成为 IOrderedMongoQueryable 的原因..但是, _mongoQueryableMock 不会返回 IOrderedMongoQueryable .而且,如果尝试用 IOrderedMongoQueryable 替换 _mongoQueryableMock IMongoQueryable ,也会失败.我没有找到一种方法来获取 _expectedQueryable 以允许 IOrderedQueryable .

First, your GetAll() method includes an orderby, which is what's forcing IMongoQueryable to become IOrderedMongoQueryable. However, _mongoQueryableMock doesn't return IOrderedMongoQueryable. And if you try replacing _mongoQueryableMock's IMongoQueryable with IOrderedMongoQueryable, that fails, too. I didn't find a way to get _expectedQueryable to allow an IOrderedQueryable.

第二,异步可能会给您带来麻烦.在没有异步的情况下,我可以将 GetAll()查询更改为类似的内容,从而在应用orderby之前先解析该查询:

Second, Async is probably going to give you trouble. Without Async, I can change the GetAll() query to something like this, which resolves the query before applying the orderby:

   public List<DocumentType> GetAll()
   {
       var query = from t in _documentTypeRepository.Table.ToList()
                   orderby t.DisplayOrder
                   select t;
       return query.ToList();
   }

但是,我没有找到一种管理ToListAsync的方法.

However, I didn't find a way to manage the ToListAsync.

总而言之,我回到了最初的建议.不要尝试模拟IMongoQueryable.将服务更改为接受MongoClient.无论哪种方式,您都在模拟Mongo,但是接受IMongoClient就是在直接使用它,而不是将其隐藏在另一个抽象后面.

All in all, I'm back to my original advice. Don't try to mock IMongoQueryable. Change the service to accept MongoClient. Either way you're mocking Mongo, but accepting IMongoClient you're using it directly instead of hiding it behind another abstraction.

        private readonly IMongoClient _mongoClient;
        public DocumentTypeService(IMongoClient mongoClient) {...}

原始(2020-09-14)

我不能完全确定,但是我相信这是因为 _documentTypeRepository 没有返回 IMongoQueryable .它返回一个 Table ,这将强制可查询的有序mongo.

I can't be completely sure, but I believe it's because _documentTypeRepository isn't returning IMongoQueryable. It's returning a Table, which will force an ordered mongo queryable.

为此,请使用我在此处_documentTypeRepository 或者需要返回IMongoQueryable 然后转换为 Table ,或者查看IQueryable是否可以转换为 IOrderedMongoQueryable .

For this to work using the mocking method described by me here, _documentTypeRepository would either need to return IMongoQueryable then convert to Table, or see if IQueryable can convert to IOrderedMongoQueryable.

简而言之,没有什么可以嘲笑 IOrderedMongoQueryable 的返回.

In short, nothing is mocking the return of IOrderedMongoQueryable.

老实说,所有这些都指向存储库与实现紧密耦合的情况.将 IMongoQueryable 强制为 IQueryable 是一种代码异味和反模式.您可以考虑通过以下两种方式之一重新考虑服务和/或存储库层:

Honestly, what this all points to is that the repository is probably too tightly coupled to the implementation. Forcing IMongoQueryable to IQueryable is a code smell and anti-pattern. You might consider rethinking your service and/or repository layer in one of two ways:

  1. 创建一个 _documentTypeRepository.GetAll()方法,将MongoDb结果映射到 DocumentType .或
  2. 请勿在服务中使用存储库模式.相反,注入IMongoClient,作为 GetAll 方法的一部分从Mongo返回数据,并在那里映射到DocumentType.
  1. Create an _documentTypeRepository.GetAll() method that maps the MongoDb results to DocumentType. OR
  2. Don't use a repository pattern in the service. Instead, inject the IMongoClient, return the data from Mongo as part of the GetAll method, and map to DocumentType there.

这篇关于使用IMongoQueryable进行单元测试.System.InvalidCastException:无法将类型为EnumerableQuery的对象转换为IOrderedMongoQueryable类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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