-
-
Notifications
You must be signed in to change notification settings - Fork 109
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
JsRuntimePlannedInvocation not behaving as expected together with RenderComponent #78
Comments
Hi Johan, I'll take a closer look later tonight or tomorrow and get back to you. In the meantime, try wrapping your assertion in |
@johanjonsson1 hey, thanks for the patients. Here is a bit more feedback:
In general, if you want to test your component when firstRender is false, simply call As for the issue in general, a planned invocation will only "SetResult" on invocations it has already registered, thus, you have to call SetResult after RenderComponent. You also have to use WaitForAssertion, since the change in MyComponent happens async on the render thread, and the test is running in another thread. [Fact]
public void Test()
{
using var ctx = new TestContext();
var mockJsRuntime = ctx.Services.AddMockJsRuntime(JsRuntimeMockMode.Strict);
var plannedInvocation = mockJsRuntime.Setup<string>("func");
var cut = ctx.RenderComponent<MyComponent>();
plannedInvocation.SetResult("result test"); // Move to after RenderComponent
cut.WaitForAssertion(() => Assert.Equal("result test", cut.Instance.TestProperty));
} Last, but not least, to get WaitForAssertion to actually discover the change, you have to trigger a rerender in the component when InvokeAsync returns through StateHasChanged. class MyComponent : ComponentBase
{
[Inject]
protected IJSRuntime JsRuntime { get; set; }
public string? TestProperty { get; private set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
TestProperty = await JsRuntime.InvokeAsync<string>("func");
StateHasChanged();
}
}
} Now, admittedly, it took me a while to remember how planned invocation works, so it might be worth considering changing its behaviour. That would mean you cannot set a new result for later invocation that matches the planned invocation. What do you think? |
Thank you for your investigation @egil. The render again thing i'm aware of but my case is actually based on this interop example https://blazor-university.com/javascript-interop/calling-dotnet-from-javascript/lifetimes-and-memory-leaks/ Testing that the correct private GeneratorHandle (in my case set with plannedStartInvocation.SetResult) and thus not requiring a rerender, is passed back to javascript when disposing. Its working fine by moving the SetResult call and using WaitForState. var mockJsRuntime = Services.AddMockJsRuntime(JsRuntimeMockMode.Strict);
var plannedStartInvocation = mockJsRuntime.Setup<int>("start", args => true);
var plannedStopInvocation = mockJsRuntime.SetupVoid("stop", args => true);
var handlerId = 24;
var cut = RenderComponent<Component>();
plannedStartInvocation.SetResult(handlerId);
cut.WaitForState(() => true);
cut.Instance.Dispose();
Assert.AreEqual(1, plannedStartInvocation.Invocations.Count, "start should only be called once");
Assert.AreEqual(1, plannedStopInvocation.Invocations.Count, "stop should be called when disposing");
Assert.AreEqual(handlerId, plannedStopInvocation.Invocations.First().Arguments.First()); I guess the thing that threw me off was me being use to the AAA pattern and making all setup at the start of the test :) Also, with your explanation Test006 in MockJsRuntimeInvokeHandlerTest makes much more sense! Regarding your last question. I did something like that and broke Test006. public async Task Test1()
{
var identifier = "func";
var sut = new MockJsRuntimeInvokeHandler(JsRuntimeMockMode.Strict);
var plannedInvoke = sut.Setup<int>(identifier);
var jsRuntime = sut.ToJsRuntime();
plannedInvoke.SetResult(1, mutable: true); // default true?
var i1 = await jsRuntime.InvokeAsync<int>(identifier);
i1.ShouldBe(1);
plannedInvoke.SetResult(2, mutable: true);
plannedInvoke.SetResult(3, mutable: true);
plannedInvoke.SetResult(4, mutable: true);
var i2 = await jsRuntime.InvokeAsync<int>(identifier);
i2.ShouldBe(4);
}
public async Task Test2()
{
var identifier = "func";
var sut = new MockJsRuntimeInvokeHandler(JsRuntimeMockMode.Strict);
var plannedInvoke = sut.Setup<int>(identifier);
var jsRuntime = sut.ToJsRuntime();
plannedInvoke.SetResult(1, mutable: false);
var i1 = await jsRuntime.InvokeAsync<int>(identifier);
i1.ShouldBe(1);
plannedInvoke.SetResult(2); // ignored or exception
var i2 = await jsRuntime.InvokeAsync<int>(identifier);
i2.ShouldBe(1);
} |
I agree. I will try to get that change into the next release. |
Just a quick update on this @johanjonsson1. I decided that |
…ests will get the result when they arrive, if one is set. A new result can also be provided at any time, but it will only affect requests arriving after it has been set, not the previous. Closes #78
I have a test where MyComponent is setting a property once during OnAfterRenderAsync when firstRender is true which it only is when constructing the component using RenderComponent.
When i call Setup and then SetResult with a specified value on the planned invocation for the MockJsRuntime, the expected behaviour is that the value specified is returned when calling InvokeAsync.
MyComponent.razor
MyComponentBase.cs
And this test case:
Results in test case failing
Additional info:
Above test works as expected if I change the RegisterInvocation method in JsRuntimePlannedInvocationBase to also return Completed tasks but that results in other failing unit tests
The text was updated successfully, but these errors were encountered: