Search code examples
.netasp.net-coreblazornunitbunit

How to test EditForm component without the EditForm?


I got this error:

System.InvalidOperationException : Microsoft.AspNetCore.Components.Forms.InputText requires a > > cascading parameter of type EditContext. For example, you can use Microsoft.AspNetCore.Components.Forms.InputText inside an EditForm.

This is my component (_Item):

@if (UtiliseItemCostum())
{
    <div class="form-group mt-3">
        <label>Item</label>
        @if (EpicerieDetailsUtiliseItemCustom())
        {
            <InputText id="itemNonEnregistre" @bind-Value="EpicerieDetailsDto.ItemCustom"></InputText>
        }
        else if (GabaritDetailsUtiliseItemCustom())
        {
            <InputText id="itemNonEnregistre" @bind-Value="GabaritDetailsDto.ItemCustom"></InputText>
        }
    </div>
}
else
{
    <_MenuDeroulantItems EpicerieDetailsDto="@EpicerieDetailsDto" GabaritDetailsDto="@GabaritDetailsDto" />
}

And this is the parent component:

<EditForm Model="EpicerieDetailsDto" OnValidSubmit="GererEpicerieDetails">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <div class="form-group mt-3">
        <label>@(EpicerieDetailsDto.UtiliseItemCustom ? "Item créé par soi-même:" : "Item dans l'application:")</label>
        <InputCheckbox id="choisirItem" @bind-Value="@EpicerieDetailsDto.UtiliseItemCustom"></InputCheckbox>
    </div>

    <_Item EpicerieDetailsDto="@EpicerieDetailsDto"/>

    <_MenuDeroulantEpicerie EpicerieDetailsDto="@EpicerieDetailsDto"/>
    <ValidationMessage For="()=>EpicerieDetailsDto.Epicerie"></ValidationMessage>

    <_MenuDeroulantUnitesMesure EpicerieDetailsDto="@EpicerieDetailsDto"/>
    <ValidationMessage For="()=>EpicerieDetailsDto.UniteMesure"></ValidationMessage>

    <div class="form-group mt-3">
        <label>Acheté?</label>
        <InputCheckbox @bind-Value="EpicerieDetailsDto.Achete"></InputCheckbox>
    </div>

    <div class="form-group mt-3">
        <label>Quantité</label>
        <InputNumber @bind-Value="EpicerieDetailsDto.Quantite" class="form-control"></InputNumber>
        <ValidationMessage For="()=>EpicerieDetailsDto.Quantite"></ValidationMessage>
    </div>

    <div class="d-flex justify-content-between align-items-end mt-3">
        <NavLink href="@Chemins.EpiceriesDetails(EpicerieId)" class="btn btn-secondary">Retour</NavLink>
        <button class="btn btn-primary py-2" id="soumettre-details-epicere">@Titre</button>
    </div>
</EditForm>

The problem is that when I try this test:

public static IEnumerable<TestCaseData> ItemTestCases()
{
    yield return new TestCaseData(null, new EpicerieDetailsDto() { UtiliseItemCustom = true });
    yield return new TestCaseData(new GabaritDetailsDto() { UtiliseItemCustom = true }, null);
}

[Test]
[TestCaseSource(nameof(ItemTestCases))]
public void QuandChangeCheckBox_AlorsChangeChoixItem(GabaritDetailsDto gabarit, EpicerieDetailsDto epicerie)
{
    var cut = _testContext.RenderComponent<_Item>(parametres => 
        parametres
            .Add(p => p.EpicerieDetailsDto, epicerie)
            .Add(p => p.GabaritDetailsDto, gabarit)
    );

    // Agir Affirmer
    Assert.NotNull(cut.Find("#itemNonEnregistre"));
}

The InputText doesn't find the EditForm. How could I test it?

I excpect that I will be able to test my component with the InputText in somehow mocking the EditForm I guess?


Solution

  • If you need a full explanation, checkout the discussion over on bUnit as mentioned by @Qiang Fu.

    The TL;DR Version: FormComponents do need a cascading value (the EditContext) to work properly.

    With bUnit you can attach a EditContext as parent to your component like this:

    var model = new EpicerieDetailsDto();
    var editContext = new EditContext(model);
    var form = RenderComponent<CascadingValue<EditContext>>(
      p => p.Add(s => s.Value, editContext)
            .AddChildContent<_Item>(/*Your parameters goes here*/)
    );
    
    var cut = form.FindComponent<_Item>(); // This is your component