backend-testing

Test Naming Standards

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "backend-testing" with this command: npx skills add exceptionless/exceptionless/exceptionless-exceptionless-backend-testing

Backend Testing

Test Naming Standards

Follow Microsoft's unit testing best practices:

Pattern: MethodUnderTest_Scenario_ExpectedBehavior

  • MethodUnderTest — The actual method on the class being tested, not necessarily the entry point you call. For example, when testing ObjectToInferredTypesConverter , use Read (the converter's method) even though you invoke it via _serializer.Deserialize() .

  • Scenario — The input, state, or condition being tested.

  • ExpectedBehavior — What the method should do or return.

// ✅ Good: Clear method, scenario, and expected behavior [Fact] public void GetValue_JObjectWithUserInfo_ReturnsTypedUserInfo() { }

[Fact] public void GetValue_MissingKey_ThrowsKeyNotFoundException() { }

[Fact] public void Read_EmptyArray_ReturnsEmptyList() { } // Tests ObjectToInferredTypesConverter.Read()

[Fact] public void Write_NestedDictionary_SerializesCorrectly() { } // Tests ObjectToInferredTypesConverter.Write()

[Fact] public async Task PostEvent_WithValidPayload_ReturnsAccepted() { }

// ❌ Bad: Vague or missing context [Fact] public void TestGetValue() { }

[Fact] public void CanGetValue() { }

[Fact] public void Deserialize_EmptyArray_ReturnsEmptyList() { } // Wrong: Deserialize is the entry point, not the method under test

Running Tests

All tests

dotnet test

By test name

dotnet test --filter "FullyQualifiedName~PostEvent_WithValidPayload_ReturnsAccepted"

By class name

dotnet test --filter "ClassName~EventControllerTests"

Test Folder Structure

Tests mirror the source structure:

tests/Exceptionless.Tests/ ├── AppWebHostFactory.cs # WebApplicationFactory for integration tests ├── IntegrationTestsBase.cs # Base class for integration tests ├── TestWithServices.cs # Base class for unit tests with DI ├── Controllers/ # API controller tests ├── Jobs/ # Job tests ├── Repositories/ # Repository tests ├── Services/ # Service tests ├── Utility/ # Test data builders │ ├── AppSendBuilder.cs # Fluent HTTP request builder │ ├── DataBuilder.cs # Test data creation │ ├── EventData.cs │ ├── OrganizationData.cs │ ├── ProjectData.cs │ ├── ProxyTimeProvider.cs # Time manipulation │ └── ... └── Validation/ # Validator tests

Integration Test Base Pattern

Inherit from IntegrationTestsBase which uses Foundatio.Xunit's TestWithLoggingBase :

// From tests/Exceptionless.Tests/IntegrationTestsBase.cs public abstract class IntegrationTestsBase : TestWithLoggingBase, IAsyncLifetime, IClassFixture<AppWebHostFactory> { protected readonly TestServer _server; private readonly ProxyTimeProvider _timeProvider;

public IntegrationTestsBase(ITestOutputHelper output, AppWebHostFactory factory) : base(output)
{
    _server = factory.Server;
    _timeProvider = GetService&#x3C;ProxyTimeProvider>();
}

protected TService GetService&#x3C;TService>() where TService : notnull
    => ServiceProvider.GetRequiredService&#x3C;TService>();

protected FluentClient CreateFluentClient()
{
    var settings = GetService&#x3C;JsonSerializerSettings>();
    return new FluentClient(CreateHttpClient(), new NewtonsoftJsonSerializer(settings));
}

}

Real Test Example

From EventControllerTests.cs:

