问题描述
我使用GetAll()方法跟踪Service,并编写了测试此方法的测试。
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)
能否请您解释一下为什么类型不是IOrderedMongoQueryable以及如何解决此问题?谢谢
解决方法
新(2020-09-15)
我尽可能地重现了您的努力。有两个问题。
首先,您的const v = [
[2,'apple'],[3,[4,[5,'banana'],[6,[7,'orange'],[8,[9,[10,]
const reduced = v.reduce((ac,[num,fruit]) => ({
...ac,[fruit]: [...(ac[fruit] || []),Number(num)]
}),{})
const mapped = Object.entries(reduced).map(([fruit,v]) => ({
value: fruit,end: Math.max(...v),start: Math.min(...v)
}))
console.log('mapped',mapped)
方法包含一个GetAll()
,这正是迫使orderby
成为IMongoQueryable
的原因。但是,IOrderedMongoQueryable
不会返回_mongoQueryableMock
。而且,如果您尝试将IOrderedMongoQueryable
的{{1}}替换为_mongoQueryableMock
,那也会失败。我没有找到让IMongoQueryable
允许IOrderedMongoQueryable
的方法。
第二,异步可能会给您带来麻烦。在没有异步的情况下,我可以将_expectedQueryable
查询更改为如下所示,这样可以在应用orderby之前解析查询:
IOrderedQueryable
但是,我没有找到一种管理ToListAsync的方法。
总而言之,我回到了最初的建议。不要尝试模拟IMongoQueryable。更改服务以接受MongoClient。无论哪种方式,您都在模拟Mongo,但是接受IMongoClient则是在直接使用它,而不是将其隐藏在另一个抽象后面。
GetAll()
原始(2020-09-14)
我不能完全确定,但是我相信是因为 public List<DocumentType> GetAll()
{
var query = from t in _documentTypeRepository.Table.ToList()
orderby t.DisplayOrder
select t;
return query.ToList();
}
没有返回 private readonly IMongoClient _mongoClient;
public DocumentTypeService(IMongoClient mongoClient) {...}
。它返回一个_documentTypeRepository
,这将迫使有序mongo可查询。
要使用我here描述的模拟方法来工作,IMongoQueryable
要么需要返回IMongoQueryable then 转换为Table
,要么IQueryable可以转换为_documentTypeRepository
。
简而言之,Table
的返回无可厚非。
老实说,所有这些都指向该存储库可能与实现紧密耦合。将IOrderedMongoQueryable
强制为IOrderedMongoQueryable
是一种代码异味和反模式。您可以考虑通过以下两种方式之一重新考虑服务和/或存储库层:
- 创建一个
IMongoQueryable
方法,将MongoDb结果映射到IQueryable
。或 - 请勿在服务中使用存储库模式。相反,请注入IMongoClient,作为
_documentTypeRepository.GetAll()
方法的一部分从Mongo返回数据,并在那里映射到DocumentType。