Skip to content

Null exception when calling RenderComponent for Telerik Scheduler #973

@jyatesmike

Description

@jyatesmike

Attempting to write tests for Telerik Scheduler component. It is getting a null reference exception.
Example:
Testing this component:

@namespace HCHB.BlazorComponents
@typeparam TItem
@using Telerik.Blazor.Components

<TelerikRootComponent>
	<TelerikScheduler Data="@Data"
		@bind-Date="@SchedulerStartDate"
		@bind-View="@SchedulerCurrentView"
		AllowUpdate="true"
		TItem="TItem">
		<SchedulerViews>
			<SchedulerDayView StartTime="@DayStart" EndTime="@DayEnd" />
			<SchedulerWeekView StartTime="@DayStart" EndTime="@DayEnd" SlotDivisions="1" SlotDuration="30" />
			<SchedulerMonthView />
		</SchedulerViews>
	</TelerikScheduler>
</TelerikRootComponent>
using Telerik.Blazor;
using Microsoft.AspNetCore.Components;
using System.Diagnostics.CodeAnalysis;

namespace HCHB.BlazorComponents
{
    public partial class Scheduler<TItem>
    {
        [Parameter, AllowNull]
        public IEnumerable<TItem> Data { get; set; }

        [Parameter]
        public DateTime SchedulerStartDate { get; set; } = new DateTime(2022, 7, 25);

        [Parameter]
        public SchedulerView SchedulerCurrentView { get; set; } = SchedulerView.Week;

        // only the time portion matters
        [Parameter]
        public DateTime DayStart { get; set; } = new DateTime(2000, 1, 1, 6, 0, 0);
        [Parameter]
        public DateTime DayEnd { get; set; } = new DateTime(2000, 1, 1, 19, 0, 0);
    }

    public enum AppointmentStatus
    {
        Accepted,
        Overdue,
        Pending
    }

    public class SchedulerAppointment
    {
        public Guid Id { get; set; }
        public string Title { get; set; } = string.Empty;
        public string Description { get; set; } = string.Empty;
        public DateTime Start { get; set; }
        public DateTime End { get; set; }
        public bool IsAllDay { get; set; }

        public SchedulerAppointment()
        {
            Id = Guid.NewGuid();
        }
        public AppointmentStatus Status { get; set; }
    }
}

With this test:

[Fact]
public void NewTelerikTest()
{
	_ = RenderComponent<Scheduler<List<SchedulerAppointment>>>();

	var button = this.RootComponent.Find("button[id=\"window-test-button\"]");

	Assert.Contains("Add Content", button.InnerHtml);
}

and using this TestContext...

using Bunit;
using HCHB.BlazorComponents.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
using System;
using Telerik.Blazor.Components;
using Telerik.Blazor.Services;
using Telerik.JustMock;

namespace HCHB.BlazorComponents.Tests.Common
{
    /// <summary>
    /// TestContext using a CascadingValue of type <see cref="TelerikRootComponent"/>, rather than the actual implementation.
    /// </summary>
    public class TelerikTestContext : TestContext
    {
#nullable enable
        private IRenderedComponent<TelerikRootComponent>? rootComponent;
#nullable disable
        public IRenderedComponent<TelerikRootComponent> RootComponent
            => rootComponent ?? throw new InvalidOperationException("The RootComponent is not available before a component has been rendered with the TestContext.");

        public TelerikTestContext()
        {
            // mock the JS Interop service, you cannot use the one coming from the context
            var jsRuntimeMock = Mock.Create<IJSRuntime>();

            // you can also register a real one for actual localization to test that too
            var localizerMock = Mock.Create<ITelerikStringLocalizer>();

            // make sure JS Interop is available first
            Services.AddSingleton(jsRuntimeMock);

            // add the Telerik Blazor services like in a regular app
            Services.AddUIServices();
            Services.AddSingleton(localizerMock);
        }

        public override IRenderedFragment Render(RenderFragment renderFragment)
        {
            EnsureRootComponent();
            return base.Render(renderFragment);
        }

        public override IRenderedComponent<TComponent> Render<TComponent>(RenderFragment renderFragment)
        {
            EnsureRootComponent();
            return base.Render<TComponent>(renderFragment);
        }

        public override IRenderedComponent<TComponent> RenderComponent<TComponent>(params ComponentParameter[] parameters)
        {
            EnsureRootComponent();
            return base.RenderComponent<TComponent>(parameters);
        }

        public override IRenderedComponent<TComponent> RenderComponent<TComponent>(Action<ComponentParameterCollectionBuilder<TComponent>> parameterBuilder)
        {
            EnsureRootComponent();
            return base.RenderComponent(parameterBuilder);
        }

        private void EnsureRootComponent()
        {
            if (rootComponent is not null) return;

            // add a Telerik Root Component to hold all Telerik components and other content
            rootComponent = (IRenderedComponent<TelerikRootComponent>)Renderer.RenderComponent<TelerikRootComponent>(new ComponentParameterCollection());

            // provide the Telerik Root Component to the child components that need it (the Telerik components)
            RenderTree.TryAdd<CascadingValue<TelerikRootComponent>>(p =>
            {
                p.Add(parameters => parameters.IsFixed, true);
                p.Add(parameters => parameters.Value, RootComponent.Instance);
            });
        }
    }
}

Results in this output:

 Message: 
System.NullReferenceException : Object reference not set to an instance of an object.

  Stack Trace: 
ContentTableBase`1.SetSlotMetrics(Dictionary`2 metrics)
ContentTableBase`1.GetSlotMetrics()
ContentTableBase`1.OnAfterRenderAsync(Boolean firstRender)
TestRenderer.AssertNoUnhandledExceptions() line 384
TestRenderer.Render[TResult](RenderFragment renderFragment, Func`2 activator) line 247
TestRenderer.RenderFragment(RenderFragment renderFragment) line 53
TestContextBaseRenderExtensions.RenderInsideRenderTree(TestContextBase testContext, RenderFragment renderFragment) line 43
TestContextBaseRenderExtensions.RenderInsideRenderTree[TComponent](TestContextBase testContext, RenderFragment renderFragment) line 23
TestContext.Render[TComponent](RenderFragment renderFragment) line 66
TestContext.RenderComponent[TComponent](Action`1 parameterBuilder) line 52
Scheduler.Test1() line 34

Expected behavior:

Expect RenderComponent to not throw an exception here. Unless I am missing something. Have looked at it for a few days and tried various things but always same exception.

Version info:

  • bUnit version: 1.14.4
  • .NET Runtime and Blazor version: net6.0
  • Telerik.UI.for.Blazor 4.0.0
  • OS type and version:Windows 10 Enterprise

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions