When building automated incident response pipelines or custom webhook receivers in ASP.NET Core, ensuring that your endpoints handle incoming requests reliably is paramount. A webhook controller acts as the gateway between external cloud monitoring services (like Azure Monitor) and your application's internal logic. If it fails to parse a payload, mishandles security tokens, or crashes on malformed data, your incident response pipeline breaks silently.
To guarantee high reliability, you need comprehensive unit tests. In this comprehensive guide, we will walk through writing robust unit tests for an ASP.NET Core webhook controller using xUnit, Moq, and FluentAssertions.
Why Unit Test Webhook Controllers?
Webhook endpoints have specific testing requirements that set them apart from standard CRUD controllers:
Security Verification: You must ensure unauthorized requests lacking valid secret tokens are rejected instantly with a
401 Unauthorized.Payload Resilience: External services can occasionally send malformed or incomplete data; your controller must gracefully return a
400 Bad Request.Behavior Verification: Beyond returning correct HTTP status codes, you need to verify that incoming alerts are correctly logged and passed down to downstream services.
Step 1: Install Required Test NuGet Packages
Before writing tests, ensure your testing project has the necessary packages installed. Navigate to your YourSolution.UnitTests project and run:
Bash
dotnet add package xunit
dotnet add package xunit.runner.visualstudio
dotnet add package Moq
dotnet add package FluentAssertions
dotnet add package Microsoft.AspNetCore.Mvc.Core
dotnet add package Microsoft.NET.Test.Sdk
Step 2: Setting Up the Test Class and Mocks
Because your webhook controller depends on ILogger<WebhookController> for logging alert events, you will use Moq to create a mock logger. We will use FluentAssertions for clean, expressive assertions.
Create a test class named WebhookControllerTests.cs:
C#
using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
using YourSolution.API.Controllers;
using YourSolution.API.Models;
namespace YourSolution.UnitTests.Controllers
{
public class WebhookControllerTests
{
private readonly Mock<ILogger<WebhookController>> _loggerMock;
private readonly WebhookController _controller;
private const string ValidToken = "YourSuperSecretToken123";
public WebhookControllerTests()
{
_loggerMock = new Mock<ILogger<WebhookController>>();
_controller = new WebhookController(_loggerMock.Object);
}
}
}
Step 3: Writing Unit Tests for Success, Security, and Edge Cases
A complete test suite for a webhook controller should cover three distinct scenarios: valid payloads with correct authorization, unauthorized token attempts, and malformed payloads.
1. Testing the Success Path (Valid Payload & Token)
Verify that when a well-formed Azure Common Alert Schema payload arrives with the correct secret token, the controller returns 200 OK and logs the alert.
C#
[Fact]
public async Task ReceiveAzureAlert_ReturnsOk_WhenPayloadIsValidAndTokenIsCorrect()
{
// Arrange
var payload = new AzureAlertPayload
{
SchemaId = "azureMonitorCommonAlertSchema",
Data = new AlertData
{
Essentials = new AlertEssentials
{
AlertRule = "API-High-Failure-Rate",
Severity = "Sev2",
MonitorCondition = "Fired",
FiredDateTime = DateTime.UtcNow,
Description = "Failure rate exceeded 5%."
}
}
};
// Act
var result = _controller.ReceiveAzureAlert(ValidToken, payload);
// Assert
result.Should().BeOfType<OkObjectResult>();
var okResult = result as OkObjectResult;
okResult?.StatusCode.Should().Be(200);
// Verify that the logger captured the alert event
_loggerMock.Verify(
x => x.Log(
LogLevel.Warning,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("Azure Alert Fired")),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)!),
Times.Once);
}
2. Testing Security Constraints (Invalid Token)
Ensure that requests with incorrect or missing secret tokens are rejected immediately.
C#
[Fact]
public async Task ReceiveAzureAlert_ReturnsUnauthorized_WhenTokenIsInvalid()
{
// Arrange
var payload = new AzureAlertPayload();
const string invalidToken = "WrongToken999";
// Act
var result = _controller.ReceiveAzureAlert(invalidToken, payload);
// Assert
result.Should().BeOfType<UnauthorizedObjectResult>();
var unauthorizedResult = result as UnauthorizedObjectResult;
unauthorizedResult?.StatusCode.Should().Be(401);
}
3. Testing Data Validation (Malformed Payloads)
Verify that if Azure sends a payload where essential data is missing, the controller rejects it gracefully.
C#
[Fact]
public async Task ReceiveAzureAlert_ReturnsBadRequest_WhenPayloadEssentialsAreNull()
{
// Arrange
var malformedPayload = new AzureAlertPayload
{
Data = new AlertData
{
Essentials = null! // Simulating missing essentials
}
};
// Act
var result = _controller.ReceiveAzureAlert(ValidToken, malformedPayload);
// Assert
result.Should().BeOfType<BadRequestObjectResult>();
var badRequestResult = result as BadRequestObjectResult;
badRequestResult?.StatusCode.Should().Be(400);
}
Summary
By unit testing your webhook controller with xUnit, Moq, and FluentAssertions, you ensure that your cloud monitoring integration is resilient, secure, and production-ready:
Payload Verification: Guarantees that Azure Common Alert Schema data is parsed correctly without unhandled null reference exceptions.
Security Enforcement: Validates that secret token checks prevent unauthorized third-party requests.
Behavioral Confirmation: Asserts that critical alerts are properly logged and processed.

Join the conversation! Your thoughts help the community grow.