Skip to main content
Testing is essential for ensuring your Intent Architect modules work correctly and continue to work as you make changes.

Testing Strategy

A comprehensive testing strategy for modules includes:

Integration Tests

Test complete code generation from metadata to output

Unit Tests

Test individual templates, decorators, and extensions

Output Verification

Verify generated code compiles and meets requirements

Regression Tests

Ensure changes don’t break existing functionality

Test Application Structure

The recommended approach is to create Intent Architect test applications:
Tests/
├── YourModule.Tests/
│   ├── Intent.Metadata/              # Designer metadata
│   ├── YourModule.Tests.Application/  # Generated application
│   ├── YourModule.Tests.IntegrationTests/  # Test project
│   ├── YourModule.Tests.sln
│   ├── YourModule.Tests.application.config
│   └── modules.config

Creating a Test Application

Step 1: Create the Intent Application

1

Create new application

Create a new Intent Architect application specifically for testing your module.
2

Install your module

Add your module (and its dependencies) to the test application.
3

Create test metadata

Design metadata in the designers that exercises your module’s features.
4

Run Software Factory

Generate the code and verify it produces expected output.

Step 2: Add Integration Tests

Create a test project to verify the generated code:
IntegrationTests.csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <IsPackable>false</IsPackable>
    <IsTestProject>true</IsTestProject>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
    <PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
    <PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\YourModule.Tests.Application\YourModule.Tests.Application.csproj" />
  </ItemGroup>
</Project>

Integration Testing Patterns

Testing Generated Code Compiles

The most basic test is ensuring generated code compiles:
CompilationTests.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourModule.Tests.IntegrationTests
{
    [TestClass]
    public class CompilationTests
    {
        [TestMethod]
        public void GeneratedCodeCompiles()
        {
            // If this test project builds successfully,
            // it means all generated code compiles
            Assert.IsTrue(true);
        }
    }
}

Testing Template Output

Verify specific template outputs exist and are correct:
CommandHandlerTests.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourModule.Tests.Application.Commands;

namespace YourModule.Tests.IntegrationTests
{
    [TestClass]
    public class CommandHandlerTests
    {
        [TestMethod]
        public void CommandHandlerExists()
        {
            // Verify the handler class was generated
            var handler = new CreateOrderHandler();
            Assert.IsNotNull(handler);
        }

        [TestMethod]
        public void CommandHandlerImplementsCorrectInterface()
        {
            // Verify it implements IRequestHandler
            var handler = new CreateOrderHandler();
            Assert.IsInstanceOfType(handler, typeof(IRequestHandler<CreateOrderCommand>));
        }

        [TestMethod]
        public async Task HandlerExecutesWithoutError()
        {
            // Test the handler can be instantiated and called
            var handler = new CreateOrderHandler();
            var command = new CreateOrderCommand { /* ... */ };
            
            // This will throw NotImplementedException in scaffolded code,
            // but verifies the signature is correct
            await Assert.ThrowsExceptionAsync<NotImplementedException>(
                () => handler.Handle(command, CancellationToken.None));
        }
    }
}

Testing Dependency Injection Configuration

Verify DI registrations are correct:
DependencyInjectionTests.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MediatR;

namespace YourModule.Tests.IntegrationTests
{
    [TestClass]
    public class DependencyInjectionTests
    {
        [TestMethod]
        public void MediatRIsRegistered()
        {
            // Arrange
            var services = new ServiceCollection();
            
            // Act
            ConfigureServices(services);
            var provider = services.BuildServiceProvider();
            
            // Assert
            var mediator = provider.GetService<IMediator>();
            Assert.IsNotNull(mediator);
        }

        [TestMethod]
        public void CommandHandlersAreRegistered()
        {
            // Arrange
            var services = new ServiceCollection();
            ConfigureServices(services);
            var provider = services.BuildServiceProvider();
            
            // Act
            var handler = provider.GetService<IRequestHandler<CreateOrderCommand>>();
            
            // Assert
            Assert.IsNotNull(handler);
        }

        private void ConfigureServices(IServiceCollection services)
        {
            // Call your generated configuration method
            services.AddApplication(/* ... */);
        }
    }
}

Testing Business Logic

Test generated repositories, services, and handlers:
RepositoryTests.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourModule.Tests.Application.Repositories;
using YourModule.Tests.Domain.Entities;

namespace YourModule.Tests.IntegrationTests
{
    [TestClass]
    public class RepositoryTests
    {
        private IOrderRepository _repository;

        [TestInitialize]
        public void Setup()
        {
            // Setup in-memory database or mock
            _repository = CreateRepository();
        }

