RadEndpoints.Testing
1.0.0-alpha.19
dotnet add package RadEndpoints.Testing --version 1.0.0-alpha.19
NuGet\Install-Package RadEndpoints.Testing -Version 1.0.0-alpha.19
<PackageReference Include="RadEndpoints.Testing" Version="1.0.0-alpha.19" />
<PackageVersion Include="RadEndpoints.Testing" Version="1.0.0-alpha.19" />
<PackageReference Include="RadEndpoints.Testing" />
paket add RadEndpoints.Testing --version 1.0.0-alpha.19
#r "nuget: RadEndpoints.Testing, 1.0.0-alpha.19"
#:package RadEndpoints.Testing@1.0.0-alpha.19
#addin nuget:?package=RadEndpoints.Testing&version=1.0.0-alpha.19&prerelease
#tool nuget:?package=RadEndpoints.Testing&version=1.0.0-alpha.19&prerelease
RadEndpoints
An API library bringing REPR-style endpoint classes to .NET Minimal API.
Library Goals
Should be:
- Lightweight -- and easy to work with or without.
- Junior Developer Friendly -- without impeding more experienced engineers.
- Backward Compatible -- with Minimal API configuration and features.
- Configurable -- using MinimalApi RouteHandlerBuilder.
- Well Structured -- for common tasks such as validation, mapping and error handling.
- Extensible -- to allow for custom alternate endpoint implmentations.
- Fast and Easy -- to rapidly scaffold projects, endpoints and tests.
Features:
REPR Endpoint Classes
- Reduced configuration noise over minimal api endpoints
- Constructor dependency injection
- Scoped lifetime
- Assembly scanned and configured request validator and model mapper
- Built-in Endpoint Class Conveniences
- HttpContext
- Logger
- Environment Info
- Response Object
- Model Mapper
- TypedResult Shortcuts
public class GetSampleEndpoint(ISampleService sampleService) : RadEndpoint<GetSampleRequest, GetSampleResponse, GetSampleMapper>
{
public override void Configure()
{
Get("/samples/{id}")
.Produces<GetSampleResponse>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status404NotFound)
.ProducesValidationProblem()
.WithDocument(tag: "Sample", desc: "Get Sample by ID");
//Any NET minimal api (RouteHandlerBuilder) configuration works here.
}
public override async Task Handle(GetSampleRequest r, CancellationToken ct)
{
var sample = await sampleService.GetSampleById(r.Id, ct);
if(sample is null)
{
SendNotFound("Sample not found.");
return;
}
Response = Map.FromEntity(sample);
Send();
}
}
Request Model Binding and Validation
- Automatic request model binding from route, query, header, and body using [AsParameters].
- Automatic request model validation execution using FluentValidation
- Simply add your AbstractValidator class that targets your request model type.
public class GetSampleRequest
{
[FromRoute]
public int Id { get; set; }
}
public class GetSampleRequestValidator : AbstractValidator<GetSampleRequest>
{
public GetSampleRequestValidator()
{
RuleFor(e => e.Id).GreaterThan(0);
}
}
public class GetSampleResponse
{
public SampleDto Data { get; set; } = null!;
public string Message { get; set; } = "Sample retrieved successfully";
}
Endpoint Model Mapper
- Assign mappers for conventient access from endpoint
- Makes mapping a first class citizen with the endpoint configuration
- Map manually or with a mapping tool like AutoMapper or Mapster
public class GetExampleMapper : IRadMapper<GetExampleRequest, GetExampleResponse, Example>
{
public GetExampleResponse FromEntity(Example e) => new()
{
Data = new()
{
Id = e.Id,
FirstName = e.FirstName,
LastName = e.LastName
}
};
public Example ToEntity(GetExampleRequest r) => throw new NotImplementedException();
}
Flexibility and Alternate Base Endpoint Class
- Don't like the endpoint base classes? Make your own using the included abstractions.
- Want to use a bare minimum REPR endpoint with Open Union Types? Go for it.
- Need to create a super specialized edge case endpoint with pure minimal api endpoint? No problem.
//Code samples coming soon
Integration Testing
- Strongly typed "Routeless" HttpClient extensions
- Reduced maintenance with automatic endpoint route discovery and request model binding
- Easy to navigate to endpoint code from test
- Consistent and convenient response assertions for HttpResponse and ProblemDetails using FluentAssertions
- Detailed exception messages so you dig less to find test issues.
[Fact]
public async void When_RequestValid_ReturnsSuccess()
{
//Arrange
var request = new GetSampleRequest { Id = 1 };
//Act
var r = await f.Client.GetAsync<GetSampleEndpoint, GetSampleRequest, GetSampleResponse>(request);
//Assert
r.Should()
.BeSuccessful<GetSampleResponse>()
.WithStatusCode(HttpStatusCode.OK);
}
Unit Testing
In many cases, I recommend Integration testing as you can get more value from the test. However, in some tricky situations, unit testing could be the better choice.
- EndpointFactory class designed to created a unit testable Endpoint instance. (Install the
- Ability to mock the built-in ILogger, IHttpContextProvider, and IWebHostBuilders
- Ability to unit test endpoints with mappers
- Note: Use Integration tests if you want to test with your abstract fluent validators.
- Example: FactoryTestEndpoint and UnitTestFactoryTests
[Fact]
public async Task When_CreateEndpoint_WithCustomLogger_ShouldUseProvidedLogger()
{
// Arrange
var request = new TestRequest { TestProperty = 10 };
var mockLogger = Substitute.For<ILogger<TestEndpoint>>();
var endpoint = EndpointFactory.CreateEndpoint(
logger: mockLogger,
constructorArgs: []);
// Act
await endpoint.Handle(request, CancellationToken.None);
// Assert
mockLogger.Received(1).Log(
LogLevel.Information,
Arg.Any<EventId>(),
Arg.Is<object>(o => o.ToString().Contains("TestProperty:10")),
Arg.Any<Exception>(),
Arg.Any<Func<object, Exception?, string>>());
}
CLI For Scaffolding
- Scaffold multiple new endpoints very quickly.
- Import a set of endoints using a JSON definition.
- Full parameter support for 1 line endpoint creation.
Endpoint Wizard
<img src="https://github.com/MetalHexx/RadEndpoints/assets/9291740/8782c1e9-ef40-4c0b-9b1c-dc9f96ae3826" width="60%" height="60%" alt="Description of Image"/>
JSON definition
[
{
"BaseNamepace": "Demo.Api.Endpoints",
"ResourceName": "User",
"Verb": "Get",
"EndpointName": "GetUser",
"Path": "/users/{id}",
"Entity": "User",
"Tag": "User",
"Description": "Get User by ID",
"WithMapper": true
},
{
"BaseNamepace": "Demo.Api.Endpoints",
"ResourceName": "User",
"Verb": "Post",
"EndpointName": "CreateUser",
"Path": "/users",
"Entity": "User",
"Tag": "User",
"Description": "Create a new User",
"WithMapper": true
}
...other endpoints.
]
Bulk JSON Import
<img src="https://github.com/MetalHexx/RadEndpoints/assets/9291740/eafc6050-9afd-4c4b-a844-a6b1033b9f98" width="60%" height="60%" alt="Description of Image"/>
📦 Installation
You can install the RadEndpoints packages from NuGet:
Install via .NET CLI:
dotnet add package RadEndpoints
dotnet add package RadEndpoints.Cli
dotnet add package RadEndpoints.Testing
Coming Soon:
- Project templates
- Observability Tooling
- Additional code coverage
- Documentation / How Tos
Credits
- FastEndpoints -- as many of the ideas from that project inspired this one.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- FluentAssertions (>= 6.12.0)
- NSubstitute (>= 5.3.0)
- RadEndpoints (>= 1.0.0-alpha.19)
- xunit.extensibility.core (>= 2.4.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
1.0.0-alpha.19 | 37 | 9/1/2025 |
1.0.0-alpha.18 | 252 | 7/26/2025 |
1.0.0-alpha.17 | 212 | 5/16/2025 |
1.0.0-alpha.16 | 137 | 5/8/2025 |
1.0.0-alpha.15 | 134 | 5/7/2025 |
1.0.0-alpha.14 | 92 | 5/4/2025 |
1.0.0-alpha.13 | 485 | 1/30/2025 |
1.0.0-alpha.12 | 91 | 12/12/2024 |
1.0.0-alpha.11 | 87 | 11/26/2024 |
1.0.0-alpha.10 | 83 | 11/26/2024 |
1.0.0-alpha.9 | 74 | 11/15/2024 |
1.0.0-alpha.8 | 239 | 4/29/2024 |
1.0.0-alpha.5 | 90 | 4/28/2024 |
1.0.0-alpha.4 | 110 | 2/24/2024 |
1.0.0-alpha.3 | 94 | 2/23/2024 |
1.0.0-alpha.2 | 96 | 2/21/2024 |
1.0.0-alpha.1 | 89 | 2/21/2024 |