moqblazor-server-sidexunitbunit

Mock ProtectedSessionStorage for Blazor


Is there a way to Mock ProtectedSessionStorage in a Blazor Server Side project?

I tried below code but I am getting error : "Type to mock (ProtectedSessionStorage) must be an interface, a delegate, or a non-sealed, non-static class."

private readonly Mock<ProtectedSessionStorage> _sessionStorage = new();
private readonly Mock<IDataProtector> _mockDataProtector = new();
private readonly Mock<IDataProtectionProvider> _mockDataProtectionProvider = new();


//in ctor()
Services.AddSingleton(_sessionStorage.Object);

//mock IDataProtector
_mockDataProtector = new Mock<IDataProtector>();
_mockDataProtector.Setup(sut => sut.Protect(It.IsAny<byte[]>())).Returns(Encoding.UTF8.GetBytes("protectedText"));
_mockDataProtector.Setup(sut => sut.Unprotect(It.IsAny<byte[]>())).Returns(Encoding.UTF8.GetBytes("originalText"));
Services.AddSingleton(_mockDataProtector.Object);

//mock IDataProtectionProvider
_mockDataProtectionProvider = new Mock<IDataProtectionProvider>();
_mockDataProtectionProvider.Setup(s => s.CreateProtector(It.IsAny<string>())).Returns(_mockDataProtector.Object);
Services.AddSingleton(_mockDataProtectionProvider.Object);


//in testMethod()
EquipmentSearchFilterDto filter = new();
filter.HospitalID = 1;

var result = new ProtectedBrowserStorageResult<EquipmentSearchFilterDto>();

_sessionStorage.Setup(x => x.GetAsync<EquipmentSearchFilterDto>(It.IsAny<string>()))
    .ReturnsAsync(new ProtectedBrowserStorageResult<EquipmentSearchFilterDto>());

I thought of hiding ProtectedSessionStorage implementation behind an interface unfortunately I was unable to come up with one. Any ideas?


Solution

  • Due to the way these classes were designed mocking them was not easy, but I finally managed to do it.

    Note that for this to work this code must run on class that inherits Bunit.TestContext from https://bunit.dev/

    The TestContext will allow you to use this.JSInterop below.

        // This string will be the json of the object you want
        // to receive when GetAsync is called from your ProtectedSessionStorage.
        // In my case it was an instance of my UserSession class.
        string userSessionJson = JsonSerializer.Serialize(new UserSession
        {
            EMail = "test@test.com",
            Name = "Test User",
            Role = "TestRole"
        });
    
        // Base64 used internally on the ProtectedSessionStorage.
        string base64UserSessionJson = Convert.ToBase64String(Encoding.ASCII.GetBytes(userSessionJson));
    
        // Notice how the mocked methods return the json.
        Mock<IDataProtector> mockDataProtector = new Mock<IDataProtector>();
        _ = mockDataProtector.Setup(sut => sut.Protect(It.IsAny<byte[]>())).Returns(Encoding.UTF8.GetBytes(base64UserSessionJson));
        _ = mockDataProtector.Setup(sut => sut.Unprotect(It.IsAny<byte[]>())).Returns(Encoding.UTF8.GetBytes(userSessionJson));
    
        Mock<IDataProtectionProvider> mockDataProtectionProvider = new Mock<IDataProtectionProvider>();
        _ = mockDataProtectionProvider.Setup(s => s.CreateProtector(It.IsAny<string>())).Returns(mockDataProtector.Object);
    
        // This is where the JSInterop by bunit is used.
        // localStorage might need to be changed to your variable name or
        // purpose? If you execute the test without this setup
        // an exception will tell you the name you need to use.
        _ = this.JSInterop.Setup<string>("localStorage.getItem", "UserSession").SetResult(base64UserSessionJson);
    
        // Use this instance on your constructor or context.
        ProtectedSessionStorage protectedSessionStorage = new ProtectedSessionStorage(
            this.JSInterop.JSRuntime,
            mockDataProtectionProvider.Object);