public class EventControllerTests : IntegrationTestsBase { private readonly IEventRepository _eventRepository; private readonly IQueue<EventPost> _eventQueue;

public EventControllerTests(ITestOutputHelper output, AppWebHostFactory factory) : base(output, factory)
{
    _eventRepository = GetService&#x3C;IEventRepository>();
    _eventQueue = GetService&#x3C;IQueue&#x3C;EventPost>>();
}

[Fact]
public async Task PostEvent_WithValidPayload_EnqueuesAndProcessesEvent()
{
    // Arrange
    /* language=json */
    const string json = """{"message":"test","reference_id":"TestReferenceId"}""";

    // Act
    await SendRequestAsync(r => r
        .Post()
        .AsTestOrganizationClientUser()
        .AppendPath("events")
        .Content(json, "application/json")
        .StatusCodeShouldBeAccepted()
    );

    var stats = await _eventQueue.GetQueueStatsAsync();
    Assert.Equal(1, stats.Enqueued);

    var processEventsJob = GetService&#x3C;EventPostsJob>();
    await processEventsJob.RunAsync();
    await RefreshDataAsync();

    // Assert
    var events = await _eventRepository.GetAllAsync();
    var ev = events.Documents.Single(e => e.Type == Event.KnownTypes.Log);
    Assert.Equal("test", ev.Message);
}

}

Test Structure (Arrange-Act-Assert)

Use clear // Arrange , // Act , // Assert comments for readability:

[Fact] public void GetValue_DirectUserInfoType_ReturnsTypedValue() { // Arrange var userInfo = new UserInfo("test@example.com", "Test User"); var data = new DataDictionary { { "user", userInfo } };

// Act
var result = data.GetValue&#x3C;UserInfo>("user", _jsonOptions);

// Assert
Assert.NotNull(result);
Assert.Equal("test@example.com", result.Identity);
Assert.Equal("Test User", result.Name);

}

JSON String Literals

Use /* language=json */ comment before JSON strings for IDE syntax highlighting and validation:

[Fact] public void GetValue_JsonStringWithError_ReturnsTypedError() { // Arrange /* language=json */ const string json = """{"message":"Test error","type":"System.Exception"}"""; var data = new DataDictionary { { "@error", json } };

// Act
var result = data.GetValue&#x3C;Error>("@error", _jsonOptions);

// Assert
Assert.NotNull(result);
Assert.Equal("Test error", result.Message);

}

FluentClient Pattern

Use SendRequestAsync with AppSendBuilder for HTTP testing:

await SendRequestAsync(r => r .Post() .AsTestOrganizationUser() // Basic auth with test user .AppendPath("organizations") .Content(new NewOrganization { Name = "Test" }) .StatusCodeShouldBeCreated() );

// Available auth helpers r.AsGlobalAdminUser() // TEST_USER_EMAIL r.AsTestOrganizationUser() // TEST_ORG_USER_EMAIL r.AsFreeOrganizationUser() // FREE_USER_EMAIL r.AsTestOrganizationClientUser() // API key bearer token

Test Data Builders

Create test data with CreateDataAsync :

var (stacks, events) = await CreateDataAsync(b => b .Event() .TestProject() .Type(Event.KnownTypes.Error) .Message("Test error"));

Assert.Single(stacks); Assert.Single(events);

Time Manipulation with ProxyTimeProvider

NOT ISystemClock — use .NET 8+ TimeProvider with ProxyTimeProvider :

// Access via protected property protected ProxyTimeProvider TimeProvider => _timeProvider;

// Advance time TimeProvider.Advance(TimeSpan.FromHours(1));

// Set specific time TimeProvider.SetUtcNow(new DateTimeOffset(2024, 1, 15, 12, 0, 0, TimeSpan.Zero));

// Restore to system time TimeProvider.Restore();

Registered in test services:

services.ReplaceSingleton<TimeProvider>(_ => new ProxyTimeProvider());

Test Principles

  • TDD workflow — When fixing bugs or adding features, write a failing test first

  • Use real serializer — Tests use the same JSON serializer as production

  • Use real time provider — Manipulate via ProxyTimeProvider when needed

  • Refresh after writes — Call RefreshDataAsync() after database changes

  • Clean state — ResetDataAsync() clears data between tests

Foundatio.Xunit

Base class provides logging integration:

using Foundatio.Xunit;

public class MyTests : TestWithLoggingBase { public MyTests(ITestOutputHelper output) : base(output) { Log.DefaultLogLevel = LogLevel.Information; } }

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

backend-testing

When to use this skill

Repository Source
General

shadcn-svelte components

No summary provided by upstream source.

Repository SourceNeeds Review
General

tanstack-form

No summary provided by upstream source.

Repository SourceNeeds Review