-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Mocking EventHub Publishing Samples #21630
Conversation
Thank you for your contribution @ACHYUT001! We will review the pull request and get back to you soon. |
Hi @ACHYUT001. Thanks very much for helping to improve the Azure SDK experience!
I don't think that we need to go over each of the sample scenarios. I see the goal for the mocking samples as demonstrating the high-level concept for the major scenarios. For example, the most important would be:
The issue outlines some of the other types around Event Hubs metadata, which we can demonstrate using the producer or consumer. Generally speaking, we just want to help people realize that they don't need an interface to enable mocking - the virtual members can be mocked the same way. We also want to help them find the |
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample09_MockingEventHubPublisher.cs
Outdated
Show resolved
Hide resolved
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample09_MockingEventHubPublisher.cs
Outdated
Show resolved
Hide resolved
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample09_MockingEventHubPublisher.cs
Outdated
Show resolved
Hide resolved
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample09_MockingEventHubPublisher.cs
Outdated
Show resolved
Hide resolved
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample10_MockingReadingEventsTests.cs
Show resolved
Hide resolved
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample10_MockingReadingEventsTests.cs
Show resolved
Hide resolved
mockEventProcessorClient | ||
.Setup(c => c.StopProcessingAsync(It.IsAny<CancellationToken>())) | ||
.Returns(Task.CompletedTask); | ||
|
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.
@jsquire ,
How do I mock invoking my event and error handlers for the EventProcessorClient? (separate sample in the processor package)
Demonstrate using the EventHubsModelFactory to create the types needed by the event args
I'm having trouble understanding the scenario here, could you please help me out, what is it that we should be mocking?
processEventHanlder and processErrorHandler?
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 don't think there's much value in trying to use the processor client for invoking the handlers; they're owned by developer code so invoking them directly is a better strategy. I was suggesting that we demonstrate calling the handler with the args
needed for each.
For example, the ProcessEventArgs
constructor can be called directly, but needs an EvenrData
and PartitionContext
that should be created using the model factory. My thought was that it would be helpful to see the args constructed so that developers know how to call their handlers for testing purposes.
Assert.AreEqual(lastOffset, properties.Offset); | ||
Assert.AreEqual(fakeDate, properties.EnqueuedTime); | ||
Assert.AreEqual(fakeDate, properties.LastReceivedTime); | ||
} |
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.
Should we assert the values here?
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 should probably do a "call the metadata method, assert the values" pattern, interleaving the calls. Something like:
- Get Event Hub Properties
- Assert Event Hub Properties
- Get Partition Properties
- Assert Partition Properties
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.
On second thought, we can probably just show the query and then leave a comment for "the stuff you just read matches the stuff that you mocked"...
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.
removed the asserts and added comments
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.
please let me know if we should change the language of the comments :)
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 think the high level of them is great. We'll eventually want to go with proper capitalization and punctuation to match the Event Hubs codebase. Here, too, we'll need to double-check that our line lengths fit into the GitHub width to avoid scrolling.
(that's easier to do when looking at the markdown, so I suggest we leave it until that point)
mockEventProcessorClient | ||
.Setup(c => c.StopProcessingAsync(It.IsAny<CancellationToken>())) | ||
.Returns(Task.CompletedTask); | ||
|
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 don't think there's much value in trying to use the processor client for invoking the handlers; they're owned by developer code so invoking them directly is a better strategy. I was suggesting that we demonstrate calling the handler with the args
needed for each.
For example, the ProcessEventArgs
constructor can be called directly, but needs an EvenrData
and PartitionContext
that should be created using the model factory. My thought was that it would be helpful to see the args constructed so that developers know how to call their handlers for testing purposes.
|
||
var mockBatchSizeInBytes = 1024; | ||
var eventStore = new List<EventData>(); | ||
var mockEventBatch = EventHubsModelFactory.EventDataBatch(mockBatchSizeInBytes, eventStore, null, x => true); |
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.
var mockEventBatch = EventHubsModelFactory.EventDataBatch(mockBatchSizeInBytes, eventStore, null, x => true); | |
var mockEventBatch = EventHubsModelFactory.EventDataBatch(mockBatchSizeInBytes, eventStore, null, eventToAdd => true); |
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample09_MockingEventHubPublisherTests.cs
Outdated
Show resolved
Hide resolved
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample09_MockingEventHubPublisherTests.cs
Outdated
Show resolved
Hide resolved
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample10_MockingReadingEventsTests.cs
Outdated
Show resolved
Hide resolved
[Test] | ||
public async Task ReadPartition() | ||
{ | ||
await using var scope = await EventHubScope.CreateAsync(1); |
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 line won't work in the current form; either we'll need to mark the class as "Live"
[TestFixture]
[Category(TestCategory.Live)]
[Category(TestCategory.DisallowVisualStudioLiveUnitTesting)]
public class Sample10_MockingReadingEventsTests
Or we'll want to make sure that we don't rely on the live scope here.
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample10_MockingReadingEventsTests.cs
Show resolved
Hide resolved
Assert.AreEqual(lastOffset, properties.Offset); | ||
Assert.AreEqual(fakeDate, properties.EnqueuedTime); | ||
Assert.AreEqual(fakeDate, properties.LastReceivedTime); | ||
} |
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 should probably do a "call the metadata method, assert the values" pattern, interleaving the calls. Something like:
- Get Event Hub Properties
- Assert Event Hub Properties
- Get Partition Properties
- Assert Partition Properties
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample10_MockingReadingEventsTests.cs
Show resolved
Hide resolved
@jsquire, |
[Test] | ||
public async Task BasicEventProcessing() | ||
{ | ||
#region Snippet:EventHubs_Processor_Sample07_MockingEventProcessingHandlers |
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.
The concept is solid, and I think this will be helpful! I wonder if we should consider breaking this up into two snippets, one for each handler. We've found that our smaller and more focused snippets tend to be considered easier for folks browsing.
.ReturnsAsync(new string[] { mockPartitionId }); | ||
mockEventHubConsumerClient | ||
.Setup(c => c.ReadEventsFromPartitionAsync(It.IsAny<string>(), It.IsAny<EventPosition>(), It.IsAny<CancellationToken>())) | ||
.Returns(GetMockEventsFromPartition(mockPartitionId)); |
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 may be a bit tough to include in a snippet and, though it's fairly obvious it's meant to illustrate, it isn't "copy/paste" ready which often frustrates folks. Maybe we should consider making this a local function that generates inline so that we can easily package it with the snippet? There's a new constructor overload for EventData
that just takes a string which should help streamline this kind of illustration.
I'm thinking something like:
async IAsyncEnumerable<PartitionEvent> GetMockEvents()
{
var partitionContext = EventHubsModelFactory.PartitionContext(partitionId,);
await Task.Yield();
yield return new PartitionEvent(partitionContext, new EventData("One"));
yield return new PartitionEvent(partitionContext, new EventData("Two"));
yield return new PartitionEvent(partitionContext, new EventData("Three"));
}
Of course, that would lead us to wanting to either have another sample to demonstrate the last enqueued event properties or just a small bit of text in the sample itself that mentions it. I suppose we could also just model it here, but I was trying to keep this to the minimum needed. Thoughts?
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 see what you mean, it'd be better to keep it to a minimum, so that it's easier to understand.
I could always write an overload to the GetMockEvents()
with last enqueued event and use that for a new sample.
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.
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.
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.
Rebase on main
, it's a recent commit. 😄
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.
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.
updated the function to generate EventData
inline
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.
Cool, it looks good! Now we just need to hoist it into the snippet as a local function so that everything is available to the reader at a glance.
sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample10_MockingReadingEventsTests.cs
Show resolved
Hide resolved
.ReturnsAsync(new string[1] { "sample id" }); | ||
|
||
var mockEventHubPartitionReceiver = new Mock<PartitionReceiver>(); | ||
var mockBatchData = GenerateBatchData(); |
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 should consider showing this inline.
|
||
string firstPartition; | ||
|
||
await using (var producer = mockEventHubProducerClient.Object) |
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 think we can bypass this part, since we're mocking the receiver directly. Since we control the partition that we're accepting, then we don't need to show the flow of querying one.
cancellationSource.Token); | ||
|
||
//do we need assertion like this? | ||
for (var index = 0; index < mockBatchData.Count(); ++index) |
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 probably don't need the assert; a comment would cover it well enough, I think.
Assert.AreEqual(lastOffset, properties.Offset); | ||
Assert.AreEqual(fakeDate, properties.EnqueuedTime); | ||
Assert.AreEqual(fakeDate, properties.LastReceivedTime); | ||
} |
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.
On second thought, we can probably just show the query and then leave a comment for "the stuff you just read matches the stuff that you mocked"...
private async IAsyncEnumerable<PartitionEvent> GetMockEventsFromPartition(string partitionId,LastEnqueuedEventProperties lastEnqueuedEventProperties = default) | ||
{ | ||
// we are using this as we dont have an async operation in our test | ||
await Task.Yield(); |
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.
Something like this would make a good stand-alone sample snippet for "how to mock the the async enumerable with last enqueued properties"; we should consider using a simplified form inline with the "read events" snippets, and having this be it's own
added assert statement on expected values
added demos to create event hub metadata types
refactored code for metadata demos
This pull request is protected by Check Enforcer. What is Check Enforcer?Check Enforcer helps ensure all pull requests are covered by at least one check-run (typically an Azure Pipeline). When all check-runs associated with this pull request pass then Check Enforcer itself will pass. Why am I getting this message?You are getting this message because Check Enforcer did not detect any check-runs being associated with this pull request within five minutes. This may indicate that your pull request is not covered by any pipelines and so Check Enforcer is correctly blocking the pull request being merged. What should I do now?If the check-enforcer check-run is not passing and all other check-runs associated with this PR are passing (excluding license-cla) then you could try telling Check Enforcer to evaluate your pull request again. You can do this by adding a comment to this pull request as follows: What if I am onboarding a new service?Often, new services do not have validation pipelines associated with them. In order to bootstrap pipelines for a new service, please perform following steps: For data-plane/track 2 SDKs Issue the following command as a pull request comment:
For track 1 management-plane SDKsPlease open a separate PR and to your service SDK path in this file. Once that PR has been merged, you can re-run the pipeline to trigger the verification. |
removed unnecessary mocks and assert statements added batch event generation inline
...e.Messaging.EventHubs.Processor/tests/Snippets/Sample07_MockingEventProcessorHandlersTest.cs
Show resolved
Hide resolved
/// | ||
[Test] | ||
public async Task MockingReadEventsFromPartition_WithLastEnqueuedEventProperties() | ||
{ |
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.
added a new test method to demonstrate reading events with last enqueued event properties, this uses the GetsMockEventFromPartition
overload with lastEnqueuedEventPartitions
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.
please let me know if we need this test method or just the local GetsMockEventFromPartition
overload with lastEnqueuedEventPartitions
is enough.
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.
If we make a local function out of GetMockEventsFromPartition
and demonstrate calling it with the last enqueued mock, I think that's a good way to focus on what we're trying to highlight here. We can probably kill the consumer bits, call GetMockEventsFromPartition
and demonstrate using the partition context to return the mocked data. Maybe we consider a trailing comment on being able to use this pattern while mocking out consumers.
What are your thoughts?
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.
just reiterating/ thinking out loud here :
we should not mock the consumer, and call the GetMockEventsFromPartition()
directly and use that to return the mocked data using the returned partition context?
What scenario's would one want to not use this pattern?
Is the idea to demonstrate using PartitionContext
to fetch mock data w/o using the ConsumerClient
?
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 want each snippet to be as self-contained as possible; ideally, a developer should be able to copy/paste the sample and it should compile/run without having to hunt down common utility methods in other snippets. For clarity, it's better to demonstrate a concept in isolation than to introduce complexity with trying to demonstrate too many things at once.
If we feel that showing the generation doesn't work without the consumer bits, I'm good with that. We'll want to break things down into multiple snippets that we can render back-toback since there's a clear functionality delineation, which also potentially allows us to reuse the same utility method snippet elsewhere. For example:
#region Snippet:EventHubs_Sample10_GeneratingMockEvents
public async IAsyncEnumerable<PartitionEvent> GetMockEventsFromPartition(int partitionId)
{
var partitionContext = EventHubsModelFactory.PartitionContext(partitionId);
await Task.Yield();
yield return new PartitionEvent(partitionContext, new EventData("One"));
yield return new PartitionEvent(partitionContext, new EventData("Two"));
yield return new PartitionEvent(partitionContext, new EventData("Three"));
}
#endregion
#region Snippet:EventHubs_Sample10_UsingMockEventsWithTheConsumer
// .. THE CONSUMER BITS
#endregion
For a more complex illustration, we break things down in a couple of different places, in different ways:
-
In our basic Hello World sample, we introduce creating clients in their own snippet and mention that we're assuming this pattern below for the more complex cases. This works because it's simple and foundational.
-
In the Migration Guide, we demonstrate moving checkpoints in a few snippets that appear together in a section, where each builds on the other. We do this to try and reduce complexity by talking over independent parts independently. Here, things are tightly clustered because they are somewhat complex and their usage is really isolated to this particular scenario.
cancellationSource.Token)) | ||
{ | ||
string readFromPartition = partitionEvent.Partition.PartitionId; | ||
ReadOnlyMemory<byte> eventBodyBytes = partitionEvent.Data.EventBody.ToMemory(); |
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.
just a thought do we need to add an assert or comment here, to point out the mocked data is being returned?
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.
Good call. I'm comfortable with what you decide; if it were me, I'd probably go the lazy route and just add a comment.
...e.Messaging.EventHubs.Processor/tests/Snippets/Sample07_MockingEventProcessorHandlersTest.cs
Show resolved
Hide resolved
var mockEventBody = new BinaryData("This is a sample event body"); | ||
var mockEventData = EventHubsModelFactory.EventData(mockEventBody); | ||
|
||
var mockProcessEventArgs = new ProcessEventArgs(mockPartitionContext, mockEventData, updateToken => Task.CompletedTask, CancellationToken.None); |
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.
Once we get the overall form to the point that we're comfortable, we'll want to keep an eye on line length; the samples get read on GitHub more often than downloaded. We've had some strong feedback to earlier samples that we needed to make that we fit into the GitHub width to keep things readable.
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.
sounds reasonable, so I should probably push updateToken to next line then...
cancellationSource.Token)) | ||
{ | ||
string readFromPartition = partitionEvent.Partition.PartitionId; | ||
ReadOnlyMemory<byte> eventBodyBytes = partitionEvent.Data.EventBody.ToMemory(); |
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.
Good call. I'm comfortable with what you decide; if it were me, I'd probably go the lazy route and just add a comment.
/// | ||
[Test] | ||
public async Task MockingReadEventsFromPartition_WithLastEnqueuedEventProperties() | ||
{ |
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.
If we make a local function out of GetMockEventsFromPartition
and demonstrate calling it with the last enqueued mock, I think that's a good way to focus on what we're trying to highlight here. We can probably kill the consumer bits, call GetMockEventsFromPartition
and demonstrate using the partition context to return the mocked data. Maybe we consider a trailing comment on being able to use this pattern while mocking out consumers.
What are your thoughts?
Assert.AreEqual(lastOffset, properties.Offset); | ||
Assert.AreEqual(fakeDate, properties.EnqueuedTime); | ||
Assert.AreEqual(fakeDate, properties.LastReceivedTime); | ||
} |
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 think the high level of them is great. We'll eventually want to go with proper capitalization and punctuation to match the Event Hubs codebase. Here, too, we'll need to double-check that our line lengths fit into the GitHub width to avoid scrolling.
(that's easier to do when looking at the markdown, so I suggest we leave it until that point)
.ReturnsAsync(new string[] { mockPartitionId }); | ||
mockEventHubConsumerClient | ||
.Setup(c => c.ReadEventsFromPartitionAsync(It.IsAny<string>(), It.IsAny<EventPosition>(), It.IsAny<CancellationToken>())) | ||
.Returns(GetMockEventsFromPartition(mockPartitionId)); |
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.
Cool, it looks good! Now we just need to hoist it into the snippet as a local function so that everything is available to the reader at a glance.
Hi @jsquire, I have just created draft PR #23677 to demonstrate use of Factory in the Handler tests. Am I moving in the right direction? Please share your thoughts. These are just first steps to understand if it conceptually makes sense. |
Hi @ACHYUT001. Thanks very much for your contribution and helping to improve the Azure SDK experience. Since this work stream has been inactive for a while and another community member has expressed interest in taking this on, I'm going to close this out. I appreciate your efforts and would welcome the opportunity to collaborate again in the future should you wish to do so. |
CloudServiceRP 2022-09-04 version (Azure#21630) * create 2022-09-04 cloudService Folder * update references to new version * update readme and fix common.json divergence * Adding zones, example and renaming IP to Ip in cloudservices swagger (Azure#21232) * Adding zones, example and renaming frontendIPConfigurations to frontendIpConfigurations, LoadBalancerFrontendIPConfiguration to LoadBalancerFrontendIpConfiguration, LoadBalancerFrontendIPConfigurationProperties to LoadBalancerFrontendIpConfigurationProperties * resolving CI validations * changing privateIPAddress to privateIpAddress, publiCIPAddress to ppublicIpAddress * reverting publicIpAddress, privateIpAddress to privateIPAddress, publicIPAddress * removing x-ms-client-flatten and parameter order change Co-authored-by: Theodore Chang <theodore.l.chang@gmail.com> Co-authored-by: ashvermamsft <70934942+ashvermamsft@users.noreply.github.com>
@jsquire ,
I've started out writing the mocks.
Here are a few for Publishing, requesting your thoughts.
Should I cover all the scenarios as done in the publishing sample?
I also needed your thoughts in understanding the points to cover in the markdown file of the sample.
Thanks