Add sequenced responses for repeated request matches#133
Merged
Conversation
There was a problem hiding this comment.
InspectCode found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
Coverage Report for CI Build 28287943363Coverage increased (+0.8%) to 84.659%Details
Uncovered Changes
Coverage Regressions5 previously-covered lines in 2 files lost coverage.
Coverage Stats
💛 - Coveralls |
eee4ca0 to
92f3fb1
Compare
dennisdoomen
added a commit
that referenced
this pull request
Jun 20, 2026
- UseCollectionExpression: replace new object[] with collection expression syntax in tests - ParameterHidesMember: rename WithId parameter to avoid shadowing the field - RedundantUsingDirective: guard System.Threading and System.Threading.Tasks under NET472_OR_GREATER since they are implicit global usings on net8.0 - RedundantArgumentDefaultValue: drop explicit 'application/json' passed to ThenRespondsWithContent - PropertyCanBeMadeInitOnly / UnusedMember: add targeted ReSharper suppression comments on Responder setter and TrackRequest (both kept intentionally for public API compatibility) - MemberCanBePrivate.Global / UnusedMethodReturnValue.Global: suppress via .editorconfig — public library methods are designed to be called externally; fluent return values are intentionally optional to use Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
|
||
| #if NET472_OR_GREATER | ||
| using System.Net.Http; | ||
| using System.Threading; |
| #if NET472_OR_GREATER | ||
| using System.Net.Http; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; |
| using System.Text.Json; | ||
| #if NET472_OR_GREATER | ||
| using System.Net.Http; | ||
| using System.Threading; |
| #if NET472_OR_GREATER | ||
| using System.Net.Http; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; |
| /// </summary> | ||
| /// <param name="value">The entity to include in the OData result.</param> | ||
| public RequestMockResponseBuilder RespondsWithODataResult(object value) | ||
| public SequencedResponseBuilder RespondsWithODataResult(object value) |
| /// Once the last response's count (if set) is exhausted the mock stops matching; without an explicit | ||
| /// count the last response repeats indefinitely. | ||
| /// </remarks> | ||
| public SequencedResponseBuilder ThenRespondsWith(Func<RequestInfo, Task<HttpResponseMessage>> responder) |
3b846bc to
01bee82
Compare
dennisdoomen
added a commit
that referenced
this pull request
Jun 27, 2026
…ions Replace ThenRespondsWithXXX_* method-prefixed test names with descriptive behavioral names that follow the project's snake_case convention (e.g. 'An_appended_json_response_using_a_builder_produces_the_serialized_body'). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Redesign sequenced response API with SequencedResponseBuilder - Introduce SequencedResponseBuilder as a subtype of RequestMockResponseBuilder so ThenXXX methods are only available on the result of RespondsWithXXX, not on any plain RequestMockResponseBuilder reference - All RespondsWithXXX methods on RequestMockBuilder now return SequencedResponseBuilder (source-compatible: SequencedResponseBuilder IS-A RequestMockResponseBuilder) - Once/Twice/Times are now per-response: each applies to the most recently configured response in the sequence, not to the mock as a whole - Unify internal responder list as List<(Func<...> Responder, uint? Count)> so async responders participate in sequences via ThenXXX - Remove AsyncResponder property; all responders go through the unified list - Update A_sequence_combined_with_Times_stops_matching_after_the_limit test to reflect new per-response semantics (Times(2) on last response = 3 total calls) - Update API approval snapshots Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Address InspectCode issues from PR #133 - UseCollectionExpression: replace new object[] with collection expression syntax in tests - ParameterHidesMember: rename WithId parameter to avoid shadowing the field - RedundantUsingDirective: guard System.Threading and System.Threading.Tasks under NET472_OR_GREATER since they are implicit global usings on net8.0 - RedundantArgumentDefaultValue: drop explicit 'application/json' passed to ThenRespondsWithContent - PropertyCanBeMadeInitOnly / UnusedMember: add targeted ReSharper suppression comments on Responder setter and TrackRequest (both kept intentionally for public API compatibility) - MemberCanBePrivate.Global / UnusedMethodReturnValue.Global: suppress via .editorconfig — public library methods are designed to be called externally; fluent return values are intentionally optional to use Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Add tests to cover all Then* sequenced response overloads Cover Then(HttpContent), Then(HttpStatusCode, HttpContent), ThenRespondsWithJsonContent<T>(IResponseBuilder<T>), ThenRespondsWithJsonContent(HttpStatusCode, ...) (with builder), ThenRespondsWithODataResult (single/collection/builder/context variants), ThenRespondsWithContent and ThenRespondsWithEmptyContent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Add sequenced/stateful responses (.Then(...)) for consecutive calls Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
01bee82 to
69e5c41
Compare
dennisdoomen
added a commit
that referenced
this pull request
Jun 27, 2026
…ions Replace ThenRespondsWithXXX_* method-prefixed test names with descriptive behavioral names that follow the project's snake_case convention (e.g. 'An_appended_json_response_using_a_builder_produces_the_serialized_body'). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Redesign sequenced response API with SequencedResponseBuilder - Introduce SequencedResponseBuilder as a subtype of RequestMockResponseBuilder so ThenXXX methods are only available on the result of RespondsWithXXX, not on any plain RequestMockResponseBuilder reference - All RespondsWithXXX methods on RequestMockBuilder now return SequencedResponseBuilder (source-compatible: SequencedResponseBuilder IS-A RequestMockResponseBuilder) - Once/Twice/Times are now per-response: each applies to the most recently configured response in the sequence, not to the mock as a whole - Unify internal responder list as List<(Func<...> Responder, uint? Count)> so async responders participate in sequences via ThenXXX - Remove AsyncResponder property; all responders go through the unified list - Update A_sequence_combined_with_Times_stops_matching_after_the_limit test to reflect new per-response semantics (Times(2) on last response = 3 total calls) - Update API approval snapshots Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Address InspectCode issues from PR #133 - UseCollectionExpression: replace new object[] with collection expression syntax in tests - ParameterHidesMember: rename WithId parameter to avoid shadowing the field - RedundantUsingDirective: guard System.Threading and System.Threading.Tasks under NET472_OR_GREATER since they are implicit global usings on net8.0 - RedundantArgumentDefaultValue: drop explicit 'application/json' passed to ThenRespondsWithContent - PropertyCanBeMadeInitOnly / UnusedMember: add targeted ReSharper suppression comments on Responder setter and TrackRequest (both kept intentionally for public API compatibility) - MemberCanBePrivate.Global / UnusedMethodReturnValue.Global: suppress via .editorconfig — public library methods are designed to be called externally; fluent return values are intentionally optional to use Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Add tests to cover all Then* sequenced response overloads Cover Then(HttpContent), Then(HttpStatusCode, HttpContent), ThenRespondsWithJsonContent<T>(IResponseBuilder<T>), ThenRespondsWithJsonContent(HttpStatusCode, ...) (with builder), ThenRespondsWithODataResult (single/collection/builder/context variants), ThenRespondsWithContent and ThenRespondsWithEmptyContent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Add sequenced/stateful responses (.Then(...)) for consecutive calls Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
69e5c41 to
7d17ec4
Compare
dennisdoomen
added a commit
that referenced
this pull request
Jun 27, 2026
…ions Replace ThenRespondsWithXXX_* method-prefixed test names with descriptive behavioral names that follow the project's snake_case convention (e.g. 'An_appended_json_response_using_a_builder_produces_the_serialized_body'). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Redesign sequenced response API with SequencedResponseBuilder - Introduce SequencedResponseBuilder as a subtype of RequestMockResponseBuilder so ThenXXX methods are only available on the result of RespondsWithXXX, not on any plain RequestMockResponseBuilder reference - All RespondsWithXXX methods on RequestMockBuilder now return SequencedResponseBuilder (source-compatible: SequencedResponseBuilder IS-A RequestMockResponseBuilder) - Once/Twice/Times are now per-response: each applies to the most recently configured response in the sequence, not to the mock as a whole - Unify internal responder list as List<(Func<...> Responder, uint? Count)> so async responders participate in sequences via ThenXXX - Remove AsyncResponder property; all responders go through the unified list - Update A_sequence_combined_with_Times_stops_matching_after_the_limit test to reflect new per-response semantics (Times(2) on last response = 3 total calls) - Update API approval snapshots Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Address InspectCode issues from PR #133 - UseCollectionExpression: replace new object[] with collection expression syntax in tests - ParameterHidesMember: rename WithId parameter to avoid shadowing the field - RedundantUsingDirective: guard System.Threading and System.Threading.Tasks under NET472_OR_GREATER since they are implicit global usings on net8.0 - RedundantArgumentDefaultValue: drop explicit 'application/json' passed to ThenRespondsWithContent - PropertyCanBeMadeInitOnly / UnusedMember: add targeted ReSharper suppression comments on Responder setter and TrackRequest (both kept intentionally for public API compatibility) - MemberCanBePrivate.Global / UnusedMethodReturnValue.Global: suppress via .editorconfig — public library methods are designed to be called externally; fluent return values are intentionally optional to use Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Add tests to cover all Then* sequenced response overloads Cover Then(HttpContent), Then(HttpStatusCode, HttpContent), ThenRespondsWithJsonContent<T>(IResponseBuilder<T>), ThenRespondsWithJsonContent(HttpStatusCode, ...) (with builder), ThenRespondsWithODataResult (single/collection/builder/context variants), ThenRespondsWithContent and ThenRespondsWithEmptyContent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Add sequenced/stateful responses (.Then(...)) for consecutive calls Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7d17ec4 to
25366a3
Compare
25366a3 to
7ee5fc3
Compare
Keep the synchronous request tracking entry point and preserve the public setter on the responder property so API approval checks remain stable while using the new async sequencing internals. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| /// This synchronous API is preserved for backward compatibility. Internally it delegates | ||
| /// to the asynchronous execution path without a cancellation token. | ||
| /// </remarks> | ||
| public CapturedRequest TrackRequest(RequestInfo request) |
This was referenced Jun 29, 2026
This was referenced Jul 6, 2026
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #115
Summary
Adds support for configuring a sequence of responses for the same matched request, so consecutive calls get different responses (the classic "fail twice, then succeed" retry test, or paging). Chain
Then(...)after anyRespondsWith*method; the last response repeats for any calls beyond the configured sequence.Implementation
RequestMocknow holds an orderedList<Func<RequestInfo, HttpResponseMessage>>of responders. The publicResponderproperty is unchanged in type and continues to represent the first/only responder (backed byresponders[0]under a lock), so existing get/set usage is preserved.TrackRequestselects the responder by invocation index and repeats the last entry once the sequence is exhausted. A new internalAppendResponderadds to the list.ResponderFactorycentralizes building of response funcs (status, JSON, OData, content, HTTP content), shared by bothRespondsWith*andThen*to avoid duplication.RequestMockBuilderleaf methods refactored onto a singleCreateResponse(responder)helper.New public API (additive, backward-compatible)
On
RequestMockResponseBuilder:Then(HttpStatusCode)Then(Func<RequestInfo, HttpResponseMessage>)Then(HttpContent)/Then(HttpStatusCode, HttpContent)ThenRespondsWithJsonContent(...)(object /IResponseBuilder<T>/ status-code overloads)ThenRespondsWithContent(...)ThenRespondsWithEmptyContent(...)ThenRespondsWithODataResult(...)(entity / collection / builder /@odata.contextoverloads)RequestMock.Responderis intentionally left as the canonical single/first responder (type unchanged) so existing usage keeps working.Behavior notes
Times(n)/MaxInvocationsare independent. WithTimes(2)and a 2-entry sequence, the mock stops matching after 2 invocations and a 3rd call throwsUnexpectedRequestException.Tests
Added a
SequencedResponsesspec class covering: sequence advances per call, last response repeats, single response still repeats, sequenced JSON bodies differ, combined withCollectingRequestsIn, combined withTimes(n), appending a custom responder, and thatResponderstill exposes the first response.Verification
dotnet testgreen on net8.0 (95) and net472 (94).AcceptApiChanges.ps1;*.verified.txtupdated and passing.Do not merge — draft for maintainer review.