Skip to content
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

Closed
wants to merge 7 commits into from

Conversation

ACHYUT001
Copy link

@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

@ghost ghost added Event Hubs customer-reported Issues that are reported by GitHub users external to the Azure organization. labels Jun 5, 2021
@ghost
Copy link

ghost commented Jun 5, 2021

Thank you for your contribution @ACHYUT001! We will review the pull request and get back to you soon.

@ghost ghost added the Community Contribution Community members are working on the issue label Jun 5, 2021
@ghost
Copy link

ghost commented Jun 5, 2021

CLA assistant check
All CLA requirements met.

@jsquire
Copy link
Member

jsquire commented Jun 7, 2021

Hi @ACHYUT001. Thanks very much for helping to improve the Azure SDK experience!

Should I cover all the scenarios as done in the publishing sample?

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:

  • How do I mock publishing a batch of events?

    • Demonstrating how to use Moq to setup the EventHubProducerClient to handle SendAsync
    • Demonstrating how to use the EventHubsModelFactory to create an EventDataBatch and use the callback to pass a result to TryAdd
  • How do I mock reading events from the EventHubConsumer?

    • Demonstrate how to use the EventHubsModelFactory to create an EventData` instance with the broker-owned properties set.
    • Demonstrate how to use Moq to setup the enumerable for ReadEventsFromPartitionAsync
  • How do I mock reading events from the PartitionReceiver?

    • Demonstrate how to use the EventHubsModelFactory to create an EventData` instance with the broker-owned properties set.
    • Demonstrate how to use Moq to setup the enumerable for ReceiveEventsAsync
  • 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

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 EventHubsModelFactory, as it's critical to the mocking story and oft overlooked.

mockEventProcessorClient
.Setup(c => c.StopProcessingAsync(It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask);

Copy link
Author

@ACHYUT001 ACHYUT001 Jun 27, 2021

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?

Copy link
Member

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);
}
Copy link
Author

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?

Copy link
Member

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

Copy link
Member

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"...

Copy link
Author

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

Copy link
Author

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 :)

Copy link
Member

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);

Copy link
Member

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var mockEventBatch = EventHubsModelFactory.EventDataBatch(mockBatchSizeInBytes, eventStore, null, x => true);
var mockEventBatch = EventHubsModelFactory.EventDataBatch(mockBatchSizeInBytes, eventStore, null, eventToAdd => true);

[Test]
public async Task ReadPartition()
{
await using var scope = await EventHubScope.CreateAsync(1);
Copy link
Member

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.

Assert.AreEqual(lastOffset, properties.Offset);
Assert.AreEqual(fakeDate, properties.EnqueuedTime);
Assert.AreEqual(fakeDate, properties.LastReceivedTime);
}
Copy link
Member

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

@ACHYUT001
Copy link
Author

@jsquire,
I have committed the discussed changes, please review and provide your thoughts :)
Thanks

[Test]
public async Task BasicEventProcessing()
{
#region Snippet:EventHubs_Processor_Sample07_MockingEventProcessingHandlers
Copy link
Member

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));
Copy link
Member

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?

Copy link
Author

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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I dont see the EventData constructor which accepts string:
image

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Copy link
Member

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. 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

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

Copy link
Member

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.

.ReturnsAsync(new string[1] { "sample id" });

var mockEventHubPartitionReceiver = new Mock<PartitionReceiver>();
var mockBatchData = GenerateBatchData();
Copy link
Member

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)
Copy link
Member

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)
Copy link
Member

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);
}
Copy link
Member

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();
Copy link
Member

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

@check-enforcer
Copy link

check-enforcer bot commented Jul 2, 2021

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:
/check-enforcer evaluate
Typically evaulation only takes a few seconds. If you know that your pull request is not covered by a pipeline and this is expected you can override Check Enforcer using the following command:
/check-enforcer override
Note that using the override command triggers alerts so that follow-up investigations can occur (PRs still need to be approved as normal).

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:

/azp run prepare-pipelines
This will run a pipeline that analyzes the source tree and creates the pipelines necessary to build and validate your pull request. Once the pipeline has been created you can trigger the pipeline using the following comment:
/azp run net - [service] - ci

For track 1 management-plane SDKs

Please 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
///
[Test]
public async Task MockingReadEventsFromPartition_WithLastEnqueuedEventProperties()
{
Copy link
Author

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

Copy link
Author

@ACHYUT001 ACHYUT001 Jul 2, 2021

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.

Copy link
Member

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?

Copy link
Author

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?

Copy link
Member

@jsquire jsquire Jul 19, 2021

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();
Copy link
Author

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?

Copy link
Member

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.

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);
Copy link
Member

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.

Copy link
Author

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();
Copy link
Member

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()
{
Copy link
Member

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);
}
Copy link
Member

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));
Copy link
Member

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.

@andreyshihov
Copy link
Contributor

andreyshihov commented Aug 31, 2021

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

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.

@jsquire
Copy link
Member

jsquire commented Sep 13, 2021

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.

@jsquire jsquire closed this Sep 13, 2021
azure-sdk pushed a commit to azure-sdk/azure-sdk-for-net that referenced this pull request Dec 9, 2022
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. Event Hubs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants