Moq 设置没有返回正确的值

Moq setup is not returning correct value

我查看了其他答案,但尚未找到任何有用的信息。也许我误会了。 GetDocumentCount 模拟不工作。

Class 测试中:

public class DeleteDocument
{
    private readonly IMongoClient _mongoClient;

    public DeleteDocument(IMongoClient mongoClient)
    {
        this._mongoClient = mongoClient;
    }

    public override bool ProcessRequest(DeleteDocumentRequest request)
    {
        var docObjectIds = request.Ids.Select(ObjectId.Parse).ToArray();
        var documents = _mongoClient.GetDocumentsCollection(Constants.DATABASE);
        var documentCount = GetDocumentCount(documents, docObjectIds);
        
        if (documentCount != request.Ids.Length)
        {
            throw new ValidationException(-6, "The document count doesn't match");
        }
        
        ... more code here
        return true;
    }

    public virtual long GetDocumentCount(IMongoCollection<Document> docs, ObjectId[] objIds)
    {
        return docs.Find(x => objIds.Contains(x.Id)).CountDocuments();
    }
}

单元测试是遗留代码,无法正常工作,我很难理解它是否真的在正确模拟 MongoDb。我还没有找到很多 MongoDb 模拟的好例子。这是完整的单元测试:

[TestMethod]
public void DeleteDocument_DocumentCountDoesNotMatch_ReturnsErrorCode()
{
    var mockIMongoCollection = new Mock<IMongoCollection<Document>>();
    var asyncCursor = new Mock<IAsyncCursor<Document>>();

    var docCollection = new[]
    {
        // this is the document to delete
        new Document
        {
            CreateDate = DateTime.UtcNow,
            Id = new ObjectId("6074d4da433ab6b5a731b82c"),
            IsActive = true
        }
    };

    mockIMongoCollection.Setup(collection => collection.FindSync(
            Builders<Document>.Filter.Empty,
            It.IsAny<FindOptions<Document>>(),
            default))
        .Returns(asyncCursor.Object);
    
    asyncCursor.SetupSequence(async => async.MoveNext(default))
        .Returns(true)
        .Returns(false);

    asyncCursor.SetupGet(async => async.Current)
        .Returns(docCollection);

    var mongoDatabaseMock = new Mock<IMongoDatabase>();
    mongoDatabaseMock.Setup(x => x.GetCollection<Document>(
            It.IsAny<string>(),
            It.IsAny<MongoCollectionSettings>()))
        .Returns(mockIMongoCollection.Object);

    var mongoClientMock = new Mock<IMongoClient>();
    mongoClientMock.Setup(x => x.GetDatabase(
            It.IsAny<string>(),
            It.IsAny<MongoDatabaseSettings>()))
        .Returns(mongoDatabaseMock.Object);

    var mockClass = new Mock<DeleteDocument>(mongoClientMock.Object) 
        { CallBase = true };
    mockClass.Setup(
            x => x.GetDocumentCount
            (It.IsAny<IMongoCollection<Document>>(),
                It.IsAny<ObjectId[]>()))
        .Returns(Convert.ToInt64(1));

    var ex = Assert.ThrowsException<ValidationException>(() => 
        new DeleteDocument(mongoClientMock.Object)
        .ProcessRequest(new DeleteDocumentRequest(null)
        {
            Ids = new[] { "6074d4da433ab6b5a731b82c" }
        }));

    Assert.AreEqual(-6, ex.Code);
}

看来您创建了被测对象的模拟

//...

var mockClass = new Mock<DeleteDocument>(mongoClientMock.Object) { //<-- mocked instance
    CallBase = true 
};

//...

然后在进行测试时继续使用被测对象的另一个单独实例

//...

var ex = Assert.ThrowsException<ValidationException>(() => 
    new DeleteDocument(mongoClientMock.Object) //<-- new instance here
    .ProcessRequest(new DeleteDocumentRequest(null)
    {
        Ids = new[] { "6074d4da433ab6b5a731b82c" }
    }));

//...

由于您还想覆盖 GetDocumentCount 的行为,因此在这种边缘情况下的模拟是在执行测试时需要使用的

[TestMethod]
public void DeleteDocument_DocumentCountDoesNotMatch_ReturnsErrorCode() {
    //Arrange

    //...omitted for brevity

    var mockClass = new Mock<DeleteDocument>(mongoClientMock.Object) {
        CallBase = true 
    };
    mockClass
        .Setup(x => x.GetDocumentCount(It.IsAny<IMongoCollection<Document>>(), It.IsAny<ObjectId[]>()))
        .Returns(Convert.ToInt64(99)); //<-- this need to not match request

    var request = new DeleteDocumentRequest(null) {
            Ids = new[] { "6074d4da433ab6b5a731b82c" }
    }

    //Act & 
    Action act = () => mockClass.Object.ProcessRequest(request);        

    //Assert
    ValidationException ex = Assert.ThrowsException<ValidationException>(act);
    Assert.AreEqual(-6, ex.Code);
}