CargoWise Integration Testing Strategies: Complete Guide 2025

Nov 15, 2025
cargowisetestingintegration-testingunit-testing
0

Testing CargoWise integrations is complex due to the message-based architecture, XML schemas, external dependencies, and asynchronous processing. Without comprehensive testing strategies, integration bugs can cause data loss, duplicate records, or system failures in production. Effective testing requires understanding different test types, mocking strategies, sandbox environments, and test automation patterns.

This comprehensive guide covers CargoWise integration testing strategies from unit tests to end-to-end integration tests. You'll learn how to mock eAdapter responses, set up sandbox environments, implement test data factories, automate testing pipelines, and ensure your integrations are production-ready.

Testing Strategy Overview

Testing Pyramid for CargoWise Integrations

A well-structured testing strategy follows the testing pyramid:

1. Unit Tests (70%)

  • Test individual components in isolation
  • Mock external dependencies
  • Fast execution
  • High coverage

2. Integration Tests (20%)

  • Test component interactions
  • Use test doubles or sandbox
  • Moderate execution time
  • Focus on critical paths

3. End-to-End Tests (10%)

  • Test complete workflows
  • Use sandbox environment
  • Slower execution
  • Validate business scenarios

Test Categories

Unit Tests

  • Message transformation logic
  • Validation rules
  • Business logic
  • Error handling

Integration Tests

  • eAdapter message exchange
  • Database operations
  • Queue processing
  • API interactions

End-to-End Tests

  • Complete shipment workflows
  • Multi-system integration
  • Error recovery scenarios
  • Performance under load

Unit Testing

Testing Message Transformation

Test XML transformation logic in isolation:

[Fact]
public void ShouldTransformShipmentCorrectly()
{
    // Arrange
    var sourceXml = @"<?xml version=""1.0""?>
        <Shipment>
            <ShipmentNumber>SH-001</ShipmentNumber>
            <TransportMode>Air</TransportMode>
            <Weight>150.5</Weight>
        </Shipment>";

    var transformer = new ShipmentTransformer();
    
    // Act
    var result = transformer.Transform(sourceXml);
    
    // Assert
    var resultDoc = XDocument.Parse(result);
    Assert.Equal("SH-001", resultDoc.Descendants("ShipmentId").First().Value);
    Assert.Equal("AIR", resultDoc.Descendants("Mode").First().Value);
    Assert.Equal("150.50", resultDoc.Descendants("Weight").First().Value);
}

Testing Validation Logic

Test data validation rules:

[Theory]
[InlineData("", false, "ShipmentId is required")]
[InlineData("SH-001", true, null)]
[InlineData(null, false, "ShipmentId is required")]
public void ShouldValidateShipmentId(string shipmentId, bool expectedValid, string expectedError)
{
    // Arrange
    var validator = new ShipmentValidator();
    var shipment = new Shipment { ShipmentId = shipmentId };
    
    // Act
    var result = validator.Validate(shipment);
    
    // Assert
    Assert.Equal(expectedValid, result.IsValid);
    if (!expectedValid)
    {
        Assert.Contains(expectedError, result.Errors.Select(e => e.Message));
    }
}

Testing Business Logic

Test business rule implementations:

[Fact]
public void ShouldCalculateTotalWeightCorrectly()
{
    // Arrange
    var shipment = new Shipment
    {
        Items = new[]
        {
            new Item { Weight = 50.5m, Quantity = 2 },
            new Item { Weight = 25.0m, Quantity = 1 }
        }
    };
    
    var calculator = new ShipmentCalculator();
    
    // Act
    var totalWeight = calculator.CalculateTotalWeight(shipment);
    
    // Assert
    Assert.Equal(126.0m, totalWeight);
}

Mocking External Dependencies

Use mocks to isolate components:

public class CargoWiseMessageProcessorTests
{
    [Fact]
    public async Task ShouldProcessMessageSuccessfully()
    {
        // Arrange
        var mockEAdapterClient = new Mock<ICargoWiseEAdapterClient>();
        mockEAdapterClient
            .Setup(x => x.SendMessageAsync(It.IsAny<string>()))
            .ReturnsAsync(new CargoWiseResponse
            {
                Success = true,
                MessageId = "MSG-001"
            });

        var processor = new CargoWiseMessageProcessor(mockEAdapterClient.Object);
        var message = CreateTestMessage();
        
        // Act
        var result = await processor.ProcessMessageAsync(message);
        
        // Assert
        Assert.True(result.Success);
        mockEAdapterClient.Verify(
            x => x.SendMessageAsync(It.IsAny<string>()),
            Times.Once);
    }
}

Integration Testing

Setting Up Test Infrastructure

Create test infrastructure for integration tests:

public class CargoWiseIntegrationTestFixture : IAsyncLifetime
{
    public ICargoWiseEAdapterClient EAdapterClient { get; private set; }
    public ITestDatabase TestDatabase { get; private set; }
    public IMessageQueue TestQueue { get; private set; }

    public async Task InitializeAsync()
    {
        // Set up test database
        TestDatabase = await CreateTestDatabaseAsync();
        
        // Set up test message queue
        TestQueue = new InMemoryMessageQueue();
        
        // Set up eAdapter client (sandbox or mock)
        EAdapterClient = await CreateEAdapterClientAsync();
    }

    public async Task DisposeAsync()
    {
        await TestDatabase.CleanupAsync();
        await TestQueue.DisposeAsync();
    }

    private async Task<ITestDatabase> CreateTestDatabaseAsync()
    {
        var connectionString = TestConfiguration.GetTestDatabaseConnectionString();
        var database = new TestDatabase(connectionString);
        await database.InitializeAsync();
        return database;
    }

    private async Task<ICargoWiseEAdapterClient> CreateEAdapterClientAsync()
    {
        // Use sandbox environment for integration tests
        var config = TestConfiguration.GetCargoWiseSandboxConfig();
        return new CargoWiseEAdapterClient(config);
    }
}

Testing Message Exchange

Test complete message exchange flows:

public class CargoWiseMessageExchangeTests : IClassFixture<CargoWiseIntegrationTestFixture>
{
    private readonly CargoWiseIntegrationTestFixture _fixture;

    public CargoWiseMessageExchangeTests(CargoWiseIntegrationTestFixture fixture)
    {
        _fixture = fixture;
    }

    [Fact]
    public async Task ShouldSendAndReceiveMessage()
    {
        // Arrange
        var message = CreateTestShipmentMessage();
        var processor = new CargoWiseMessageProcessor(_fixture.EAdapterClient);
        
        // Act
        var response = await processor.SendMessageAsync(message);
        
        // Assert
        Assert.True(response.Success);
        Assert.NotNull(response.MessageId);
        
        // Verify message was processed
        var status = await _fixture.EAdapterClient.GetMessageStatusAsync(response.MessageId);
        Assert.Equal(MessageStatus.Processed, status);
    }
}

Testing Error Scenarios

Test error handling and recovery:

[Fact]
public async Task ShouldHandleValidationErrors()
{
    // Arrange
    var invalidMessage = CreateInvalidShipmentMessage();
    var processor = new CargoWiseMessageProcessor(_fixture.EAdapterClient);
    
    // Act & Assert
    var exception = await Assert.ThrowsAsync<ValidationException>(
        () => processor.SendMessageAsync(invalidMessage));
    
    Assert.Contains("ShipmentId is required", exception.Message);
}

[Fact]
public async Task ShouldRetryOnTransientErrors()
{
    // Arrange
    var message = CreateTestShipmentMessage();
    var processor = new CargoWiseMessageProcessor(_fixture.EAdapterClient);
    
    // Simulate transient error
    _fixture.EAdapterClient.SimulateTransientError();
    
    // Act
    var response = await processor.SendMessageAsync(message);
    
    // Assert - Should succeed after retry
    Assert.True(response.Success);
}

Sandbox Environment Setup

CargoWise Sandbox Configuration

Configure sandbox environment for testing:

public class CargoWiseSandboxConfig
{
    public string BaseUrl { get; set; } = "https://sandbox.cargowise.com/";
    public string ApiKey { get; set; }
    public string CompanyCode { get; set; }
    public bool AutoCleanup { get; set; } = true;
    public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(5);
}

public class SandboxEnvironment
{
    private readonly CargoWiseSandboxConfig _config;
    private readonly ILogger<SandboxEnvironment> _logger;

    public async Task<SandboxTestData> SetupTestDataAsync()
    {
        // Create test shipment
        var shipment = await CreateTestShipmentAsync();
        
        // Create test organization
        var organization = await CreateTestOrganizationAsync();
        
        return new SandboxTestData
        {
            ShipmentId = shipment.Id,
            OrganizationId = organization.Id,
            CreatedAt = DateTime.UtcNow
        };
    }

    public async Task CleanupTestDataAsync(SandboxTestData testData)
    {
        try
        {
            await DeleteTestShipmentAsync(testData.ShipmentId);
            await DeleteTestOrganizationAsync(testData.OrganizationId);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to cleanup test data");
        }
    }
}

Test Data Factories

Create reusable test data:

public class CargoWiseTestDataFactory
{
    public static ShipmentMessage CreateShipmentMessage(
        string shipmentId = null,
        string mode = "Air",
        decimal? weight = null)
    {
        return new ShipmentMessage
        {
            ShipmentId = shipmentId ?? $"SH-{Guid.NewGuid():N}",
            Mode = mode,
            Weight = weight ?? 100.0m,
            Origin = CreateLocation("New York", "US", "JFK"),
            Destination = CreateLocation("London", "GB", "LHR"),
            Items = new[] { CreateItem() }
        };
    }

    public static Location CreateLocation(string city, string country, string code)
    {
        return new Location
        {
            City = city,
            Country = country,
            Code = code
        };
    }

    public static Item CreateItem(
        string description = "Test Item",
        decimal weight = 50.0m,
        int quantity = 1)
    {
        return new Item
        {
            Description = description,
            Weight = weight,
            Quantity = quantity
        };
    }
}

Mocking Strategies

Mock eAdapter Client

Create mock implementations for testing:

public class MockCargoWiseEAdapterClient : ICargoWiseEAdapterClient
{
    private readonly Dictionary<string, CargoWiseResponse> _responses;
    private readonly List<SentMessage> _sentMessages;

    public MockCargoWiseEAdapterClient()
    {
        _responses = new Dictionary<string, CargoWiseResponse>();
        _sentMessages = new List<SentMessage>();
    }

    public Task<CargoWiseResponse> SendMessageAsync(string messageXml)
    {
        var message = XDocument.Parse(messageXml);
        var messageId = message.Descendants("MessageId").FirstOrDefault()?.Value 
            ?? Guid.NewGuid().ToString();

        _sentMessages.Add(new SentMessage
        {
            MessageId = messageId,
            Content = messageXml,
            Timestamp = DateTime.UtcNow
        });

        if (_responses.TryGetValue(messageId, out var response))
        {
            return Task.FromResult(response);
        }

        return Task.FromResult(new CargoWiseResponse
        {
            Success = true,
            MessageId = messageId
        });
    }

    public void SetupResponse(string messageId, CargoWiseResponse response)
    {
        _responses[messageId] = response;
    }

    public IReadOnlyList<SentMessage> GetSentMessages()
    {
        return _sentMessages.AsReadOnly();
    }
}

Mock Message Queue

Mock message queue for testing:

public class InMemoryMessageQueue : IMessageQueue
{
    private readonly ConcurrentQueue<QueueMessage> _messages;
    private readonly Dictionary<string, QueueMessage> _processingMessages;

    public InMemoryMessageQueue()
    {
        _messages = new ConcurrentQueue<QueueMessage>();
        _processingMessages = new Dictionary<string, QueueMessage>();
    }

    public Task EnqueueAsync(QueueMessage message)
    {
        _messages.Enqueue(message);
        return Task.CompletedTask;
    }

    public Task<QueueMessage> DequeueAsync(CancellationToken cancellationToken = default)
    {
        if (_messages.TryDequeue(out var message))
        {
            _processingMessages[message.Id] = message;
            return Task.FromResult(message);
        }

        return Task.FromResult<QueueMessage>(null);
    }

    public Task CompleteAsync(string messageId)
    {
        _processingMessages.Remove(messageId);
        return Task.CompletedTask;
    }

    public int GetMessageCount()
    {
        return _messages.Count;
    }
}

Test Automation

Continuous Integration Setup

Configure CI/CD for automated testing:

# .github/workflows/integration-tests.yml
name: Integration Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup .NET
        uses: actions/setup-dotnet@v3
        with:
          dotnet-version: '8.0.x'
      
      - name: Restore dependencies
        run: dotnet restore
      
      - name: Run unit tests
        run: dotnet test --filter Category=Unit --no-restore
      
      - name: Run integration tests
        run: dotnet test --filter Category=Integration --no-restore
        env:
          CARGOWISE_SANDBOX_API_KEY: ${{ secrets.CARGOWISE_SANDBOX_API_KEY }}
          TEST_DATABASE_CONNECTION: ${{ secrets.TEST_DATABASE_CONNECTION }}

