-
Notifications
You must be signed in to change notification settings - Fork 291
Fix SynchronizationContext which is set in TestInitialize not preserved in test method #7202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Copilot
wants to merge
9
commits into
main
Choose a base branch
from
copilot/fix-synchronizationcontext-in-tests
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+259
−18
Conversation
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
Copilot
AI
changed the title
[WIP] Fix regression with custom SynchronizationContext in tests
Add acceptance test for SynchronizationContext preservation in TestInitialize
Jan 12, 2026
Youssef1313
reviewed
Jan 12, 2026
| } | ||
| """; | ||
|
|
||
| private const string SynchronizationContextSourceCode = """ |
Member
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot Move that a new test class for clarity
Youssef1313
approved these changes
Jan 12, 2026
…itialize Co-authored-by: Youssef1313 <[email protected]>
Co-authored-by: Youssef1313 <[email protected]>
91dfa20 to
faa1c24
Compare
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.
MSTest v4 resets custom
SynchronizationContextset inTestInitializeto null before test method execution, breaking a 10+ year pattern used for testing async UI code (MAUI, WPF, WinForms).Changes
SynchronizationContext_WhenSetInTestInitialize_IsPreservedInTestMethodin newSynchronizationContextTests.csfileUnitTestSynchronizationContext[TestInitialize], verifies same instance in[TestMethod]Reproduction
Root cause:
AsyncTaskMethodBuilderrestores previousSynchronizationContextwhen async fixture methods complete synchronously.Original prompt
This section details on the original issue you should resolve
<issue_title>Regression: Custom SynchronizationContext set in TestInitialize is removed</issue_title>
<issue_description>## Describe the bug
It's common to use a custom
SynchronizationContext(e.g. UnitTestSynchronizationContext) for unit testing of client applications (e.g. MAUI, WPF, WinForms). This allows to simulate the same async behavior in the unit test environment as we have in the application.We initialize the custom
SynchronizationContextin aTestInitializemethod and dispose it inTestCleanup. This worked fine for more than 10 years (even with legacy MSTest v1).Starting with MSTest v4 this approach does not work anymore. The
SynchronizationContext.Currentis nownullin the test method.Steps To Reproduce
UnitTest.MSTest: Uses MSTest 4.0.2. This one fails because the SynchonizationContext is set to null before calling the test method.UnitTestOld.MSTest: Uses MSTest 3.9.3. This one passes as it works as expected.UnitTest.XUnit: Uses the latest Xunit v3 with the same approach. This one passes as it works as expected.</issue_description>
<agent_instructions>Add an acceptance test for this scenario where a TestInitialize sets its own synchronization context, and expect that the synchronization context remains the same when executing the test method.</agent_instructions>
Comments on the Issue (you are @copilot in this section)
@Youssef1313 This is most likely because we moved away from blocking threads using `GetAwaiter().GetResult()` in multiple places, to be fully async instead. In that case we are controlled by `AsyncTaskMethodBuilder`. I would guess the previous synchronization context gets restored here:https://github.com/dotnet/runtime/blob/8fcb70a35c893ad7620e0d6ac4f7ea812af0dca6/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderCore.cs#L42-L47
There are three potential fixes I can think of:
Detect that the fixture we are calling into isn't awaitable beforehand, and go into a fully synchronous code path instead of calling into an async method that will complete synchronously anyways. Avoiding the async method will avoid the state machine completely and will make sure the synchronization context is preserved.
AsyncMethodBuilderAttribute) that doesn't reset the sync context? In that case, our own async calls will reset it.Manually capture the SynchronizationContext exactly after we call the fixture, and restore it back just before we call the test method.
Use a custom async method builder ourselves in our async methods for executing fixtures. Something like the following:
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.