From d637bfcc4dabef9da5435a41353b56bfb3fa4567 Mon Sep 17 00:00:00 2001 From: Emma Hall Date: Tue, 21 Jul 2026 12:13:53 +0100 Subject: [PATCH 1/3] Add failing test for MockSequence not verifying all setups were called MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reproduces #1683 — VerifyAll() does not throw when a Setup registered via InSequence was never invoked. Test currently fails as expected; fix to follow in a subsequent commit. --- src/Moq.Tests/MockSequenceFixture.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Moq.Tests/MockSequenceFixture.cs b/src/Moq.Tests/MockSequenceFixture.cs index eab722405..97efe61c6 100644 --- a/src/Moq.Tests/MockSequenceFixture.cs +++ b/src/Moq.Tests/MockSequenceFixture.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors. // All rights reserved. Licensed under the BSD 3-Clause License; see License.txt. +using System; +using System.Collections.Generic; using Xunit; namespace Moq.Tests @@ -114,6 +116,22 @@ public void SameMockInvalidSequenceFail() Assert.Throws(() => a.Object.Do(200)); } + + [Fact] + public void SameMockNotAllSequenceCalledFail() + { + var a = new Mock(MockBehavior.Strict); + + var sequence = new MockSequence(); + a.InSequence(sequence).Setup(x => x.Do(100)).Returns(101); + a.InSequence(sequence).Setup(x => x.Do(200)).Returns(201); + a.InSequence(sequence).Setup(x => x.Do(300)).Returns(301); + + a.Object.Do(100); + a.Object.Do(200); + + Assert.Throws(() => a.VerifyAll()); + } public interface IFoo { From 389b0ed8c1f5dd5129a3a37744d0ec97d664478e Mon Sep 17 00:00:00 2001 From: Emma Hall Date: Tue, 21 Jul 2026 14:42:03 +0100 Subject: [PATCH 2/3] Fix: #1683 MockSequence doesn't ensure all calls in sequence were executed --- src/Moq/Condition.cs | 5 ++++- src/Moq/Mock.cs | 2 +- src/Moq/MockSequence.cs | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Moq/Condition.cs b/src/Moq/Condition.cs index c245974a8..036d254f2 100644 --- a/src/Moq/Condition.cs +++ b/src/Moq/Condition.cs @@ -10,14 +10,17 @@ sealed class Condition Func condition; Action? success; - public Condition(Func condition, Action? success = null) + public Condition(Func condition, Action? success = null, bool sequence = false) { this.condition = condition; this.success = success; + this.sequence = sequence; } public bool IsTrue => this.condition.Invoke(); + public bool sequence { get; } + public void SetupEvaluatedSuccessfully() => this.success?.Invoke(); } } diff --git a/src/Moq/Mock.cs b/src/Moq/Mock.cs index b178f0475..9448cdb63 100644 --- a/src/Moq/Mock.cs +++ b/src/Moq/Mock.cs @@ -293,7 +293,7 @@ internal void Verify(Func predicate, HashSet verifiedMocks) var errors = new List(); - foreach (var setup in this.MutableSetups.FindAll(setup => !setup.IsConditional && predicate(setup))) + foreach (var setup in this.MutableSetups.FindAll(setup => (!setup.IsConditional || setup.Condition!.sequence) && predicate(setup))) { try { diff --git a/src/Moq/MockSequence.cs b/src/Moq/MockSequence.cs index b26ca5eae..d83605320 100644 --- a/src/Moq/MockSequence.cs +++ b/src/Moq/MockSequence.cs @@ -44,7 +44,8 @@ internal ISetupConditionResult For(Mock mock) return new WhenPhrase(mock, new Condition( condition: () => expectationPosition == sequenceStep, - success: NextStep)); + success: NextStep, + sequence: true)); } } From 999ee9634b2abd889a699dceb43ffe7ff4c8e59e Mon Sep 17 00:00:00 2001 From: Emma Hall Date: Tue, 21 Jul 2026 15:14:15 +0100 Subject: [PATCH 3/3] changelog entry --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index b9c73981b..ea1016e80 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,8 @@ - FileLoadException for 'System.Threading.Tasks.Extensions, Version=4.2.0.1' (or similar) at `Moq.Async.AwaitableFactory..cctor()` on .NET Framework 4.6.2+ no longer occurs when updating Moq and other dependencies bring in newer versions of the package. Common non-ValueTask usage succeeds without binding redirects. The initialization now only eagerly registers Task providers and uses runtime FullName checks + deferred creation for ValueTask support. [#1648](https://github.com/devlooped/moq/issues/1648) +- `Verify`/`VerifyAll` now correctly fail if a `MockSequence` step was set up but never invoked. Previously, sequence setups were silently excluded from verification (the same exemption used for `mock.When(...)` conditions), so a test could omit calls to later sequence steps and still pass `VerifyAll()`. Note: this may turn previously-passing tests that rely on this gap into failing ones — that's the fix working as intended. [#1683](https://github.com/devlooped/moq/issues/1683) + ## [v4.20.72](https://github.com/devlooped/moq/tree/v4.20.72) (2024-09-07) [Full Changelog](https://github.com/devlooped/moq/compare/v4.20.71...v4.20.72)