Blazor Web App (new)

Blazor Web App (new) 

(SPAs in Azure)


   Blazor natively treats app state as a first-class citizen. You do not need to deal with the complex serialization, API layer construction, or JavaScript state synchs required by frameworks like React or Angular. By default, Blazor Web Apps use Prerendering (Server executes your state container, renders the HTML, and sends it to the browser. Then, interactive connection kicks in, instantiates a new state container, and re-renders the page.).

Blazor Web App (new)
   Introduced in .NET 8, this is the gold standard for modern Blazor SPAs. It loads the initial page rapidly using Blazor Server, while downloading the WebAssembly bundle in the background. Subsequent interactions use the browser's local resources, giving you the best of both state models. For a highly stateful SPA, the Interactive Auto render mode is the crown jewel because it solves the "slow initial load" problem that plagued traditional client-side SPAs.
    Steps when User Visits App:
1) Server instantly renders raw HTML/CSS (Static SSR) ----> User sees page instantly 
2) Blazor Server spins up via SignalR (Interactive Server) -> User can immediately interact 
3) Background downloads WebAssembly bundle (.NET runtime) -> Happens invisibly 
4) On next visit, switches to client-side WebAssembly (Interactive WebAssembly) -> Zero server RAM cost now

DbContext Problems
   If you use the Interactive Auto mode, your data-fetching and state logic must be location-agnostic. A standard DbContext injected directly into a component will crash. To build a stateful SPA in .NET 8+, you must abstract your data state behind an interface. You then register two different implementations of that interface: one for the Server project and one for the Client (WASM) project.
    Shared interface:
public interface ICustomerStateService {    
    List<CustomerDto> Customers { get; }
    event Action? OnStateChanged;
    Task LoadCustomersAsync(); }

    Server:
public class ServerCustomerStateService : ICustomerStateService {
    private readonly MyDbContext _context; // Direct database access
    public List<CustomerDto> Customers { get; private set; } = new();
    public event Action? OnStateChanged;

    public ServerCustomerStateService(MyDbContext context) => _context = context;

    public async Task LoadCustomersAsync()
    {
        // Fetch straight from the database for the initial fast load
        Customers = await _context.Customers.ToListAsync();
        OnStateChanged?.Invoke();
    } }

   Browser Client:
public class ClientCustomerStateService : ICustomerStateService {
    private readonly HttpClient _http; // Network access
    public List<CustomerDto> Customers { get; private set; } = new();
    public event Action? OnStateChanged;

    public ClientCustomerStateService(HttpClient http) => _http = http;

    public async Task LoadCustomersAsync()
    {
        // Once running on the browser, fetch via secure Web API endpoints
        Customers = await _http.GetFromJsonAsync<List<CustomerDto>>("api/customers") ?? new();
        OnStateChanged?.Invoke();
    } }


Register Them Separately in Program.cs
  • In the Server Project's Program.cs:
    builder.Services.AddScoped<ICustomerStateService, ServerCustomerStateService>();
  • In the Client Project's Program.cs:
    builder.Services.AddScoped<ICustomerStateService, ClientCustomerStateService>();

                                                                  TESTING

Component Testing with bUnit 
   Use bUnit, a dedicated, open-source testing library built specifically for Blazor components. bUnit renders your components in an in-memory, headless environment using C#. It is  incredibly fast (executing in milliseconds) and integrates natively with standard C# test  runners like xUnit, NUnit, or MSTest.
Key Features of bUnit:
  • No Browser Needed: It creates a virtualized Blazor architecture directly in memory.
  • Dependency Injection (DI) Mocking: You can inject mock data stores or mock HTTP clients straight into the component context.
  • Semantic HTML Verification: It compares HTML outputs structurally, ignoring irrelevant formatting quirks like whitespace variations or attribute ordering.

