blazorblazor-server-sidebunit

In bUnit how can I wait for OnAfterRenderAsync() in the page that is my RenderComponent to complete?


Blazor server, .NET 8 (pre-release), I have a bUnit test where I need to wait for the OnAfterRenderAsync() in the page I am testing to complete before running tests.

So I need something like:

var renderedComponent = ctx.RenderComponent<LouisHowe.web.Pages.Account.ConfirmPhone>();
renderedComponent.WaitForRenderToComplete();

Is there a way to test for this? The best I have come up with is set a value in a hidden element in the OnAfterRenderAsync() and WaitFor that element to have that value. I don't like populating elements solely for testing, but that would work.

Update: Why do I need this? Because I have two pages where I have a @ref= to a child component. And I access that to complete the initialization of the page.

I also have a page that needs to call JavaScript to get some browser settings to complete the initialization of a page.

So in those two cases, I'm not able to test everything having been initialized on the page until OnAfterRenderAsync() completes.


Solution

  • Your questions might be either misleading or there is a misunderstanding. If you are in OnAfterRenderAsnyc - there will be no more render-cycles (hence the name of the function). Only exception: You call StateHasChanged inside.

    So I am not sure if you want to wait until OnAfterRendererAsync gets called or if you really want to await that call somehow. I will try to answer both.

    Wait until OnAfterRenderAsync gets called

    You can leverage the behavior of the Blazor renderer itself. That is, as long as you have synchronous code, RenderComponent will return to the test code right before OnAfterRenderAsync is called.

    Now, synchronous code would also be achievable as long as you have async code that is directly completed.

    So if you have something like this:

    protected override async Task OnInitializedAsync()
    {
       bar = await Repository.GetByIdAsync(id);
    }
    

    And in your test you do something like that:

    var repositoryMock = CreateRepositoryMockOrFake();
    Repository.GetByIdAsync(1).Returns(Task.FromResult(new Bar());
    

    You still run synchronously! With that, RenderComponent will be synchronous! So once RenderComponent returns - the OnAfterRenderAsync function is called.

    Wait until OnAfterRenderAsync is finished

    Now that part is trickier! The method isn't called OnAfterRenderAsync without a reason. So without any further information, it is hard to tell what to do.

    1. You could use WaitForState and look for an internal observable state
    var cut = RenderComponent<Foo>();
    
    // Bar will be set to 10 inside OnAfterRenderAsync
    cut.WaitForState(() => cut.Instance.Bar == 10);
    
    1. There is observable DOM change If you have something like StateHasChanged inside your OnAfterRenderAsync obviously you can just observe that state from the outside, as you would with any other button click or whatnot.

    2. There is no direct observable state change If there is nothing you can directly observe, well it might be worth not testing it all! It seems like an implementation detail and should be left alone.

    The exception to this is, of course, if you "not observable state change" has effects on render cycles / events what not afterward. If so, trigger those actions.