-
-
Notifications
You must be signed in to change notification settings - Fork 108
Description
Hi there,
after #3462 had been closed yesterday, I updated my project from TUnit version 0.67.19 to 0.75.0. I discovered two more problems, which I - again - hope are actual errors and not changes that I simply missed in the release changelogs.
1. Prior to version 0.70.0 a test like this using an AAA syntax (Arrange, Act, Assert) had a green test result:
// using System.Windows;
// using Microsoft.Web.WebView2.Wpf;
public const string s_STAThreadExecuted = "STAThreadExecuted";
public class WebViewController
{
private readonly WebView2 _webView;
public WebViewController(WebView2 webView)
{
ArgumentNullException.ThrowIfNull(webView);
_webView = webView;
_webView.SetCurrentValue(FrameworkElement.UseLayoutRoundingProperty, true);
}
}
[TestExecutor<STAThreadExecutor>]
[NotInParallel(s_STAThreadExecuted)]
public class WebViewControllerTests
{
[Test]
public async Task ConstructorCallWithValidWebView_DoesNotThrowException()
{
var webView = new WebView2();
void constructorCall() => new WebViewController(webView);
await Assert.That(constructorCall).ThrowsNothing();
}
}Now the result of the exact same test is red due to an InvalidOperationException being thrown during the assertion:
ConstructorCallWithValidWebView_DoesNotThrowException
Source: Tests.cs line 41
Duration: 7 ms
Message:
Expected to not throw any exception
but threw System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
at Assert.That(constructorCall).ThrowsNothing()
Stack Trace:
Assertion`1.ExecuteCoreAsync()
Assertion`1.AssertAsync()
WebViewControllerTests.ConstructorCallWithValidWebView_DoesNotThrowException() line 48
<<GetTestsAsync>b__0_2>d.MoveNext() line 75
--- End of stack trace from previous location ---
<<get_CreateExecutableTestFactory>b__2>d.MoveNext()
--- End of stack trace from previous location ---
ExecutableTest.InvokeTestAsync(Object instance, CancellationToken cancellationToken)
<<ExecuteTestAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
<<ExecuteAsyncActionWithMessagePump>b__0>d.MoveNext()
--- End of stack trace from previous location ---
DedicatedThreadExecutor.ExecuteAsync(Func`1 action)
TestExecutor.ExecuteTestAsync(AbstractExecutableTest executableTest, CancellationToken cancellationToken)
TimeoutHelper.ExecuteWithTimeoutAsync(Func`2 taskFactory, Nullable`1 timeout, CancellationToken cancellationToken, String timeoutMessage)
TestExecutor.ExecuteAsync(AbstractExecutableTest executableTest, CancellationToken cancellationToken)
TestExecutor.ExecuteAsync(AbstractExecutableTest executableTest, CancellationToken cancellationToken)
TestExecutor.ExecuteAsync(AbstractExecutableTest executableTest, CancellationToken cancellationToken)
<<ExecuteTestInternalAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
<<ExecuteTestInternalAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
RetryHelper.ExecuteWithRetry(TestContext testContext, Func`1 action)
RetryHelper.ExecuteWithRetry(TestContext testContext, Func`1 action)
TestCoordinator.ExecuteTestInternalAsync(AbstractExecutableTest test, CancellationToken cancellationToken)
It seems like the assertion (more specifically the Action used to encapsulate the constructor call) is not called on the same thread the WebView2 instance is created on (i.e. the same thread, the test is entered with - which should be the STA Thread due to the use of the [TestExecutor<STAThreadExecutor>] annotation). Therefore, the call to _webView.SetCurrentValue in the WebViewController constructor results in an exception, as WebView2 mostly only accepts calls coming from the thread that owns it.
If I ditch the AAA structure and therefore the encapsulation of the constructor call, the test becomes green again:
[Test]
public async Task ConstructorCallWithValidWebView_DoesNotThrowException()
{
var webView = new WebView2();
// Implicit assertion: no Exception is thrown.
_ = new WebViewController(webView);
}If this actually is the targeted / expected behavior: is there a workaround how to make the assertion definitively run on the same thread as the rest of the test?
2. In #3462 assertion method IsDefault has been re-implemented for nullable reference types like object and string. However, this fix is not working for nullable value types like bool?:
[Test]
public async Task NullableBool_IsDefault()
{
bool? nullableBool = default;
// Error:
await Assert.That(nullableBool).IsDefault();
}The type 'bool?' must be a non-nullable value type in order to use it as parameter 'TValue' in the generic type or method 'IsDefaultAssertionExtensions.IsDefault(IAssertionSource)'