ApiTestFramework 0.1.3
dotnet add package ApiTestFramework --version 0.1.3
NuGet\Install-Package ApiTestFramework -Version 0.1.3
<PackageReference Include="ApiTestFramework" Version="0.1.3" />
<PackageVersion Include="ApiTestFramework" Version="0.1.3" />
<PackageReference Include="ApiTestFramework" />
paket add ApiTestFramework --version 0.1.3
#r "nuget: ApiTestFramework, 0.1.3"
#:package ApiTestFramework@0.1.3
#addin nuget:?package=ApiTestFramework&version=0.1.3
#tool nuget:?package=ApiTestFramework&version=0.1.3
ApiTestFramework
Lightweight, pluggable test framework utilities for exercising real HTTP APIs in .NET 8.0 solutions. It ships an ApiClient (with JWT support), data-source readers, logging abstractions, and optional reporting hooks so you can focus on authoring clear, reliable API tests.
Install (NuGet)
Add the package to any test or integration project (xUnit, NUnit, MSTest, console harness, etc.). Pin for reproducibility:
dotnet add package ApiTestFramework --version 0.1.3
Or pull the latest available:
dotnet add package ApiTestFramework
Quick Start
using ApiClient; // Namespace of the ApiClient in the package
var baseUrl = "https://api.example.com"; // Your API root
var token = "eyJhbGci..."; // JWT or bearer token
var client = new ApiClient.ApiClient(baseUrl, token);
var user = await client.GetAsync<dynamic>("/users/1");
Console.WriteLine(user);
Key async methods:
GetAsync<T>(string path)PostAsync<T>(string path, object body)PutAsync<T>(string path, object body)DeleteAsync(string path)
Example PUT & DELETE:
// Update user
var updated = await client.PutAsync<dynamic>("/users/1", new { name = "Updated" });
// Delete user
await client.DeleteAsync("/users/1");
Real API Integration Test Example (xUnit)
Integration tests against a live server are powerful but slow & environment-dependent. Keep them opt-in by skipping them unless required. Example below uses environment variables so CI/secrets are not hard-coded:
using System;
using System.Threading.Tasks;
using Xunit;
using ApiClient;
namespace ApiTests
{
// Disabled by default; enable by setting TEST_API_BASE_URL and TEST_API_TOKEN or removing Skip.
public class ApiClientRealIntegrationTests
{
private const string EnvBaseUrl = "TEST_API_BASE_URL";
private const string EnvToken = "TEST_API_TOKEN";
[Fact(Skip = "Integration test - enable by setting TEST_API_BASE_URL and TEST_API_TOKEN or removing Skip")]
public async Task GetAsync_Integration_FetchesSingleUser_RealServer()
{
var baseUrl = Environment.GetEnvironmentVariable(EnvBaseUrl);
var token = Environment.GetEnvironmentVariable(EnvToken);
if (string.IsNullOrEmpty(baseUrl) || string.IsNullOrEmpty(token))
throw new InvalidOperationException($"Integration test requires environment variables {EnvBaseUrl} and {EnvToken} to be set.");
var apiClient = new ApiClient.ApiClient(baseUrl, token);
var user = await apiClient.GetAsync<dynamic>("/users/1");
Assert.NotNull(user);
}
[Fact(Skip = "Integration test - enable by setting TEST_API_BASE_URL and TEST_API_TOKEN or removing Skip")]
public async Task PostAsync_Integration_CreatesUser_RealServer()
{
var baseUrl = Environment.GetEnvironmentVariable(EnvBaseUrl);
var token = Environment.GetEnvironmentVariable(EnvToken);
if (string.IsNullOrEmpty(baseUrl) || string.IsNullOrEmpty(token))
throw new InvalidOperationException($"Integration test requires environment variables {EnvBaseUrl} and {EnvToken} to be set.");
var apiClient = new ApiClient.ApiClient(baseUrl, token);
var payload = new { name = "Integration Test User", email = "itest+user@example.com" };
var created = await apiClient.PostAsync<dynamic>("/users", payload);
Assert.NotNull(created);
}
}
}
Enabling Integration Tests (Windows PowerShell)
$env:TEST_API_BASE_URL = "https://api.yourserver.com"
$env:TEST_API_TOKEN = "your-jwt-or-bearer-token"
dotnet test
For a one-off invocation:
TEST_API_BASE_URL="https://api.yourserver.com"; TEST_API_TOKEN="token"; dotnet test
Guidelines
- Keep unit tests fast & isolated; only mark a few critical real-path scenarios as integration.
- Fail fast if env vars absent (as shown) for clear developer feedback.
- Consider separate CI job/stage for live API tests.
Package Contents Overview
| Folder | Purpose |
|---|---|
ApiClient/ |
Core HTTP client + interface for DI (IApiClient). |
Authentication/ |
JwtAuthenticator helper for bearer/JWT handling. |
DataReaders/ |
CSV / JSON / DB readers enabling data-driven tests. |
Logger/ |
File logger & AppInsights logger abstractions. |
Reporting/ |
Hook point for test reporting (e.g., Allure). |
Logging
Use FileTestLogger for lightweight run diagnostics; plug in AppInsightsLogger when instrumentation key & configuration are available. Wrap logging via ITestLogger for dependency injection and test isolation.
File Logger Example
using Logger;
var log = new FileTestLogger(Path.Combine(AppContext.BaseDirectory, "test-run.log"));
log.LogInfo("Starting user API tests");
try
{
var user = await client.GetAsync<dynamic>("/users/1");
log.LogInfo($"Fetched user: {user}");
}
catch (Exception ex)
{
log.LogError($"Failure fetching user: {ex.Message}");
}
AppInsights Logger Example
using Logger;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
var telemetryConfig = TelemetryConfiguration.CreateDefault();
telemetryConfig.ConnectionString = "InstrumentationKey=YOUR_KEY"; // or full connection string
var telemetryClient = new TelemetryClient(telemetryConfig);
var aiLogger = new AppInsightsLogger(telemetryClient);
aiLogger.LogInfo("Test run started");
Recommendation: Abstract logger behind ITestLogger in test fixtures for easier swapping.
Data-Driven Tests
Readers expose simple APIs to fetch structured rows that can feed [Theory] test data. Implement a POCO matching your CSV/JSON schema.
CSV Reader Example
using DataReaders;
public record UserRow(int Id, string Email, string Name);
var csvReader = new CsvDataReader<UserRow>();
IEnumerable<UserRow> users = csvReader.ReadData("TestData/users.csv");
foreach (var u in users)
{
Assert.False(string.IsNullOrWhiteSpace(u.Email));
}
JSON Reader Example (array of objects)
using DataReaders;
public record Product(int Id, string Sku, decimal Price);
var jsonReader = new JsonDataReader<Product>();
IEnumerable<Product> products = jsonReader.ReadData("TestData/products.json");
Assert.NotEmpty(products);
Database Reader Example (manual mapping placeholder)
using DataReaders;
// Define a model you will populate manually
public class AuditEntry { public int Id { get; set; } public string Event { get; set; } }
// Initialize with connection string & query
var auditReader = new DbDataReader<AuditEntry>(connectionString: "Server=.;Database=AppDb;Trusted_Connection=True;",
query: "SELECT Id, Event FROM Audit ORDER BY Id DESC");
// NOTE: Current implementation returns empty list; extend mapping inside while(reader.Read()) loop.
IEnumerable<AuditEntry> audits = auditReader.ReadData();
Mapping Guidance: In DbDataReader<T> modify the loop to construct instances of T and add them to the result list.
Using with xUnit [Theory]
public static IEnumerable<object[]> UserData()
{
var csv = new CsvDataReader<UserRow>();
return csv.ReadData("TestData/users.csv").Select(u => new object[] { u });
}
[Theory]
[MemberData(nameof(UserData))]
public void Email_Is_Valid(UserRow user)
{
Assert.Contains('@', user.Email);
}
Error Handling Strategy
Non-success HTTP responses surface as HttpRequestException including status/body for quick diagnosis. Expect/Assert error flows by capturing the exception and inspecting StatusCode or serialized problem details.
Publishing (dotnet pack & push)
Create a package:
dotnet pack .\ApiTestFramework\ApiTestFramework.csproj -c Release -o .\nupkg
Push to feed:
dotnet nuget push .\nupkg\ApiTestFramework.*.nupkg --source "<FeedUrl>" --api-key "<API_KEY>"
Include symbols (.snupkg) for better debugging in consuming solutions.
Optional Reporting (Allure)
Framework keeps reporting dependencies out to remain lean. If you need rich step/result reports, add Allure packages in the consuming test project and wrap lifecycle events. (Previous extended example retained in version history.)
Roadmap Ideas
- Polly-based retries / circuit breaker for transient faults.
- PATCH / DELETE convenience methods.
- Token auto-refresh & expiry buffer in
JwtAuthenticator. - Extended structured logging (correlation IDs, timing).
- Advanced data-binding helpers for complex CSV schemas.
Troubleshooting
- 401/403: Verify the token env var is set and unexpired.
- Serialization issues: Confirm JSON casing settings; payload might be a
JsonElement—inspect properties directly. - Timeouts: Increase client timeout or check network connectivity.
- Empty data rows: Validate file path & ensure build
CopyToOutputDirectoryif needed.
License
MIT (adjust as organizational policy requires).
Contributing
- Fork & create feature branch.
- Add/adjust code (keep changes focused & small).
- Include XML doc comments for public members.
- Update this README if new public features ship.
- Open PR referencing issue or enhancement request.
Versioning
Semantic versioning (MAJOR.MINOR.PATCH). Breaking API changes increment MAJOR; additive backwards-compatible features increment MINOR; fixes/security patches increment PATCH.
At a Glance
| Feature | Status |
|---|---|
| Core HTTP Client | Stable |
| JWT Auth Helper | Stable |
| CSV/JSON Readers | Beta |
| DB Reader | Beta |
| File Logger | Stable |
| AppInsights Log | Planned (config dependent) |
| Reporting Hooks | Extensible |
Changelog
- 0.1.3: Added PUT/DELETE examples, logger samples (file/AppInsights), CSV/JSON/DB reader usage, xUnit Theory pattern.
- 0.1.2: Updated README content and version; added changelog.
- 0.1.1: Refined README, installation guidance, integration test examples.
- 0.1.0: Initial release with core client, readers, logging abstractions.
Happy testing!
| 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
- CsvHelper (>= 30.0.1)
- Microsoft.ApplicationInsights (>= 2.21.0)
- Microsoft.Data.SqlClient (>= 5.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.