c# - 使用 IMongoQueryable 进行单元测试。System.InvalidCastException:无法将 EnumerableQuery 类型的对象转换为 IOrderedMongoQueryable 类型
问题描述
我使用 GetAll() 方法跟踪服务,并编写了测试该方法的测试。
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():
[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)
您能否解释一下为什么 type 不是 IOrderedMongoQueryable 以及如何解决这个问题?谢谢
解决方案
新 (2020-09-15)
我尽可能地复制了你的努力。有两个问题。
首先,您的GetAll()
方法包括一个orderby
,这是强制IMongoQueryable
成为IOrderedMongoQueryable
。但是,_mongoQueryableMock
不返回IOrderedMongoQueryable
。如果您尝试将_mongoQueryableMock
's IMongoQueryable
替换为IOrderedMongoQueryable
,那也会失败。我没有找到一种方法来_expectedQueryable
允许IOrderedQueryable
.
其次,异步可能会给你带来麻烦。如果没有异步,我可以将GetAll()
查询更改为这样的内容,这会在应用 orderby 之前解析查询:
public List<DocumentType> GetAll()
{
var query = from t in _documentTypeRepository.Table.ToList()
orderby t.DisplayOrder
select t;
return query.ToList();
}
但是,我没有找到管理 ToListAsync 的方法。
总而言之,我回到了我最初的建议。不要试图模拟 IMongoQueryable。更改服务以接受 MongoClient。无论哪种方式,您都在嘲笑 Mongo,但接受 IMongoClient 您是在直接使用它,而不是将其隐藏在另一个抽象后面。
private readonly IMongoClient _mongoClient;
public DocumentTypeService(IMongoClient mongoClient) {...}
原创 (2020-09-14)
我不能完全确定,但我相信这是因为_documentTypeRepository
没有返回IMongoQueryable
。它返回 a Table
,这将强制一个有序的 mongo 可查询。
为此,使用我在这里描述的模拟方法工作,_documentTypeRepository
需要返回 IMongoQueryable然后转换为Table
,或者查看 IQueryable 是否可以转换为IOrderedMongoQueryable
。
简而言之,没有什么可以嘲笑IOrderedMongoQueryable
.
老实说,这一切都表明存储库可能与实现的耦合过于紧密。强制IMongoQueryable
是IQueryable
代码异味和反模式。您可以考虑通过以下两种方式之一重新考虑您的服务和/或存储库层:
- 创建一个
_documentTypeRepository.GetAll()
将 MongoDb 结果映射到DocumentType
. 或者 - 不要在服务中使用存储库模式。相反,注入 IMongoClient,从 Mongo 返回数据作为
GetAll
方法的一部分,并映射到那里的 DocumentType。
推荐阅读
- flutter - 如何使用 Flutter 绘制波浪线
- r - R. 从 df A 中获取第一行与 df B 的元素匹配的列
- python - 如何描述主题kafka的配置?
- list - 如何从飞镖颤动中的键或键值对获取索引
- amazon-web-services - 将 CloudFormation 参数传递给 AppSync 解析器
- javascript - 正则表达式替换括号中的文本
- android - 在 Android 上使用 insets 填充 WebView 中的内容
- c - 文件处理问题,在c中自动生成其他类型的数据
- drools - 使用 InsertLogical 和累积之间的区别
- routes - OSRM 流量预测