        [TestMethod]
        public async Task CanAddAndRetrieveEntity()
        {
            // Arrange
            var order = new Order { Id = Guid.NewGuid(), /* ... */ };
            
            // Act
            await _repository.AddAsync(order);
            var retrieved = await _repository.GetByIdAsync(order.Id);
            
            // Assert
            Assert.IsNotNull(retrieved);
            Assert.AreEqual(order.Id, retrieved.Id);
        }

        private IOrderRepository CreateRepository()
        {
            // Create repository with test database context
            return new OrderRepository(CreateDbContext());
        }
    }
}

Unit Testing Modules

For more granular testing, create unit tests for individual components:

Testing Templates

TemplateUnitTests.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Intent.Engine;
using Intent.Modules.YourModule.Templates;

namespace YourModule.UnitTests
{
    [TestClass]
    public class CommandHandlerTemplateTests
    {
        [TestMethod]
        public void GeneratesCorrectClassName()
        {
            // Arrange
            var model = CreateTestModel("CreateOrder");
            var template = new CommandHandlerTemplate(
                CreateTestOutputTarget(), 
                model);
            
            // Act
            var output = template.TransformText();
            
            // Assert
            Assert.IsTrue(output.Contains("class CreateOrderHandler"));
        }

        [TestMethod]
        public void ImplementsIRequestHandlerInterface()
        {
            // Arrange
            var model = CreateTestModel("CreateOrder");
            var template = new CommandHandlerTemplate(
                CreateTestOutputTarget(), 
                model);
            
            // Act
            var output = template.TransformText();
            
            // Assert
            Assert.IsTrue(output.Contains("IRequestHandler<CreateOrderCommand>"));
        }

        [TestMethod]
        public void IncludesRequiredUsings()
        {
            // Arrange
            var model = CreateTestModel("CreateOrder");
            var template = new CommandHandlerTemplate(
                CreateTestOutputTarget(), 
                model);
            
            // Act
            var output = template.TransformText();
            
            // Assert
            Assert.IsTrue(output.Contains("using MediatR;"));
            Assert.IsTrue(output.Contains("using System.Threading;"));
            Assert.IsTrue(output.Contains("using System.Threading.Tasks;"));
        }

        private CommandModel CreateTestModel(string name)
        {
            // Create mock/test model
            return new CommandModel(/* ... */);
        }

        private IOutputTarget CreateTestOutputTarget()
        {
            // Create mock output target
            return Mock.Of<IOutputTarget>();
        }
    }
}

Testing Decorators

DecoratorUnitTests.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Intent.Modules.YourModule.Decorators;

namespace YourModule.UnitTests
{
    [TestClass]
    public class LoggingDecoratorTests
    {
        [TestMethod]
        public void AddsLoggingStatements()
        {
            // Arrange
            var template = CreateTestTemplate();
            var decorator = new LoggingCommandHandlerDecorator(
                template, 
                CreateTestApplication());
            
            // Act
            decorator.BeforeHandleMethod();
            var output = template.TransformText();
            
            // Assert
            Assert.IsTrue(output.Contains("_logger.LogInformation"));
        }

        [TestMethod]
        public void AddsRequiredUsings()
        {
            // Arrange
            var decorator = new LoggingCommandHandlerDecorator(
                CreateTestTemplate(), 
                CreateTestApplication());
            
            // Act
            var usings = decorator.DeclareUsings().ToList();
            
            // Assert
            Assert.IsTrue(usings.Contains("Microsoft.Extensions.Logging"));
        }

        [TestMethod]
        public void HasCorrectPriority()
        {
            // Arrange
            var decorator = new LoggingCommandHandlerDecorator(
                CreateTestTemplate(), 
                CreateTestApplication());
            
            // Assert
            Assert.AreEqual(10, decorator.Priority);
        }
    }
}

Testing Factory Extensions

FactoryExtensionUnitTests.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Intent.Modules.YourModule.FactoryExtensions;

namespace YourModule.UnitTests
{
    [TestClass]
    public class DependencyInjectionExtensionTests
    {
        [TestMethod]
        public void RegistersMediatRCorrectly()
        {
            // Arrange
            var application = CreateTestApplication();
            var extension = new DependencyInjectionFactoryExtension();
            
            // Simulate event
            application.EventDispatcher.Publish(new ContainerRegistrationRequest
            {
                Concern = "MediatR",
                ConcreteType = "typeof(MyBehavior)"
            });
            
            // Act
            extension.OnAfterTemplateRegistrations(application);
            
            // Assert
            var diTemplate = application.FindTemplateInstance<ICSharpFileBuilderTemplate>(
                TemplateRoles.Application.DependencyInjection);
            var output = diTemplate.TransformText();
            
            Assert.IsTrue(output.Contains("services.AddMediatR"));
            Assert.IsTrue(output.Contains("cfg.AddOpenBehavior(typeof(MyBehavior))"));
        }

