CargoWise Integration Testing Strategies: Complete Guide 2025
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:
- Follow testing pyramid - More unit tests, fewer E2E tests
- Use sandbox environments - Test against real CargoWise APIs
- Mock external dependencies - Isolate components for unit testing
- Automate testing - Integrate into CI/CD pipeline
- Test error scenarios - Don't just test happy paths
- Measure coverage - Track test coverage metrics
- Performance testing - Test under load
- Property-based testing - Comprehensive coverage
- Chaos engineering - Test resilience
- Contract testing - API compatibility
Next Steps:
- Set up test infrastructure
- Write unit tests for core logic
- Create integration test suite
- Configure sandbox environment
- Automate testing in CI/CD
- Implement performance testing
- 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.