Test Categories and Attributes

Organize tests with categories:

[Fact]
[Trait("Category", "Unit")]
[Trait("Component", "Transformation")]
public void ShouldTransformShipment()
{
    // Test implementation
}

[Fact]
[Trait("Category", "Integration")]
[Trait("Component", "MessageExchange")]
public async Task ShouldSendMessageToCargoWise()
{
    // Test implementation
}

[Fact]
[Trait("Category", "E2E")]
[Trait("Scenario", "CompleteShipmentWorkflow")]
public async Task ShouldProcessCompleteShipmentWorkflow()
{
    // Test implementation
}

Performance Testing

Load Testing Integration

Test integration under load:

[Fact]
[Trait("Category", "Performance")]
public async Task ShouldHandleConcurrentMessages()
{
    // Arrange
    var processor = new CargoWiseMessageProcessor(_fixture.EAdapterClient);
    var messages = Enumerable.Range(0, 100)
        .Select(i => CargoWiseTestDataFactory.CreateShipmentMessage())
        .ToArray();
    
    // Act
    var stopwatch = Stopwatch.StartNew();
    var tasks = messages.Select(m => processor.ProcessMessageAsync(m));
    var results = await Task.WhenAll(tasks);
    stopwatch.Stop();
    
    // Assert
    Assert.All(results, r => Assert.True(r.Success));
    Assert.True(stopwatch.ElapsedMilliseconds < 30000); // Complete in under 30 seconds
}

Test Coverage

Measuring Test Coverage

Track test coverage metrics:

<!-- Directory.Build.props -->
<Project>
  <PropertyGroup>
    <CollectCoverage>true</CollectCoverage>
    <CoverletOutputFormat>opencover</CoverletOutputFormat>
    <CoverletOutput>./coverage/</CoverletOutput>
    <Exclude>[*.Tests]*</Exclude>
  </PropertyGroup>
</Project>

Best Practices

1. Test Isolation

Ensure tests don't depend on each other:

public class IsolatedTestBase : IAsyncLifetime
{
    protected ITestDatabase Database { get; private set; }

    public async Task InitializeAsync()
    {
        Database = await CreateIsolatedDatabaseAsync();
    }

    public async Task DisposeAsync()
    {
        await Database.CleanupAsync();
    }
}

2. Use Test Data Builders

Build complex test data easily:

public class ShipmentMessageBuilder
{
    private ShipmentMessage _shipment = new();

    public ShipmentMessageBuilder WithShipmentId(string id)
    {
        _shipment.ShipmentId = id;
        return this;
    }

    public ShipmentMessageBuilder WithMode(string mode)
    {
        _shipment.Mode = mode;
        return this;
    }

    public ShipmentMessageBuilder WithItem(Action<ItemBuilder> configure)
    {
        var itemBuilder = new ItemBuilder();
        configure(itemBuilder);
        _shipment.Items.Add(itemBuilder.Build());
        return this;
    }

    public ShipmentMessage Build() => _shipment;
}

3. Test Error Scenarios

Test both success and failure paths:

[Theory]
[InlineData("Invalid XML", typeof(XmlException))]
[InlineData("Missing ShipmentId", typeof(ValidationException))]
[InlineData("Invalid Mode", typeof(BusinessRuleException))]
public async Task ShouldHandleErrors(string scenario, Type expectedException)
{
    var message = CreateErrorMessage(scenario);
    var processor = new CargoWiseMessageProcessor(_fixture.EAdapterClient);
    
    await Assert.ThrowsAsync(expectedException, 
        () => processor.ProcessMessageAsync(message));
}

Advanced Testing Patterns

Property-Based Testing

Use property-based testing for comprehensive coverage:

[Property]
public Property<bool> ShouldAlwaysValidateShipmentId(NonEmptyString shipmentId)
{
    var validator = new ShipmentValidator();
    var shipment = new Shipment { ShipmentId = shipmentId.Get };
    
    return validator.Validate(shipment).IsValid.ToProperty();
}

Contract Testing

Implement contract testing for API compatibility:

