‹ 首页

abp-testing

@burakdmir · 收录于 昨天 · 上游提交 2 周前

ABP Framework v10.x (10.4/10.5) testing: integration tests, *TestBase classes (Domain/Application/EntityFrameworkCore), SQLite in-memory, Shouldly, NSubstitute, data seeding, CurrentUser/CurrentTenant.Change, AddAlwaysAllowAuthorization. Use when you need to write unit/integration tests in ABP.

适合你,如果正在基于 ABP Framework 开发和测试应用

/ 通过 npx 安装 校验哈希
npx oh-my-skill add burakdmir/abp-skills/abp-testing
/ 通过 bash 安装
curl -fsSL https://oh-my-skill.com/install.sh | bash -s -- burakdmir/abp-skills/abp-testing
/ 已经装过?验证本机副本,不用重装
npx oh-my-skill verify burakdmir/abp-skills/abp-testing
安装目标可用 --agent / --scope 或 --to 明确指定;省略时只会在唯一已存在的 agent 目录上自动选择,零命中或多命中会停止并提示。content_hash 缺失或不一致均拒装。
13GitHub stars
~1K上下文体积 · 单文件
索引托管

怎么用

商店整理自技能原文 · 版本 ce71259 · 表述以原文为准
它做什么

装上后,Claude 专注于为 ABP Framework v10.x 编写集成测试。它会生成测试基类、使用 SQLite 内存数据库、Shouldly 断言、NSubstitute 模拟外部服务、数据种子、禁用授权及切换当前用户/租户的代码。

什么时候触发

当你要求编写 ABP 单元测试、集成测试或使用 TestBase、数据种子、授权测试等关键词时触发。

装好后可以这样说
生成基于 ApplicationTestBase 的测试类。
生成领域服务测试,验证 BusinessException。
技能原文 SKILL.md作者撰写 · MIT · ce71259

ABP Framework — Testing

ABP Framework v10.x (10.4/10.5) testing guide. ABP prefers integration tests over unit tests: they run with real services + a real (SQLite in-memory) database, and internal services are not mocked.

Trigger
  • "write ABP test"
  • "ABP integration test"
  • "ABP unit test"
  • "ABP TestBase"
  • "ABP test data seed"
  • "ABP authorization test"
  • "ABP Shouldly / NSubstitute"
Test Projects and Base Classes

| Project | Scope | Base Class | |---|---|---| | *.Domain.Tests | Domain logic, entity, domain service | *DomainTestBase | | *.Application.Tests | Application services | *ApplicationTestBase | | *.EntityFrameworkCore.Tests | Repository implementations | *EntityFrameworkCoreTestBase |

Each test gets a fresh database instance. Services are resolved via GetRequiredService<T>().

Application Service Test
public class BookAppService_Tests : MyProjectApplicationTestBase
{
    private readonly IBookAppService _bookAppService;

    public BookAppService_Tests()
    {
        _bookAppService = GetRequiredService<IBookAppService>();
    }

    [Fact]
    public async Task Should_Create_Book()
    {
        // Arrange
        var input = new CreateBookDto { Name = "New Book", Price = 19.99m };

        // Act
        var result = await _bookAppService.CreateAsync(input);

        // Assert
        result.Id.ShouldNotBe(Guid.Empty);
        result.Name.ShouldBe("New Book");
    }

    [Fact]
    public async Task Should_Not_Create_Book_With_Invalid_Name()
    {
        var input = new CreateBookDto { Name = "", Price = 10m };
        await Should.ThrowAsync<AbpValidationException>(async () =>
        {
            await _bookAppService.CreateAsync(input);
        });
    }
}
Domain Service Test
public class BookManager_Tests : MyProjectDomainTestBase
{
    private readonly BookManager _bookManager;

    public BookManager_Tests()
    {
        _bookManager = GetRequiredService<BookManager>();
    }

