Skip to content
Back to skills

Tasks Test Generation

ASecurity

Use for DEVELOPER-focused unit/integration test code with xUnit (C#) and Jest (Angular) patterns. Provides ready-to-use test templates for commands, queries, entities, and components. Best for implementing actual test code. NOT for QA test specifications (use test-generation instead).

  • 18 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 5, 2026
testingtypescriptc#bashangularexpresstestingapifrontendbackend

Works with

  • api

Security analysis

A100/100

Scanned September 5, 2026

npx -y skills add CongDon1207/AGENTS.md --skill tasks-test-generation --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Tasks Test Generation?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Tasks Test Generation
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/congdon1207-tasks-test-generation-agents-md/badge)](https://www.skillsdirectory.com/skills/congdon1207-tasks-test-generation-agents-md)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: test-generation
description: Use for DEVELOPER-focused unit/integration test code with xUnit (C#) and Jest (Angular) patterns. Provides ready-to-use test templates for commands, queries, entities, and components. Best for implementing actual test code. NOT for QA test specifications (use test-generation instead).
allowed-tools: Read, Write, Edit, Grep, Glob, Bash
---

# Test Generation Workflow

## When to Use This Skill

- Creating unit tests for new code
- Adding tests for bug fixes
- Integration test development
- Test coverage improvement

## Pre-Flight Checklist

- [ ] Identify code to test (command, query, entity, component)
- [ ] Find existing test patterns: `grep "Test.*{Feature}" --include="*.cs"`
- [ ] Determine test type (unit, integration, e2e)
- [ ] Identify dependencies to mock

## File Locations

### Backend Tests

```
tests/
└── {Service}.Tests/
    ├── UnitTests/
    │   ├── Commands/
    │   │   └── Save{Entity}CommandTests.cs
    │   ├── Queries/
    │   │   └── Get{Entity}ListQueryTests.cs
    │   └── Entities/
    │       └── {Entity}Tests.cs
    └── IntegrationTests/
        └── {Feature}IntegrationTests.cs
```

### Frontend Tests

```
src/PlatformExampleAppWeb/apps/{app}/src/app/
└── features/
    └── {feature}/
        ├── {feature}.component.spec.ts
        └── {feature}.store.spec.ts
```

## Pattern 1: Command Handler Unit Test

```csharp
public class SaveEmployeeCommandTests
{
    private readonly Mock<IPlatformQueryableRootRepository<Employee, string>> _employeeRepoMock;
    private readonly Mock<IPlatformApplicationRequestContextAccessor> _contextMock;
    private readonly SaveEmployeeCommandHandler _handler;

    public SaveEmployeeCommandTests()
    {
        _employeeRepoMock = new Mock<IPlatformQueryableRootRepository<Employee, string>>();
        _contextMock = new Mock<IPlatformApplicationRequestContextAccessor>();

        // Setup default context
        var requestContext = new Mock<IPlatformApplicationRequestContext>();
        requestContext.Setup(x => x.UserId()).Returns("test-user-id");
        requestContext.Setup(x => x.CurrentCompanyId()).Returns("test-company-id");
        _contextMock.Setup(x => x.Current).Returns(requestContext.Object);

        _handler = new SaveEmployeeCommandHandler(
            Mock.Of<ILoggerFactory>(),
            Mock.Of<IPlatformUnitOfWorkManager>(),
            Mock.Of<IServiceProvider>(),
            Mock.Of<IPlatformRootServiceProvider>(),
            _employeeRepoMock.Object
        );
    }

    [Fact]
    public async Task HandleAsync_CreateEmployee_ReturnsNewEmployee()
    {
        // Arrange
        var command = new SaveEmployeeCommand
        {
            Name = "John Doe",
            Email = "john@example.com"
        };

        _employeeRepoMock
            .Setup(x => x.CreateAsync(It.IsAny<Employee>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync((Employee e, CancellationToken _) => e);

        // Act
        var result = await _handler.HandleAsync(command, CancellationToken.None);

        // Assert
        Assert.NotNull(result.Employee);
        Assert.Equal("John Doe", result.Employee.Name);
        _employeeRepoMock.Verify(x => x.CreateAsync(
            It.Is<Employee>(e => e.Name == "John Doe"),
            It.IsAny<CancellationToken>()), Times.Once);
    }

    [Fact]
    public async Task HandleAsync_UpdateEmployee_UpdatesExisting()
    {
        // Arrange
        var existingEmployee = new Employee { Id = "emp-1", Name = "Old Name" };
        var command = new SaveEmployeeCommand
        {
            Id = "emp-1",
            Name = "New Name"
        };

        _employeeRepoMock
            .Setup(x => x.GetByIdAsync("emp-1", It.IsAny<CancellationToken>()))
            .ReturnsAsync(existingEmployee);

        _employeeRepoMock
            .Setup(x => x.UpdateAsync(It.IsAny<Employee>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync((Employee e, CancellationToken _) => e);

        // Act
        var result = await _handler.HandleAsync(command, CancellationToken.None);

        // Assert
        Assert.Equal("New Name", result.Employee.Name);
        _employeeRepoMock.Verify(x => x.UpdateAsync(
            It.Is<Employee>(e => e.Name == "New Name"),
            It.IsAny<CancellationToken>()), Times.Once);
    }

    [Fact]
    public async Task HandleAsync_InvalidCommand_ReturnsValidationError()
    {
        // Arrange
        var command = new SaveEmployeeCommand
        {
            Name = ""  // Invalid: empty name
        };

        // Act & Assert
        var result = command.Validate();
        Assert.False(result.IsValid);
        Assert.Contains(result.Errors, e => e.Contains("Name"));
    }
}
```

## Pattern 2: Query Handler Unit Test

```csharp
public class GetEmployeeListQueryTests
{
    private readonly Mock<IPlatformQueryableRootRepository<Employee, string>> _repoMock;
    private readonly GetEmployeeListQueryHandler _handler;

    [Fact]
    public async Task HandleAsync_WithFilters_ReturnsFilteredResults()
    {
        // Arrange
        var employees = new List<Employee>
        {
            new() { Id = "1", Name = "Active", Status = EmployeeStatus.Active },
            new() { Id = "2", Name = "Inactive", Status = EmployeeStatus.Inactive }
        };

        _repoMock.Setup(x => x.CountAsync(It.IsAny<Expression<Func<Employee, bool>>>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync(1);

        _repoMock.Setup(x => x.GetAllAsync(It.IsAny<Func<IPlatformUnitOfWork, IQueryable<Employee>, IQueryable<Employee>>>(), It.IsAny<CancellationToken>(), It.IsAny<Expression<Func<Employee, object>>[]>()))
            .ReturnsAsync(employees.Where(e => e.Status == EmployeeStatus.Active).ToList());

        var query = new GetEmployeeListQuery
        {
            Statuses = [EmployeeStatus.Active],
            SkipCount = 0,
            MaxResultCount = 10
        };

        // Act
        var result = await _handler.HandleAsync(query, CancellationToken.None);

        // Assert
        Assert.Single(result.Items);
        Assert.Equal("Active", result.Items[0].Name);
    }
}
```

## Pattern 3: Entity Validation Test

```csharp
public class EmployeeEntityTests
{
    [Fact]
    public void UniqueExpr_ReturnsCorrectExpression()
    {
        // Arrange
        var employees = new List<Employee>
        {
            new() { CompanyId = "c1", UserId = "u1" },
            new() { CompanyId = "c1", UserId = "u2" },
            new() { CompanyId = "c2", UserId = "u1" }
        }.AsQueryable();

        // Act
        var expr = Employee.UniqueExpr("c1", "u1");
        var result = employees.Where(expr).ToList();

        // Assert
        Assert.Single(result);
        Assert.Equal("u1", result[0].UserId);
    }

    [Fact]
    public async Task ValidateAsync_DuplicateCode_ReturnsError()
    {
        // Arrange
        var repoMock = new Mock<IPlatformQueryableRootRepository<Employee, string>>();
        repoMock.Setup(x => x.AnyAsync(It.IsAny<Expression<Func<Employee, bool>>>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync(true);  // Duplicate exists

        var employee = new Employee { Id = "new", Code = "EMP001", CompanyId = "c1" };

        // Act
        var result = await employee.ValidateAsync(repoMock.Object, CancellationToken.None);

        // Assert
        Assert.False(result.IsValid);
        Assert.Contains(result.Errors, e => e.Contains("already exists"));
    }

    [Fact]
    public void ComputedProperty_IsActive_CalculatesCorrectly()
    {
        // Arrange
        var activeEmployee = new Employee { Status = EmployeeStatus.Active, IsDeleted = false };
        var inactiveEmployee = new Employee { Status = EmployeeStatus.Inactive, IsDeleted = false };
        var deletedEmployee = new Employee { Status = EmployeeStatus.Active, IsDeleted = true };

        // Assert
        Assert.True(activeEmployee.IsActive);
        Assert.False(inactiveEmployee.IsActive);
        Assert.False(deletedEmployee.IsActive);
    }
}
```

## Pattern 4: Angular Component Test

```typescript
describe('FeatureListComponent', () => {
    let component: FeatureListComponent;
    let fixture: ComponentFixture<FeatureListComponent>;
    let store: FeatureListStore;
    let apiMock: jasmine.SpyObj<FeatureApiService>;

    beforeEach(async () => {
        apiMock = jasmine.createSpyObj('FeatureApiService', ['getList', 'delete']);

        await TestBed.configureTestingModule({
            imports: [FeatureListComponent],
            providers: [FeatureListStore, { provide: FeatureApiService, useValue: apiMock }]
        }).compileComponents();

        fixture = TestBed.createComponent(FeatureListComponent);
        component = fixture.componentInstance;
        store = TestBed.inject(FeatureListStore);
    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });

    it('should load items on init', () => {
        // Arrange
        const items = [{ id: '1', name: 'Test' }];
        apiMock.getList.and.returnValue(of({ items, totalCount: 1 }));

        // Act
        fixture.detectChanges();

        // Assert
        expect(apiMock.getList).toHaveBeenCalled();
        expect(component.vm()?.items).toEqual(items);
    });

    it('should delete item', fakeAsync(() => {
        // Arrange
        store.updateState({ items: [{ id: '1', name: 'Test' }] });
        apiMock.delete.and.returnValue(of(void 0));

        // Act
        component.onDelete({ id: '1', name: 'Test' });
        tick();

        // Assert
        expect(apiMock.delete).toHaveBeenCalledWith('1');
        expect(component.vm()?.items.length).toBe(0);
    }));

    it('should show loading state', () => {
        // Arrange
        apiMock.getList.and.returnValue(new Subject()); // Never completes

        // Act
        fixture.detectChanges();

        // Assert
        expect(store.isLoading$('loadItems')()).toBe(true);
    });
});
```

## Pattern 5: Angular Store Test

```typescript
describe('FeatureListStore', () => {
    let store: FeatureListStore;
    let apiMock: jasmine.SpyObj<FeatureApiService>;

    beforeEach(() => {
        apiMock = jasmine.createSpyObj('FeatureApiService', ['getList', 'save', 'delete']);

        TestBed.configureTestingModule({
            providers: [FeatureListStore, { provide: FeatureApiService, useValue: apiMock }]
        });

        store = TestBed.inject(FeatureListStore);
    });

    it('should initialize with default state', () => {
        expect(store.currentVm().items).toEqual([]);
        expect(store.currentVm().pagination.pageIndex).toBe(0);
    });

    it('should load items', fakeAsync(() => {
        // Arrange
        const items = [{ id: '1', name: 'Test' }];
        apiMock.getList.and.returnValue(of({ items, totalCount: 1 }));

        // Act
        store.loadItems();
        tick();

        // Assert
        expect(store.currentVm().items).toEqual(items);
        expect(store.currentVm().pagination.totalCount).toBe(1);
    }));

    it('should update state immutably', () => {
        // Arrange
        const initialItems = store.currentVm().items;

        // Act
        store.updateState({ items: [{ id: '1', name: 'New' }] });

        // Assert
        expect(store.currentVm().items).not.toBe(initialItems);
    });

    it('should handle API error', fakeAsync(() => {
        // Arrange
        apiMock.getList.and.returnValue(throwError(() => new Error('API Error')));

        // Act
        store.loadItems();
        tick();

        // Assert
        expect(store.getErrorMsg$('loadItems')()).toContain('Error');
    }));
});
```

## Test Naming Convention

```
[MethodName]_[Scenario]_[ExpectedBehavior]

Examples:
- HandleAsync_ValidCommand_ReturnsSuccess
- HandleAsync_InvalidId_ThrowsNotFound
- UniqueExpr_MatchingValues_ReturnsTrue
- LoadItems_ApiError_SetsErrorState
```

## Anti-Patterns to AVOID

:x: **Testing implementation, not behavior**

```csharp
// WRONG - testing internal method calls
Assert.True(handler.WasValidateCalled);

// CORRECT - testing observable behavior
Assert.Equal("Expected", result.Value);
```

:x: **Not mocking dependencies**

```csharp
// WRONG - using real repository
var handler = new Handler(new RealRepository());

// CORRECT - using mock
var repoMock = new Mock<IRepository>();
var handler = new Handler(repoMock.Object);
```

:x: **Missing edge cases**

```csharp
// WRONG - only happy path
[Fact] public void Save_ValidData_Succeeds() { }

// CORRECT - include edge cases
[Fact] public void Save_EmptyName_ReturnsError() { }
[Fact] public void Save_DuplicateCode_ReturnsError() { }
[Fact] public void Save_NullInput_ThrowsException() { }
```

## Verification Checklist

- [ ] Unit tests cover happy path
- [ ] Edge cases and error conditions tested
- [ ] Dependencies properly mocked
- [ ] Test naming follows convention
- [ ] Assertions are specific and meaningful
- [ ] No test interdependencies
- [ ] Tests are deterministic (no random, no time-dependent)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.