public class CargoWiseApiContractTests
{
    [Fact]
    public async Task ShouldMaintainApiContract()
    {
        var contract = await LoadApiContractAsync("CargoWiseApi.v1.json");
        var actualResponse = await CallApiAsync();
        
        var validationResult = await ValidateAgainstContractAsync(
            actualResponse, contract);
        
        Assert.True(validationResult.IsValid, 
            $"Contract violation: {validationResult.Errors}");
    }
}

Chaos Engineering

Test system resilience with chaos engineering:

public class ChaosTesting
{
    public async Task InjectChaosAsync()
    {
        // Randomly inject failures
        var chaos = new ChaosMonkey();
        
        chaos.InjectNetworkLatency(TimeSpan.FromSeconds(5));
        chaos.InjectServiceFailure("CargoWise", 0.1); // 10% failure rate
        chaos.InjectMemoryPressure(0.8); // 80% memory usage
        
        // Run tests under chaos
        await RunIntegrationTestsAsync();
        
        // Verify system recovers
        Assert.True(await SystemRecoveredAsync());
    }
}

Real-World Testing Scenarios

Scenario 1: High-Volume Testing

Test under production-like load:

[Fact]
public async Task ShouldHandleHighVolume()
{
    var messages = GenerateTestMessages(10000);
    var processor = new CargoWiseMessageProcessor();
    
    var stopwatch = Stopwatch.StartNew();
    var results = await ProcessBatchAsync(messages);
    stopwatch.Stop();
    
    Assert.All(results, r => Assert.True(r.Success));
    Assert.True(stopwatch.ElapsedMilliseconds < 60000); // Under 1 minute
    Assert.True(results.Count(r => r.Success) / (double)messages.Count > 0.99); // 99% success
}

Scenario 2: Concurrent Processing

Test concurrent message processing:

[Fact]
public async Task ShouldHandleConcurrentMessages()
{
    var messages = GenerateTestMessages(100);
    var processor = new CargoWiseMessageProcessor();
    
    var tasks = messages.Select(m => processor.ProcessMessageAsync(m));
    var results = await Task.WhenAll(tasks);
    
    // Verify no race conditions
    Assert.All(results, r => Assert.True(r.Success));
    
    // Verify message ordering where required
    var orderedMessages = results.Where(r => r.RequiresOrdering);
    AssertOrdering(orderedMessages);
}

Scenario 3: Data Migration Testing

Test data migration scenarios:

[Fact]
public async Task ShouldMigrateLegacyData()
{
    var legacyData = LoadLegacyData("LegacyShipments.xml");
    var transformer = new LegacyDataTransformer();
    
    var migrated = await transformer.TransformAsync(legacyData);
    
    // Verify all data migrated
    Assert.Equal(legacyData.Shipments.Count, migrated.Shipments.Count);
    
    // Verify data integrity
    foreach (var shipment in legacyData.Shipments)
    {
        var migratedShipment = migrated.Shipments
            .First(s => s.OriginalId == shipment.Id);
        AssertDataIntegrity(shipment, migratedShipment);
    }
}

Performance Testing

Load Testing

Test system under load:

public class LoadTestSuite
{
    [Theory]
    [InlineData(100)]
    [InlineData(1000)]
    [InlineData(10000)]
    public async Task ShouldHandleLoad(int messageCount)
    {
        var messages = GenerateTestMessages(messageCount);
        var processor = new CargoWiseMessageProcessor();
        
        var metrics = await RunLoadTestAsync(messages, processor);
        
        Assert.True(metrics.AverageLatency < 1000); // Under 1 second
        Assert.True(metrics.ErrorRate < 0.01); // Less than 1% errors
        Assert.True(metrics.Throughput > 100); // Over 100 messages/second
    }
}

Stress Testing

Test system limits:

[Fact]
public async Task ShouldHandleStressConditions()
{
    var stressor = new StressTestStressor();
    
    // Gradually increase load until failure
    var maxLoad = await stressor.FindBreakingPointAsync(async load =>
    {
        var messages = GenerateTestMessages(load);
        return await ProcessBatchAsync(messages);
    });
    
    // Verify system recovers after stress
    await Task.Delay(TimeSpan.FromSeconds(30));
    Assert.True(await SystemRecoveredAsync());
    
    _logger.LogInformation("System handled load up to {MaxLoad} messages", maxLoad);
}

Test Data Management

Test Data Factories

Create comprehensive test data:

public class ComprehensiveTestDataFactory
{
    public ShipmentMessage CreateComplexShipment()
    {
        return new ShipmentMessageBuilder()
            .WithShipmentId($"SH-{Guid.NewGuid():N}")
            .WithMode(RandomMode())
            .WithOrigin(CreateRandomLocation())
            .WithDestination(CreateRandomLocation())
            .WithItems(CreateRandomItems(1, 10))
            .WithCustoms(CreateRandomCustoms())
            .WithInsurance(CreateRandomInsurance())
            .Build();
    }

    public IEnumerable<ShipmentMessage> CreateBatch(int count)
    {
        return Enumerable.Range(0, count)
            .Select(_ => CreateComplexShipment());
    }
}

Test Data Cleanup

Automated test data cleanup:

public class TestDataCleanup
{
    public async Task CleanupTestDataAsync(string testRunId)
    {
        var testData = await FindTestDataByRunIdAsync(testRunId);
        
        foreach (var data in testData)
        {
            try
            {
                await DeleteTestDataAsync(data);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Failed to cleanup test data: {DataId}", data.Id);
            }
        }
    }
}

Extended FAQ

Q: How do I test async message processing?

A: Use async test patterns:

[Fact]
public async Task ShouldProcessMessageAsync()
{
    var message = CreateTestMessage();
    var processor = new AsyncMessageProcessor();
    
    var task = processor.ProcessMessageAsync(message);
    
    // Wait with timeout
    var completed = await Task.WhenAny(
        task,
        Task.Delay(TimeSpan.FromSeconds(30)));
    
    Assert.Equal(task, completed);
    Assert.True(task.IsCompletedSuccessfully);
}

Q: How do I test error recovery?

A: Test recovery scenarios:

[Fact]
public async Task ShouldRecoverFromErrors()
{
    var processor = new ResilientMessageProcessor();
    
    // Inject error
    _errorInjector.InjectError("NetworkError");
    
    var message = CreateTestMessage();
    var result = await processor.ProcessMessageAsync(message);
    
    // Verify recovery
    Assert.True(result.Success);
    Assert.True(_recoveryMonitor.RecoveryOccurred);
}

Q: How do I test performance regressions?

A: Use performance benchmarks:

[Fact]
public async Task ShouldMaintainPerformance()
{
    var baseline = await GetBaselinePerformanceAsync();
    var current = await MeasureCurrentPerformanceAsync();
    
    // Allow 10% degradation
    Assert.True(current.AverageLatency < baseline.AverageLatency * 1.1);
    Assert.True(current.Throughput > baseline.Throughput * 0.9);
}

Case Studies

Case Study 1: Comprehensive Test Suite

Challenge: A logistics company needed comprehensive testing for their CargoWise integration handling 50,000+ messages daily.

Solution: Implemented comprehensive test suite:

public class ComprehensiveTestSuite
{
    [Fact]
    public async Task FullIntegrationTest()
    {
        // Setup
        var testData = await SetupTestDataAsync();
        
        // Execute
        var results = await ProcessTestDataAsync(testData);
        
        // Verify
        AssertAllResultsValid(results);
        AssertPerformanceMetrics(results);
        AssertDataIntegrity(testData, results);
        
        // Cleanup
        await CleanupTestDataAsync(testData);
    }
}

Results:

  • 99.9% test coverage
  • Zero production bugs in 6 months
  • 50% reduction in deployment time

Conclusion

Comprehensive testing is essential for reliable CargoWise integrations. By implementing unit tests, integration tests, using sandbox environments, and following testing best practices, you can ensure your integrations are production-ready and maintainable.

Key Takeaways:

  1. Follow testing pyramid - More unit tests, fewer E2E tests
  2. Use sandbox environments - Test against real CargoWise APIs
  3. Mock external dependencies - Isolate components for unit testing
  4. Automate testing - Integrate into CI/CD pipeline
  5. Test error scenarios - Don't just test happy paths
  6. Measure coverage - Track test coverage metrics
  7. Performance testing - Test under load
  8. Property-based testing - Comprehensive coverage
  9. Chaos engineering - Test resilience
  10. Contract testing - API compatibility

Next Steps:

  1. Set up test infrastructure
  2. Write unit tests for core logic
  3. Create integration test suite
  4. Configure sandbox environment
  5. Automate testing in CI/CD
  6. Implement performance testing
  7. Set up test data management

Testing Best Practices Summary

Test Organization