    [Fact]
    public async Task Should_Not_Allow_Duplicate_Book_Name()
    {
        await _bookManager.CreateAsync("Existing Book", 10m);

        var exception = await Should.ThrowAsync<BusinessException>(async () =>
        {
            await _bookManager.CreateAsync("Existing Book", 20m);
        });

        exception.Code.ShouldBe("MyProject:BookNameAlreadyExists");
    }
}
Naming & AAA
// Pattern: Should_ExpectedBehavior_When_Condition
public async Task Should_Throw_BusinessException_When_Name_Already_Exists() { }

[Fact]
public async Task Should_Update_Book_Price()
{
    // Arrange
    var bookId = await CreateTestBookAsync();
    // Act
    var result = await _bookAppService.UpdateAsync(bookId, new UpdateBookDto { Price = 39.99m });
    // Assert
    result.Price.ShouldBe(39.99m);
}
Assertions (Shouldly)

ABP uses the Shouldly library:

result.ShouldNotBeNull();
result.Name.ShouldBe("Expected");
result.Price.ShouldBeGreaterThan(0);
result.Items.ShouldContain(x => x.Id == expectedId);
result.Items.ShouldBeEmpty();

// Exception
var ex = await Should.ThrowAsync<BusinessException>(async () => await _service.DoAsync());
ex.Code.ShouldBe("MyProject:ErrorCode");
Test Data Seeding
public class MyProjectTestDataSeedContributor : IDataSeedContributor, ITransientDependency
{
    public static readonly Guid TestBookId = Guid.Parse("....");
    private readonly IBookRepository _bookRepository;

    public MyProjectTestDataSeedContributor(IBookRepository bookRepository)
        => _bookRepository = bookRepository;

    public async Task SeedAsync(DataSeedContext context)
    {
        await _bookRepository.InsertAsync(
            new Book(TestBookId, "Test Book", 19.99m, Guid.Empty), autoSave: true);
    }
}
Disabling Authorization in Tests
public override void ConfigureServices(ServiceConfigurationContext context)
{
    context.Services.AddAlwaysAllowAuthorization();
}
Mocking External Services (NSubstitute)

Don't mock internal ABP services — only external dependencies:

public override void ConfigureServices(ServiceConfigurationContext context)
{
    var emailSender = Substitute.For<IEmailSender>();
    emailSender.SendAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>())
        .Returns(Task.CompletedTask);
    context.Services.AddSingleton(emailSender);
}
Testing with a Specific User / Tenant
// User
using (CurrentUser.Change(TestData.UserId))
{
    var result = await _bookAppService.GetMyBooksAsync();
    result.Items.ShouldAllBe(b => b.CreatorId == TestData.UserId);
}

// Tenant
using (CurrentTenant.Change(TestData.TenantId))
{
    var result = await _bookAppService.GetListAsync(new GetBookListDto());
    // Results are filtered by tenant
}
Best Practices
  1. Prefer integration tests — real services + SQLite in-memory, don't mock internal services
  2. Keep each test independent — don't share state between tests
  3. Meaningful test data — use a seed contributor for shared data
  4. Test edge cases and error conditions — verify the exception Code
  5. Focus on a single behaviorShould_X_When_Y naming
  6. Don't test the framework internals
Related
  • [DDD](../abp-ddd/SKILL.md) — entity/domain/application service design
  • [Authorization](../abp-authorization/SKILL.md) — AddAlwaysAllowAuthorization, CurrentUser
  • [Multi-Tenancy](../abp-multitenancy/SKILL.md) — CurrentTenant.Change
  • [Development Flow](../abp-development-flow/SKILL.md) — the final step of the feature flow: testing
  • ABP Docs: https://abp.io/docs/latest/testing
按 MIT 许可原样转载,未经改动 · 在 GitHub 查看 →

评论

登录即可评论;带「已验证安装」的,是发布者名下有本店的安装或持有记录。