How to fix this simple ElementNotFoundException in Blazor?

574 Views Asked by At

I am unit testing a blazor app. I get a ElementNotFoundException. I think the cause for this is an if statement in the the index.razor page. see code below:

<div class="row">
    <div class="col-12">
        @if ((challenges != null) && (challenges.Count > 0))
        {
            <MultiStepComponent Id="MultiStepContainer" Challenges="@challenges">
                <div class="row p-3">
                    <div class="col-6" id="challengeContainer">
                        @foreach(var c in challenges)
                        {
                            <MultiStepNavigation Name="@c.Title">
                                <h1>@c.Title</h1>
                                <img class="float-left" src="@c.ImagePath" width="200" />
                                @foreach(var sentence in c.Description)
                                {
                                    <p>@sentence</p>
                                }
                            </MultiStepNavigation>
                        }
                    </div>
                    <div class="col-6">
                        <textarea rows="26" cols="120" @bind="input" id="input"></textarea>

                        <button class="btn" id="runBtn" @onclick="RunAsync">Run</button>

                        <br />
                        <textarea rows="10" cols="120" id="output" readonly>@((MarkupString)Output)</textarea>
                    </div>
                </div>
            </MultiStepComponent>
        }
    </div>
</div>

The code behind of this page (index.razor.cs) has the following initialization code:

protected override async Task OnInitializedAsync()
    {
        jsonRepository = new JSONChallengeRepository();
        challenges = await jsonRepository.GetChallengesAsync();
    }

The test for this page is here:

[Test]
    public async Task Compile_code_Success()
    {
        _codingChallengeService.Setup(c => c.SendInputToCompilerAsync("50+50")).ReturnsAsync("100");
        _testContext.Services.AddScoped(x => _codingChallengeService.Object);
        var razorComponent = _testContext.RenderComponent<Index>();
        razorComponent.Instance.challenges = GetChallenges();

        if ((razorComponent.Instance.challenges != null) && (razorComponent.Instance.challenges.Count > 0))
        {
            var runBtn = razorComponent.FindAll("button").FirstOrDefault(b => b.OuterHtml.Contains("Run"));
            var input = razorComponent.Find("#input");
            input.Change("50+50");
        
            runBtn.Click();
        
            var outputArea = razorComponent.Find("#output");
            var outputAreaText = outputArea.TextContent;
            Assert.AreEqual("100", outputAreaText);
        }

        Assert.IsNotNull(razorComponent.Instance.challenges);
    }

The #input is missing..Why??

Thanks in advance!

1

There are 1 best solutions below

0
On

I am guessing the problem is that you do not cause the component under test to re-render when you assign razorComponent.Instance.challenges property/field, and if the component does not re-render, then the markup inside @if ((challenges != null) && (challenges.Count > 0)) block in the component is not displayed.

In general, dont mutate properties (parameters) of components through the razorComponent.Instance. If you really have to do so, make sure to trigger a render after.

Instead, pass parameters to the component through the RenderComponent or SetParametersAndRender methods, or through services injected into components. That will cause the component to go through its normal render life-cycle methods.