Organize tests by:

  • Unit Tests: Fast, isolated, test single components
  • Integration Tests: Test component interactions
  • End-to-End Tests: Test complete workflows
  • Performance Tests: Test under load
  • Security Tests: Test security scenarios

Test Data Management

  • Use test data factories
  • Clean up test data after tests
  • Use realistic test data
  • Avoid hardcoded test data
  • Version control test data

Continuous Testing

  • Run tests in CI/CD pipeline
  • Fail builds on test failures
  • Generate test reports
  • Track test coverage
  • Monitor test execution time

Additional Resources

Testing Tools

  • xUnit: .NET testing framework
  • Moq: Mocking framework
  • FluentAssertions: Assertion library
  • Bogus: Test data generation
  • WireMock: HTTP mocking

Testing Patterns

  • Arrange-Act-Assert: Test structure
  • Given-When-Then: BDD style
  • Test Doubles: Mocks, stubs, fakes
  • Test Fixtures: Reusable test setup
  • Parameterized Tests: Test multiple scenarios

Advanced Testing Scenarios

Testing Message Transformation

Test XSLT and C# transformations:

[Fact]
public async Task ShouldTransformShipmentMessage()
{
    var sourceMessage = LoadTestMessage("TestData/SourceShipment.xml");
    var transformer = new MessageTransformer();
    
    var transformed = await transformer.TransformAsync(sourceMessage);
    
    Assert.NotNull(transformed);
    Assert.Equal("CW-SHIP-001", transformed.ShipmentId);
    ValidateSchema(transformed);
}

Testing Error Recovery

Test system recovery from errors:

[Fact]
public async Task ShouldRecoverFromTransientError()
{
    var processor = new ResilientMessageProcessor();
    
    // Inject transient error
    _errorInjector.InjectTransientError("NetworkError");
    
    var message = CreateTestMessage();
    var result = await processor.ProcessMessageAsync(message);
    
    Assert.True(result.Success);
    Assert.True(_recoveryMonitor.RecoveryOccurred);
}

Testing Performance

Test system performance:

[Fact]
public async Task ShouldProcessMessagesWithinSLA()
{
    var messages = GenerateTestMessages(1000);
    var processor = new MessageProcessor();
    
    var stopwatch = Stopwatch.StartNew();
    var results = await ProcessBatchAsync(messages);
    stopwatch.Stop();
    
    Assert.All(results, r => Assert.True(r.Success));
    Assert.True(stopwatch.ElapsedMilliseconds < 30000); // Under 30 seconds
}

Testing Tools and Frameworks

xUnit Configuration

Configure xUnit for integration testing:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="xunit" Version="2.6.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
    <PackageReference Include="Moq" Version="4.20.69" />
    <PackageReference Include="FluentAssertions" Version="6.12.0" />
  </ItemGroup>
</Project>

Test Data Builders

Create comprehensive test data:

public class ShipmentMessageBuilder
{
    private ShipmentMessage _shipment = new();

    public ShipmentMessageBuilder WithShipmentId(string id)
    {
        _shipment.ShipmentId = id;
        return this;
    }

    public ShipmentMessageBuilder WithMode(string mode)
    {
        _shipment.Mode = mode;
        return this;
    }

    public ShipmentMessageBuilder WithOrigin(string city, string country)
    {
        _shipment.Origin = new Location { City = city, Country = country };
        return this;
    }

    public ShipmentMessage Build() => _shipment;
}

Testing Patterns

Test Fixtures

Reusable test setup:

public class CargoWiseTestFixture : IAsyncLifetime
{
    public ICargoWiseClient Client { get; private set; }
    public ITestDatabase Database { get; private set; }

    public async Task InitializeAsync()
    {
        Database = await CreateTestDatabaseAsync();
        Client = CreateTestClient(Database);
    }

    public async Task DisposeAsync()
    {
        await Database.CleanupAsync();
        Client?.Dispose();
    }
}

Parameterized Tests

Test multiple scenarios:

[Theory]
[InlineData("AIR", "JFK", "LHR")]
[InlineData("OCEAN", "NYC", "LON")]
[InlineData("ROAD", "NYC", "BOS")]
public async Task ShouldProcessShipmentForMode(
    string mode, 
    string origin, 
    string destination)
{
    var message = new ShipmentMessageBuilder()
        .WithMode(mode)
        .WithOrigin(origin, "US")
        .WithDestination(destination, "UK")
        .Build();

    var result = await _processor.ProcessMessageAsync(message);
    
    Assert.True(result.Success);
}