End-to-End (E2E) Testing with Playwright
Why Playwright Beats Selenium and Cypress for Blazor:
  • SignalR WebSocket Resilience: Blazor Server apps rely heavily on a constant, streaming WebSocket connection. Playwright handles asynchronous DOM updates natively via its auto-wait functionality, meaning it will wait for the SignalR payload to resolve before failing a test. 
  • WebAssembly Bootstracing: Blazor WASM apps can take a moment to download the .NET WASM runtime and initialize the client application state on the first load. Playwright easily handles these initialization delays without flaky hardcoded sleep delays. 
  • Blazor Web App State Capture: As discussed earlier, Playwright can log in once, capture the cookie/storage state, and reuse that state across dozens of isolated browser contexts to run high-speed parallel tests.

Unified Local Observability & Orchestration with .NET Aspire
The ultimate tool for testing distributed workflows in the .NET 8+ ecosystem is .NET Aspire. It was specifically built to orchestrate and observe complex microservice setups directly from Windows.
1) Dashboard: When you debug your Blazor Web App via the Aspire AppHost on Windows, you get a real-time OpenTelemetry console. If a mobile user hits a button that triggers a distributed database transaction or an event queue (like Azure Service Bus), Aspire visualizes the entire path as a nested call tree. 
2) Aspire.Hosting.Testing: Instead of manually clicking through your app, you can use this framework to programmatically spin up your .NET 8 Blazor Web App, its database dependencies, and your API gateways inside an integration test project. You can execute a workflow and assert that the distributed systems successfully generated the expected trace IDs and state mutations.
Deployment Validation with .NET Aspire
1) Aspire.Hosting.Testing library
   Instead of writing brittle PowerShell scripts to check if containers or endpoints are up, .NET Aspire provides a first-class integration testing framework. You can write standard C# xUnit or NUnit tests that use the DistributedApplicationTestingBuilder to programmatically launch and validate your entire deployment topology.
  • Resource Readiness Verification: Your test code can actively inspect the orchestration state to ensure that databases, Redis caches, and microservices are completely initialized before traffic hits them.
  • Automated HttpClient Insertion: The testing framework automatically injects pre-configured HttpClient instances that map directly to your service names, allowing you to instantly run API smoke tests against your live, running topology.
2) Health Checks and Live Telemetry validation  
    .NET Aspire heavily enforces the use of the core MS Health Checks framework (Microsoft.Extensions.Diagnostics.HealthChecks).
  • Dependency Mapping: Aspire auto links your cloud infrastructure (like Azure Service Bus or SQL Server) to the health check endpoint.
  • Gateway Validation: Deployment gates (like Azure DevOps Pipelines or GitHub Actions running on Windows runners) can query the centralized Aspire-configured /health endpoint. If a single microservice cannot reach its database, the deployment automatically flags as a failure. 
  • OpenTelemetry Verification: Because Aspire leverages OpenTelemetry (OTLP) natively, your automated validation tests can actually query the telemetry in-memory to verify that post-deployment test traffic successfully generated valid traces across service boundaries with zero errors.
Best Practices for Testing Blazor
  1. Isolate Your Logic from the UI: Do not cram complex data manipulation or API calling algorithms into the @code block of your .razor files. Keep components strictly focused on UI presentation. Move complex algorithms to a standalone C# Service Class, which can be easily unit-tested with standard xUnit without any UI rendering overhead.  
  2. Test Both Blazor Host Environments: If you are building a modern Blazor Web App (.NET 8/9+) utilizing the Interactive Auto mode, keep in mind your code executes on both the server side and the client side. Ensure your bUnit test project mirrors these dependency injection layers properly so that components behave identically under test conditions as they do in production. 
  3. Cleanly Dispose of State Subscriptions: If your components manually subscribe to state change events (e.g., State.OnStateChanged += StateHasChanged), verify that your tests explicitly confirm those components implement IDisposable and unregister their handlers. If left unmanaged under continuous integration loads, these lingering event registrations cause cumulative memory bloat.

Comments

Popular posts from this blog

GHL Email Campaigns

Free AI Tools

Await