.net - 单元测试 MongoDB.Driver dotnet core
问题描述
我们正在使用命令/查询模式,其中实现对 MongoDB 如何工作有详细的了解,我们想为此编写一个测试。IMongoCollection<CarDocument>
在确保发送正确的过滤器的同时模拟 MongoDbFind
是非常具有挑战性的。我们正在使用 .NET core 2.1 和 MongoDB.Driver v2.7.2
using MongoDB.Driver;
namespace Example
{
public class SomeMongoThing : ISomeMongoThing
{
public IMongoCollection<CarDocument> GetCars()
{
var client = new MongoClient("ConnectionString");
var database = client.GetDatabase("DatabaseName");
return database.GetCollection<CarDocument>("CollectionName");
}
}
public interface ISomeMongoThing
{
IMongoCollection<CarDocument> GetCars();
}
public class GetCarQuery
{
private readonly ISomeMongoThing someMongoThing;
public GetCarQuery(ISomeMongoThing someMongoThing)
{
this.someMongoThing = someMongoThing;
}
public CarDocument Query(string aKey)
{
var schedules = someMongoThing.GetCars();
var match = schedules.Find(x => x.AKey == aKey);
return match.Any() ? match.First() : this.GetDefaultCar(schedules);
}
private CarDocument GetDefaultCar(IMongoCollection<CarDocument> schedules)
{
return schedules.Find(x => x.AKey == "Default").First();
}
}
}
我们在这里有一个测试,但我们无法编写一个测试来检查是否使用了正确aKey
的过滤器,这意味着如果我们x => x.AKey == "hello"
在实现中使用过滤器,测试应该会失败。即使代码通过.Find(x => true)
了测试。
using System.Collections.Generic;
using System.Threading;
using MongoDB.Driver;
using Moq;
using NUnit.Framework;
namespace Example
{
public class GetCarQueryTest
{
[Test]
public void ShouldGetByApiKey()
{
var mockCarDocument = new CarDocument();
var aKey = "a-key";
var result = Mock.Of<IAsyncCursor<CarDocument>>(x =>
x.MoveNext(It.IsAny<CancellationToken>()) == true
&& x.Current == new List<CarDocument>() { mockCarDocument });
var cars = Mock.Of<IMongoCollection<CarDocument>>(x => x.FindSync(
It.IsAny<FilterDefinition<CarDocument>>(),
It.IsAny<FindOptions<CarDocument, CarDocument>>(),
It.IsAny<CancellationToken>()) == result);
var someMongoThing = Mock.Of<ISomeMongoThing>(x => x.GetCars()() == cars);
var getCarQuery = new GetCarQuery(someMongoThing);
var car = getCarQuery.Query(aKey);
car.Should().Be(mockCarDocument);
}
}
}
您将如何测试提供的代码?如果在事物之间进行抽象SomeMongoThing
并GetCarQuery
有所帮助,我们愿意接受建议。这个想法是查询了解 MongoDb 以便能够利用 MongoDb 客户端的功能,并且查询的用户不必关心。
解决方案
在我看来,这似乎是作为XY 问题一部分的泄漏抽象。
来自评论
无论您如何抽象代码的某些部分来处理 MongoCollection,您如何测试该类?
我不会测试那个课程。最终包装集合的类不需要进行单元测试,因为它只是第 3 方关注点的包装器。MongoCollection 的开发人员会测试他们的代码以供发布。我将整个 mongo 依赖视为第 3 方实施问题。
看看下面的替代设计
public interface ICarRepository {
IEnumerable<CarDocument> GetCars(Expression<Func<CarDocument, bool>> filter = null);
}
public class CarRepository : ICarRepository {
private readonly IMongoDatabase database;
public CarRepository(Options options) {
var client = new MongoClient(options.ConnectionString);
database = client.GetDatabase(options.DatabaseName);
}
public IEnumerable<CarDocument> GetCars(Expression<Func<CarDocument, bool>> filter = null) {
IMongoCollection<CarDocument> cars = database.GetCollection<CarDocument>(options.CollectionName);
return filter == null ? cars.AsQueryable() : (IEnumerable<CarDocument>)cars.Find(filter).ToList();
}
}
为简单起见,我重命名了一些依赖项。它应该是不言自明的。所有与 Mongo 相关的关注点都封装在它自己的关注点中。存储库可以根据需要利用 MongoDb 客户端的所有功能,而不会泄露对第 3 方关注点的不必要依赖。
可以相应地重构依赖查询类
public class GetCarQuery {
private readonly ICarRepository repository;
public GetCarQuery(ICarRepository repository) {
this.repository = repository;
}
public CarDocument Query(string aKey) {
var match = repository.GetCars(x => x.AKey == aKey);
return match.Any()
? match.First()
: repository.GetCars(x => x.AKey == "Default").FirstOrDefault();
}
}
现在可以在一个孤立的单元测试中简单地模拟上述类的快乐路径
public class GetCarQueryTest {
[Test]
public void ShouldGetByApiKey() {
//Arrange
var aKey = "a-key";
var mockCarDocument = new CarDocument() {
AKey = aKey
};
var data = new List<CarDocument>() { mockCarDocument };
var repository = new Mock<ICarRepository>();
repository.Setup(_ => _.GetCars(It.IsAny<Expression<Func<CarDocument, bool>>>()))
.Returns((Expression<Func<CarDocument, bool>> filter) => {
return filter == null ? data : data.Where(filter.Compile());
});
var getCarQuery = new GetCarQuery(repository.Object);
//Act
var car = getCarQuery.Query(aKey);
//Assert
car.Should().Be(mockCarDocument);
}
}
测试与 Mongo 相关的实际问题需要进行集成测试,您将在其中连接到实际源以确保预期行为。
推荐阅读
- python-3.x - TypeError:“列表”对象不是迭代器 - Tensorflow 自定义指标回调
- r - arsenal::tableby 和 R Notebook:错误地添加了一行 NA
- c++ - 将安装添加到 QT pro 文件
- linux - pthread_self 返回一个大整数或 0,具体取决于 libpthread 是否存在
- angular - 角形式组。单选按钮未激活
- go - 内存使用量不断增加
- vue.js - Vue:在同一组件的两个实例之间切换不会更新视图
- numpy - 如何在特定轴上做 np.dot
- persistent-storage - mbed 持久闪存
- go - 在 Go 中为整个模块创建共享对象文件