Search code examples
c#unit-testingmoqblazor-webassemblymstest

Unit testing blazor component that includes StateHasChanged() method with MSTest


While writing a unit test for a blazor component which renders a TreeView from Telerik, I stumbled upon this error:

Message: 
Test method Redacted.Web.Client.UnitTests.Components.TreeViewTest.PerformSearch_PerformEmptySearch_NoSearchPerformed threw exception: 
System.InvalidOperationException: The render handle is not yet assigned.

  Stack Trace: 
RenderHandle.ThrowNotInitialized()
RenderHandle.Render(RenderFragment renderFragment)
ComponentBase.StateHasChanged()
TreeView.PerformSearch(String searchQuery) line 78
TreeView.PerformSearch() line 60
TreeViewTest.PerformSearch_PerformEmptySearch_NoSearchPerformed() line 42
ThreadOperations.ExecuteWithAbortSafety(Action action)

^ Tldr: It crashes when it runs StateHasChanged. I assume it because the ComponentBase has not been initialized when it is run as a unit test.

This is currently what my test looks like:

[TestInitialize]
{
   _treeView = new TreeView()

   var onUpdateMock = new Mock<Action>();
   _treeView.OnUpdate = onUpdateMock.Object;
}

[TestMethod]
public async Task PerformSearch_PerformEmptySearch_NoSearchPerformed()
{
   string searchText = string.Empty;
   _treeView.Data = generateTreeViewMockData();

   _treeView.PerformSearch(searchText);

   Assert.AreEqual(0, _treeView.ExpandedItems.Count());
   Assert.AreEqual(generateTreeViewMockData().Count, _treeView.Data.Count);
}

And this is what the function looks like:

public void PerformSearch(string searchQuery)
{
   SearchQuery = searchQuery; 
   ExpandedItems = new List<Object>();

   if (string.IsNullOrEmpty(searchQuery))
   {
      ClearSearchQuery();
   }
   else
   {
      var result = FilterData(Data.ToList(), searchQuery);
      FilteredData = result.Count == 0 ? FilteredData : result;
   }

   StateHasChanged();
}

Is there any way to mock the ComponentBase, or is there a built in function for this purpose?

The first thing I tried was removing any unnecessary usage of StateHasChanged() in my code, but this last one, is essential for the functionality of my component.

I also tried simply creating a mock of ComponentBase, to then use mock.Setup(x => x.StateHasChanged()...). That does not work however since the the method is protected.

Notes: I do not have access to any other testing libraries. I am only able to use MSTest and Moq.


Solution

  • You can try to override protected method ShouldRender() of your component and return false if it's inside unit test (perhaps use global or local flag)

    public static bool IsUnitTest = false;
    
    protected override bool ShouldRender()
    {
        return base.ShouldRender() && !IsUnitTest;
    }