Testing Infrastructure Setup

Test Environment Configuration

Configure comprehensive test environments:

public class TestEnvironmentConfig
{
    public async Task<TestEnvironment> SetupTestEnvironmentAsync()
    {
        var environment = new TestEnvironment
        {
            Database = await CreateTestDatabaseAsync(),
            MessageQueue = await CreateTestQueueAsync(),
            ApiClient = CreateTestApiClient(),
            MockServices = await SetupMockServicesAsync()
        };

        return environment;
    }
}

Test Data Seeding

Seed test data efficiently:

public class TestDataSeeder
{
    public async Task SeedTestDataAsync(ITestDatabase database)
    {
        var shipments = GenerateTestShipments(100);
        var customers = GenerateTestCustomers(50);
        var products = GenerateTestProducts(200);

        await database.SeedAsync(shipments);
        await database.SeedAsync(customers);
        await database.SeedAsync(products);
    }
}

Continuous Integration Testing

CI/CD Pipeline Integration

Integrate tests into CI/CD:

# .github/workflows/test.yml
name: Integration Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Setup .NET
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: '8.0.x'
      - name: Run tests
        run: dotnet test --collect:"XPlat Code Coverage"
      - name: Upload coverage
        uses: codecov/codecov-action@v2

Test Reporting

Generate comprehensive test reports:

public class TestReporter
{
    public async Task GenerateReportAsync(TestResults results)
    {
        var report = new TestReport
        {
            TotalTests = results.TotalTests,
            PassedTests = results.PassedTests,
            FailedTests = results.FailedTests,
            Coverage = results.Coverage,
            ExecutionTime = results.ExecutionTime
        };

        await SaveReportAsync(report);
        await PublishReportAsync(report);
    }
}

Test Automation Strategies

Automated Test Execution

Automate test execution:

public class TestAutomation
{
    public async Task<TestResults> RunAutomatedTestsAsync()
    {
        var results = new TestResults();
        
        // Run unit tests
        results.UnitTests = await RunUnitTestsAsync();
        
        // Run integration tests
        results.IntegrationTests = await RunIntegrationTestsAsync();
        
        // Run E2E tests
        results.E2ETests = await RunE2ETestsAsync();
        
        return results;
    }
}

Test Data Management

Manage test data lifecycle:

public class TestDataManager
{
    public async Task<TestData> CreateTestDataAsync(string scenario)
    {
        var data = new TestData
        {
            Shipments = await GenerateShipmentsAsync(100),
            Customers = await GenerateCustomersAsync(50),
            Products = await GenerateProductsAsync(200)
        };

        await StoreTestDataAsync(scenario, data);
        return data;
    }

    public async Task CleanupTestDataAsync(string scenario)
    {
        var data = await GetTestDataAsync(scenario);
        await DeleteTestDataAsync(data);
    }
}

Performance Testing

Load Testing

Test system under load:

public class LoadTester
{
    public async Task<LoadTestResults> RunLoadTestAsync(int concurrentUsers, TimeSpan duration)
    {
        var tasks = Enumerable.Range(0, concurrentUsers)
            .Select(i => SimulateUserAsync(duration))
            .ToArray();

        var results = await Task.WhenAll(tasks);
        
        return new LoadTestResults
        {
            TotalRequests = results.Sum(r => r.RequestCount),
            SuccessfulRequests = results.Sum(r => r.SuccessCount),
            FailedRequests = results.Sum(r => r.FailureCount),
            AverageResponseTime = results.Average(r => r.AverageResponseTime),
            P95ResponseTime = CalculatePercentile(results, 0.95)
        };
    }
}

Stress Testing

Test system limits:

public class StressTester
{
    public async Task<StressTestResults> RunStressTestAsync()
    {
        var load = 100;
        var results = new List<LoadTestResults>();

        while (true)
        {
            var result = await RunLoadTestAsync(load, TimeSpan.FromMinutes(5));
            results.Add(result);

            if (result.FailureRate > 0.1) // More than 10% failures
            {
                break; // System reached breaking point
            }

            load += 50; // Increase load
        }

        return new StressTestResults
        {
            MaxLoad = load - 50,
            Results = results
        };
    }
}

For more CargoWise integration guidance, explore our CargoWise eAdapter Integration Patterns or Error Handling & Retry Patterns.

Related posts