MI
migration-unit-testing
|
Install
mkdir -p .claude/skills/migration-unit-testing && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15876" && unzip -o skill.zip -d .claude/skills/migration-unit-testing && rm skill.zipInstalls to .claude/skills/migration-unit-testing
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Unit testing patterns for validating migrated applications. **Use when:** Creating tests to verify migration correctness and prevent regressions. **Triggers on:** Test creation requests, validation phase, post-migration verification. **Covers:** xUnit/NUnit for .NET, JUnit 5 for Java, mocking strategies, test organization patterns.333 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
About this skill
Migration Unit Testing Skill
Use this skill when creating unit tests to validate migrated applications work correctly after modernization.
When to Use This Skill
- Creating tests to validate migration correctness
- Building test suites for migrated .NET or Java applications
- Implementing equivalence testing (old vs new behavior)
- Setting up mocking for external dependencies
- Creating regression tests for business logic
- Establishing test coverage baselines
Testing Strategy for Migrated Applications
Priority Order
- Business Logic - Critical calculations, validations, workflows
- Data Access - Repository operations, query correctness
- API Endpoints - Request/response contracts, status codes
- Authentication/Authorization - Security flows
- Integrations - External service interactions
- UI Components - View models, presentation logic
Coverage Goals
| Code Area | Minimum Coverage | Target Coverage |
|---|---|---|
| Business logic | 80% | 90%+ |
| API controllers | 70% | 85% |
| Data access | 60% | 80% |
| Utilities/helpers | 70% | 90% |
.NET Testing Patterns (xUnit)
Project Setup
<!-- Tests.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.6.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.0" />
</ItemGroup>
</Project>
Test Naming Convention
MethodName_Scenario_ExpectedBehavior
Examples:
GetUser_WithValidId_ReturnsUserCreateOrder_WithInvalidItems_ThrowsValidationExceptionCalculateDiscount_WhenTotalExceeds100_Returns10Percent
Service Layer Test
public class UserServiceTests
{
private readonly Mock<IUserRepository> _repositoryMock;
private readonly Mock<ILogger<UserService>> _loggerMock;
private readonly UserService _sut; // System Under Test
public UserServiceTests()
{
_repositoryMock = new Mock<IUserRepository>();
_loggerMock = new Mock<ILogger<UserService>>();
_sut = new UserService(_repositoryMock.Object, _loggerMock.Object);
}
[Fact]
public async Task GetByIdAsync_WithExistingUser_ReturnsUser()
{
// Arrange
var expectedUser = new User { Id = 1, Name = "John Doe", Email = "[email protected]" };
_repositoryMock
.Setup(r => r.GetByIdAsync(1))
.ReturnsAsync(expectedUser);
// Act
var result = await _sut.GetByIdAsync(1);
// Assert
result.Should().NotBeNull();
result.Should().BeEquivalentTo(expectedUser);
}
[Fact]
public async Task GetByIdAsync_WithNonExistingUser_ReturnsNull()
{
// Arrange
_repositoryMock
.Setup(r => r.GetByIdAsync(It.IsAny<int>()))
.ReturnsAsync((User?)null);
// Act
var result = await _sut.GetByIdAsync(999);
// Assert
result.Should().BeNull();
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData(null)]
public async Task CreateAsync_WithInvalidName_ThrowsArgumentException(string? invalidName)
{
// Arrange
var dto = new CreateUserDto(invalidName!, "[email protected]");
// Act
var act = () => _sut.CreateAsync(dto);
// Assert
await act.Should().ThrowAsync<ArgumentException>()
.WithMessage("*name*");
}
}
Controller Integration Test
public class UsersControllerTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
private readonly WebApplicationFactory<Program> _factory;
public UsersControllerTests(WebApplicationFactory<Program> factory)
{
_factory = factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
// Replace real DB with in-memory
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
if (descriptor != null)
services.Remove(descriptor);
services.AddDbContext<AppDbContext>(options =>
options.UseInMemoryDatabase("TestDb"));
});
});
_client = _factory.CreateClient();
}
[Fact]
public async Task GetUsers_ReturnsSuccessAndCorrectContentType()
{
// Act
var response = await _client.GetAsync("/api/users");
// Assert
response.EnsureSuccessStatusCode();
response.Content.Headers.ContentType?.MediaType.Should().Be("application/json");
}
[Fact]
public async Task GetUser_WithInvalidId_ReturnsNotFound()
{
// Act
var response = await _client.GetAsync("/api/users/99999");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
[Fact]
public async Task CreateUser_WithValidData_ReturnsCreatedWithLocation()
{
// Arrange
var newUser = new { Name = "Jane Doe", Email = "[email protected]" };
var content = new StringContent(
JsonSerializer.Serialize(newUser),
Encoding.UTF8,
"application/json");
// Act
var response = await _client.PostAsync("/api/users", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Created);
response.Headers.Location.Should().NotBeNull();
}
}
Database Test with In-Memory Provider
public class UserRepositoryTests : IDisposable
{
private readonly AppDbContext _context;
private readonly UserRepository _sut;
public UserRepositoryTests()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
_context = new AppDbContext(options);
_sut = new UserRepository(_context);
}
[Fact]
public async Task AddAsync_AddsUserToDatabase()
{
// Arrange
var user = new User { Name = "Test User", Email = "[email protected]" };
// Act
await _sut.AddAsync(user);
await _context.SaveChangesAsync();
// Assert
var savedUser = await _context.Users.FirstOrDefaultAsync(u => u.Email == "[email protected]");
savedUser.Should().NotBeNull();
savedUser!.Name.Should().Be("Test User");
}
public void Dispose() => _context.Dispose();
}
Java Testing Patterns (JUnit 5)
Project Setup (pom.xml)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Service Layer Test
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
@DisplayName("Should return user when valid ID provided")
void findById_WithValidId_ReturnsUser() {
// Given
User expectedUser = new User(1L, "John Doe", "[email protected]");
when(userRepository.findById(1L)).thenReturn(Optional.of(expectedUser));
// When
Optional<User> result = userService.findById(1L);
// Then
assertThat(result)
.isPresent()
.hasValueSatisfying(user -> {
assertThat(user.getName()).isEqualTo("John Doe");
assertThat(user.getEmail()).isEqualTo("[email protected]");
});
}
@Test
@DisplayName("Should return empty when user not found")
void findById_WithNonExistingId_ReturnsEmpty() {
// Given
when(userRepository.findById(anyLong())).thenReturn(Optional.empty());
// When
Optional<User> result = userService.findById(999L);
// Then
assertThat(result).isEmpty();
}
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {" ", " "})
@DisplayName("Should throw exception for invalid name")
void create_WithInvalidName_ThrowsException(String invalidName) {
// Given
CreateUserDto dto = new CreateUserDto(invalidName, "[email protected]");
// When/Then
assertThatThrownBy(() -> userService.create(dto))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("name");
}
}
Controller Integration Test
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
@DisplayName("GET /api/users sho
---
*Content truncated.*