-
Notifications
You must be signed in to change notification settings - Fork 282
Add MessageGateway test generator #3996
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Pull Request Review: Add MessageGateway Test GeneratorSummaryThis PR introduces a test generator for messaging gateway implementations, extending the existing test generation infrastructure to reduce code duplication across different messaging providers (RabbitMQ, AWS SNS/SQS, Azure Service Bus, etc.). The implementation follows the established pattern from the Outbox generator and includes comprehensive documentation via ADR 0037. Positive Aspects ✅Architecture & Design
Code Quality
Issues & Concerns 🔴1. Critical: Null Reference Issues in DefaultMessageFactory.csLocation: The public Message Create(MessageConfiguration? configuration = null)
{
var messageHeader = new MessageHeader(
messageId: configuration.MessageId, // ⚠️ NullReferenceException if configuration is null
topic: configuration.Topic,
// ... all other properties accessed without null checkFix: Either:
Severity: HIGH - This will cause runtime exceptions 2. Code Style Violation: Inconsistent SpacingLocation: The public class MessageConfiguration
{
public Dictionary<string, object> Bag { get; set; } = new() { ... };
public Baggage Baggage { get; set; } = new(); // ⚠️ 2 spaces instead of 4
public ContentType ContentType { get; set; } = ... // ⚠️ InconsistentFix: Use consistent 4-space indentation for all properties Severity: MEDIUM - Code style violation 3. Typo in Configuration Property NameLocation: Property name has a typo: public Dictionary<string, MessagingGatewayConfiguration>? MessagingGatewaies { get; set; }Impact: This affects the public API of the configuration and should be corrected before release Severity: MEDIUM - Breaking change required 4. Hard-coded Delay ValuesLocation: The generated test has hard-coded delays:
While the configuration has Severity: LOW - Test maintainability issue 5. Missing XML Documentation CommentLocation: The Severity: LOW - Documentation completeness 6. Commented-out Code in GeneratorLocation: // await GenerateAsync(
// configuration,
// Path.Combine("MessagingGateway", prefix, "Generated", "Reactor"),
// Path.Combine("MessagingGateway", "Reactor"),
// configuration.MessagingGateway,
// filename => SkipTest(configuration.MessagingGateway, filename)
// );Questions:
Severity: LOW - Code cleanliness 7. Empty Skip Test ImplementationLocation: private static bool SkipTest(MessagingGatewayConfiguration configuration, string fileName)
{
return false;
}This always returns
Severity: LOW - Potential missing functionality Test Coverage Concerns
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No application code in the PR — skipped Code Health checks.
See analysis details in CodeScene
Quality Gate Profile: Clean Code Collective
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.
Pull Request Review: Add MessageGateway Test GeneratorSummaryThis PR successfully extends the test generator tool to support messaging gateway tests, following the established pattern from the Outbox generator. The implementation is well-architected, consistent with the existing codebase, and includes comprehensive documentation via ADR 0037. ✅ StrengthsArchitecture & Design
Code Quality
Testing Infrastructure
🔍 Issues & Concerns1. Copy-paste error in log message (Minor)Location: logger.LogInformation("Generating outbox test for {OutboxName}", key);Should be: logger.LogInformation("Generating messaging gateway test for {GatewayName}", key);2. Commented-out code (Minor)Location: Reactor pattern generation is commented out for single gateway configuration but enabled for multiple gateways (lines 87-93). This inconsistency suggests:
Recommendation: Remove commented code or add a TODO comment explaining why Reactor is disabled. 3. Typo in property name (Medium)Location: public Dictionary<string, MessagingGatewayConfiguration>? MessagingGateways { get; set; }Property is named 4. Potential null reference issues (Low)Location: Generated test template line 698 _subscription = _messageGatewayProvider.CreateSubscription(_publication.Topic!, ...)Using null-forgiving operator ( 5. Hardcoded delay values (Medium - Performance)Location: Generated test at lines 717 and 732 await Task.Delay(5000); // Hardcoded 5-second delayThe test hardcodes a 5-second delay even though the configuration specifies Recommendation: Use the configuration value from 6. Empty SkipTest implementation (Low)Location: private static bool SkipTest(MessagingGatewayConfiguration configuration, string fileName)
{
return false;
}This stub exists in
7. Missing validation (Low)Location: The configuration properties
💡 Suggestions for Improvement1. Test CoverageThe PR only includes one generated test:
2. Partition Key HandlingLocation: Line 707-710 of generated test _messageFactory.Create(new MessageConfiguration{Topic = _publication.Topic, PartitionKey = null}),The partition key is explicitly set to null. This seems intentional but isn't documented. Consider:
3. Resource Cleanup OrderingLocation: Channel is disposed synchronously ( 🔒 Security ConsiderationsNo security concerns identified. The generated tests use localhost RabbitMQ connections with guest credentials, which is appropriate for test environments. ⚡ Performance ConsiderationsTest execution time: The hardcoded 5-second delays mean each 4-message test takes ~20+ seconds. For large test suites, this could be significant. Consider:
📋 Documentation & MaintainabilityExcellent documentation overall. The ADR is thorough and includes:
Minor improvement: Add a section in the ADR about how to add new test scenarios in the future. 🎯 RecommendationsMust Fix (Before Merge)
Should Fix (Before or Shortly After Merge)
Nice to Have (Future Work)
✅ Final AssessmentRecommendation: APPROVE with minor changes This is a high-quality PR that:
The issues identified are mostly minor and can be addressed in follow-up commits or as part of this PR. The core architecture is sound and ready for use. Estimated risk level: Low Great work @lillo42! This is a valuable addition to the test infrastructure. 🎉 |
|
I just want to say how excited I am for this. |
iancooper
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can think of a few other points, but let's start with these.
| - Generates tests from Liquid templates | ||
| - Follows the same pattern as `OutboxGenerator` for consistency | ||
|
|
||
| ### 3. Updated Message Factories and Assertions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I note that I have had more success when running the Dispatcher using a handler that takes the FakeTimeProvider to advance the time, rather than using Task Delay, when timing is being used to trigger a behaviour. It's possible a few tests could benefit from this
|
|
||
| ### Negative | ||
|
|
||
| - **Learning Curve**: Developers need to understand the generator and template system |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where to start when you want to add a new feature to our MessagingGateways as well. Much harder to iterate on in-memory and then roll out. We need to document the recommended loop
| _messageAssertion = new DefaultMessageAssertion(); | ||
| } | ||
|
|
||
| public Task InitializeAsync() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems a little bit unnecessary. What forces this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's part of the IAsyncLifeTime
| if (subscription.MakeChannels == OnMissingChannel.Create) | ||
| { | ||
| // Ensuring that the queue exists before return the channel | ||
| await channel.ReceiveAsync(TimeSpan.FromMilliseconds(100), cancellationToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems problematic to have to use Receive to force the call to EnsureChannel. I wonder if we need an Init method for a channel that also calls EnsureChannel. (Receive should still call EnsureChannel, but we would have an explicit Init too)
| ); | ||
| } | ||
|
|
||
| public ChannelName GetOrCreateChannelName([CallerMemberName] string? testName = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this, over a property set via the constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'll set the test name at compilation time, it's an option I prefer via this attribute because I don't need to force those who implement it to have a constructor
| /// <summary> | ||
| /// Default implementation of <see cref="IAmAMessageFactory"/> that creates messages with randomly generated test data. | ||
| /// </summary> | ||
| public class DefaultMessageFactory : IAmAMessageFactory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use the Builder pattern here, which would allow you to have default values and then override properties: https://blog.ploeh.dk/2017/08/21/generalised-test-data-builder/
| /// <summary> | ||
| /// Defines a contract for asserting equality between two Message instances. | ||
| /// </summary> | ||
| public interface IAmAMessageAssertion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this an interface? Not sure I understand the thinking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to assert the received message against the provided message. In some message gateways like RabbitMQ, the MessageId will be different on message Requeue, so I want to use this interface to cover this cases
| /// <summary> | ||
| /// Provides configuration for creating test messages with customizable header values and body content. | ||
| /// </summary> | ||
| public class MessageConfiguration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this. A message has a wide range of defaults after all
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll move to a build, as suggested in the previous comment
Overview
Extends the \Paramore.Brighter.Test.Generator` tool to automatically generate standardized tests for messaging gateway implementations (RabbitMQ, AWS SNS/SQS, Azure Service Bus, etc.), reducing code duplication and
ensuring consistent test coverage across all gateway implementations.
What's Changed
Configuration System
MessagingGatewayandMessagingGatewaiespropertiesGenerator Implementation
Message Factory & Assertions
IAmAMessageFactoryinterface andDefaultMessageFactoryimplementationIAmAMessageAssertioninterface andDefaultMessageAssertionimplementationCreatedMessagespropertyTemplates
MessagingGateway/Proactor/- Async test templatesMessagingGateway/Reactor/- Sync test templatesDocumentation
Benefits