        [TestMethod]
        public void HandlesEventCorrectly()
        {
            // Arrange
            var extension = new DependencyInjectionFactoryExtension();
            var application = CreateTestApplication();
            extension.OnBeforeTemplateRegistrations(application);
            
            var @event = new ContainerRegistrationRequest
            {
                Concern = "MediatR",
                ConcreteType = "typeof(MyBehavior)"
            };
            
            // Act
            application.EventDispatcher.Publish(@event);
            
            // Assert
            Assert.IsTrue(@event.IsHandled);
        }
    }
}

Testing with Real Metadata

For comprehensive testing, use real Intent Architect metadata:
MetadataBasedTests.cs
[TestClass]
public class MetadataBasedTests
{
    private IApplication _application;

    [TestInitialize]
    public void Setup()
    {
        // Load the actual test application configuration
        _application = LoadTestApplication("YourModule.Tests.application.config");
    }

    [TestMethod]
    public void GeneratesExpectedNumberOfHandlers()
    {
        // Arrange
        var templates = _application.FindTemplateInstances<CommandHandlerTemplate>();
        
        // Assert - based on your test metadata
        Assert.AreEqual(5, templates.Count());
    }

    [TestMethod]
    public void AllTemplatesCanExecute()
    {
        // Arrange
        var templates = _application.FindTemplateInstances<ITemplate>();
        
        // Act & Assert
        foreach (var template in templates)
        {
            Assert.IsTrue(template.CanRunTemplate(), 
                $"Template {template.Id} cannot run");
            
            var output = template.RunTemplate();
            Assert.IsNotNull(output);
        }
    }

    private IApplication LoadTestApplication(string configPath)
    {
        // Load actual application configuration
        // Implementation depends on Intent SDK
        return ApplicationConfigLoader.Load(configPath);
    }
}

Automated Testing in CI/CD

Pre-commit Validation Script

The repository includes a pre-commit validation script:
run-pre-commit-checks.ps1
# Build all modules
dotnet build

# Run all tests
dotnet test

# Verify modules package correctly
foreach ($module in Get-ChildItem -Path "Modules" -Filter "*.csproj" -Recurse) {
    dotnet pack $module.FullName
}

Azure Pipelines Configuration

Example pipeline configuration:
azure-pipelines.yml
trigger:
  branches:
    include:
    - master
    - develop

pool:
  vmImage: 'windows-latest'

steps:
- task: UseDotNet@2
  inputs:
    version: '8.x'

- task: DotNetCoreCLI@2
  displayName: 'Restore'
  inputs:
    command: 'restore'
    projects: '**/*.csproj'

- task: DotNetCoreCLI@2
  displayName: 'Build'
  inputs:
    command: 'build'
    projects: '**/*.csproj'
    arguments: '--configuration Release'

- task: DotNetCoreCLI@2
  displayName: 'Test'
  inputs:
    command: 'test'
    projects: 'Tests/**/*.IntegrationTests.csproj'
    arguments: '--configuration Release'

- task: DotNetCoreCLI@2
  displayName: 'Pack Modules'
  inputs:
    command: 'pack'
    projects: 'Modules/**/*.csproj'
    arguments: '--configuration Release'

Test-Driven Development for Modules

Follow TDD when developing modules:
1

Write failing test

Create a test that describes the desired behavior:
[TestMethod]
public void CommandHandlerHasValidationBehavior()
{
    var output = template.TransformText();
    Assert.IsTrue(output.Contains("IValidator"));
}
2

Implement feature

Update your template/decorator to make the test pass.
3

Refactor

Clean up the implementation while keeping tests green.
4

Add integration test

Verify the feature works end-to-end in a test application.

Regression Testing

Maintain regression tests for bug fixes:
RegressionTests.cs
[TestClass]
public class RegressionTests
{
    [TestMethod]
    [TestCategory("Regression")]
    public void Issue123_HandlerShouldNotDuplicateUsings()
    {
        // Arrange
        var template = CreateTemplateWithMultipleDecorators();
        
        // Act
        var output = template.TransformText();
        
        // Assert
        var usingCount = CountOccurrences(output, "using MediatR;");
        Assert.AreEqual(1, usingCount, "Should only have one using MediatR statement");
    }

