使用Sinon Mock仅校验MongoDB第二次updateOne调用的参数
updateOne Call with Sinon Got it, let's break down how to test just the second updateOne call's parameters when using Sinon for your saveAchProgress function. There are two straightforward approaches depending on whether you're using Sinon stubs or mocks.
Using Sinon Stubs (Flexible Parameter Inspection)
Stubs are great when you want to record all calls and inspect them after execution. Here's how to target the second call specifically:
const sinon = require('sinon'); const { expect } = require('chai'); const YourMongoModel = require('../models/yourModel'); // Import your MongoDB model const { saveAchProgress } = require('../services/yourService'); // Import your function describe('saveAchProgress', () => { it('should validate the second updateOne call parameters', async () => { // Create a stub for the updateOne method const updateOneStub = sinon.stub(YourMongoModel, 'updateOne'); // Mock a successful response for the database calls updateOneStub.resolves({ acknowledged: true, modifiedCount: 1 }); // Execute the function under test with your test parameters await saveAchProgress(/* Pass your test input here */); // First, confirm the function made exactly two calls (optional but good to verify) expect(updateOneStub.calledTwice).to.be.true; // Get the arguments from the SECOND call (index 1, since calls are zero-indexed) const secondCallArgs = updateOneStub.getCall(1).args; // Validate the query filter (first argument of updateOne) expect(secondCallArgs[0]).to.deep.equal({ // Your expected query object, e.g.: userId: 'test-user-123', achievementId: 'ach-456' }); // Validate the update operation (second argument of updateOne) expect(secondCallArgs[1]).to.deep.equal({ // Your expected update object, e.g.: $set: { progress: 100, completed: true } }); // Restore the stub to avoid affecting other tests updateOneStub.restore(); }); });
Key Notes:
updateOneStub.getCall(1)targets the second invocation (since the first call is at index 0).- The
.argsproperty returns an array of all arguments passed to that specificupdateOnecall. - Use
deep.equalto compare objects/arrays since regular equality checks won't work for nested structures.
Using Sinon Mocks (Predefined Expectations)
If you prefer setting up expectations before running the test, you can use Sinon mocks to define loose rules for the first call and strict rules for the second:
const sinon = require('sinon'); const { expect } = require('chai'); const YourMongoModel = require('../models/yourModel'); const { saveAchProgress } = require('../services/yourService'); describe('saveAchProgress', () => { it('should enforce the second updateOne call parameters', async () => { // Create a mock for your MongoDB model const modelMock = sinon.mock(YourMongoModel); // Define a loose expectation for the first updateOne call (ignore parameters) modelMock.expects('updateOne') .once() .withArgs(sinon.match.any, sinon.match.any); // Define a strict expectation for the second updateOne call modelMock.expects('updateOne') .once() .withArgs( // Expected query filter { userId: 'test-user-123', achievementId: 'ach-456' }, // Expected update operation { $set: { progress: 100, completed: true } } ); // Run the function under test await saveAchProgress(/* Test parameters */); // Verify all mock expectations were met modelMock.verify(); // Restore the mock modelMock.restore(); }); });
Key Notes:
sinon.match.anyallows the first call to pass regardless of its parameters, focusing only on the second call's validity.modelMock.verify()will throw an error if any expectations (like the second call's parameters) aren't satisfied.
Both approaches work well—stubs give you more flexibility to inspect calls after the fact, while mocks let you define rigid expectations upfront. Pick whichever fits your testing style better!
内容的提问来源于stack exchange,提问作者Florent Arlandis

