From ABP Sensei
Writes integration tests for ABP Framework v10.4 using TestBase classes, SQLite in-memory, Shouldly, NSubstitute, and data seeding. Activates on test-related requests.
How this skill is triggered — by the user, by Claude, or both
Slash command
/abp-sensei:abp-testingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
ABP Framework v10.4 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.
ABP Framework v10.4 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.
| 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>().
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);
});
}
}
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");
}
}
// 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);
}
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");
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);
}
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAlwaysAllowAuthorization();
}
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);
}
// 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
}
CodeShould_X_When_Y namingnpx claudepluginhub burakdmir/abp-skills --plugin abp-senseiProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.