    [TestMethod]
    [TestCategory("Regression")]
    public void Issue456_NullableReturnTypeSupported()
    {
        // Test for specific bug fix
        var model = CreateModelWithNullableReturn();
        var template = new CommandHandlerTemplate(CreateTestOutputTarget(), model);
        
        var output = template.TransformText();
        
        Assert.IsTrue(output.Contains("Task<string?>"));
    }
}

Test Organization

Organize tests by category:
[TestClass]
public class TemplateTests
{
    [TestMethod]
    [TestCategory("Template")]
    [TestCategory("CommandHandler")]
    public void CommandHandlerTest() { }
}

[TestClass]
public class DecoratorTests
{
    [TestMethod]
    [TestCategory("Decorator")]
    [TestCategory("Validation")]
    public void ValidationDecoratorTest() { }
}

[TestClass]
public class IntegrationTests
{
    [TestMethod]
    [TestCategory("Integration")]
    [TestCategory("Slow")]
    public void EndToEndTest() { }
}
Run specific categories:
dotnet test --filter "TestCategory=Integration"
dotnet test --filter "TestCategory!=Slow"

Best Practices

Every template, decorator, and factory extension should have tests.
Create Intent Architect test applications with representative metadata.
The minimum test is ensuring generated code compiles without errors.
Test with optional properties, empty collections, null values, etc.
When fixing bugs, add tests to prevent regression.
Automate test execution in your build pipeline.
Categorize tests to run different suites (unit, integration, slow, etc.).

Common Test Scenarios

Template Output

  • Correct class names
  • Proper namespaces
  • Required using statements
  • Expected methods and properties

Decorator Behavior

  • Attributes added correctly
  • Method bodies modified
  • Constructor parameters injected
  • Priority ordering

Factory Extensions

  • Events handled correctly
  • Templates modified as expected
  • Dependencies registered
  • Configuration applied

Integration

  • Generated code compiles
  • DI container configured
  • Application runs
  • Business logic works

Test Helpers and Utilities

Create helper classes for common test operations:
TestHelpers.cs
public static class TestHelpers
{
    public static IOutputTarget CreateTestOutputTarget(
        string name = "TestProject",
        string location = "TestLocation")
    {
        var mock = new Mock<IOutputTarget>();
        mock.Setup(x => x.Name).Returns(name);
        mock.Setup(x => x.Location).Returns(location);
        return mock.Object;
    }

    public static IApplication CreateTestApplication()
    {
        var mock = new Mock<IApplication>();
        mock.Setup(x => x.EventDispatcher).Returns(new EventDispatcher());
        return mock.Object;
    }

    public static CommandModel CreateCommandModel(
        string name,
        string returnType = null)
    {
        // Create test model
        return new CommandModel(/* ... */);
    }

    public static int CountOccurrences(string text, string pattern)
    {
        return Regex.Matches(text, Regex.Escape(pattern)).Count;
    }
}

Debugging Tests

Tips for debugging failing tests:
var output = template.TransformText();
Console.WriteLine(output); // View actual output
File.WriteAllText("debug-output.cs", output); // Save to file
Set breakpoints in your template/decorator code and debug the test.
Create minimal test cases that reproduce the problem.
Run the Software Factory on your test application and inspect the output manually.

Example Test Suite

Here’s a complete example from the repository structure:
Tests/AdvancedMappingCrud.Repositories.Tests/
├── AdvancedMappingCrud.Repositories.Tests.Application/
│   ├── Commands/
│   │   └── CreateOrderHandler.cs (generated)
│   ├── Repositories/
│   │   └── OrderRepository.cs (generated)
│   └── DependencyInjection.cs (generated)
├── AdvancedMappingCrud.Repositories.Tests.IntegrationTests/
│   ├── Tests/
│   │   ├── OrderRepositoryTests.cs
│   │   ├── CommandHandlerTests.cs
│   │   └── DependencyInjectionTests.cs
│   └── AdvancedMappingCrud.Repositories.Tests.IntegrationTests.csproj
├── Intent.Metadata/
│   └── (designer metadata)
├── AdvancedMappingCrud.Repositories.Tests.sln
└── modules.config

Next Steps

Creating Modules

Package your tested modules for distribution

Template Development

Create testable templates

Decorators

Build decorator components

Factory Extensions

Develop factory extensions

Contributing Tests

When contributing to the Intent.Modules.NET repository:
  1. Add test cases for new features
  2. Update existing tests when changing behavior
  3. Run run-pre-commit-checks.ps1 before committing
  4. Ensure all tests pass in CI/CD pipeline
  5. Add test documentation in PR descriptions
The repository includes 200+ test applications demonstrating best practices for module testing.

Build docs developers (and LLMs) love