我们使用的是Command / Query模式,其中的实现具有有关MongoDB如何工作的详细知识,我们希望为此编写测试。在模拟MongoDb <bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl" lazy-init="true">
**<property name="maxAuthenticationAge" value="86400"/>**
<property name="responseSkew" value="600"/> <!-- 10 minutes -->
<property name="maxAssertionTime" value="6000"/>
</bean>
<bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint" lazy-init="true">
<property name="defaultProfileOptions">
<bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
<property name="includeScoping" value="false"/> <!-- Default: true. When true request will include Scoping element. -->
**<property name="forceAuthN" value="true"/>** <!-- Default: false. When true IDP is required to re-authenticate user and not rely on previous authentication events -->
<property name="passive" value="false"/> <!-- Default: false. Sets whether the IdP should refrain from interacting with the user during the authentication process. -->
</bean>
</property>
的同时确保发送正确的IMongoCollection<CarDocument>
过滤器非常具有挑战性。我们正在使用.NET core 2.1和MongoDB.Driver v2.7.2
Find
我们在这里有一个测试,但是我们无法编写测试以检查是否使用了正确的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
之间进行抽象有帮助,我们欢迎您提出建议。想法是查询具有有关MongoDb的知识,以便能够利用MongoDb客户端的功能,并且查询的用户不必关心。
答案 0 :(得分:0)
在我看来,这是XY problem中一部分的泄漏抽象。
来自评论
无论您如何抽象代码的一部分来处理MongoCollection,如何测试该类?
我不会测试那堂课。最终包装集合的类不需要进行单元测试,因为它只是第三者关注的包装器。 MongoCollection的开发人员将测试其代码的发布。我认为整个mongo依赖关系都是第三方实现的关注点。
看看下面的替代设计
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客户端的所有功能,而不会泄漏对第三方的担忧。
依赖查询类可以相应地重构
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相关的实际问题需要进行集成测试,在该测试中,您将连接到实际资源以确保预期的行为。