Make ServiceBus clients and objects testeable for the SDK users
See original GitHub issueAs of this moment, the main client, services and objects in Azure.Messaging.ServiceBus (track2) are not easily mockable, making it very difficult to unit test applications that use the SDK.
Making a SDK like this, that depends heavily on cloud environment to run and not being able to mock it to unit test our code is a bad design decision, specially since it would’ve been very easy to simply define interfaces for the clients, services and objects to make it possible.
I am starting new developments now and I used Microsoft.Azure.ServiceBus (track1) in my architecture tests, but was planning on switching to the new SDK when I saw it is the new way forward. This track1 SDK is barely mockable (I only had to do a little Reflection in the Message object to set a private property), but worked. The track2 SDK clients, services and objects don’t implement any interfaces that I could be mocking in my code, and the actual classes have LOTS of private inaccessible code, making it impractical or sometimes impossible to do the mocking by inheriting them.
The way I see, there are 2 possible paths to follow to “solve” this and improve things:
- option 1: define the required interfaces and change the classes to be implementations. To not break compatibility but allow for proper mock, the methods in the interfaces should expect only interfaces, structs or basic types and the current methods already in the classes should not change signature, but their implementation could move the the interface implementations [sample 1 bellow]
- option 2: make the current services, clients and objects properly inheritable, by turning the constructor, methods and properties overridable and create ways to tap into “private” code with “protected” methods when necessary (like when we need to raise an event).
sample 1 - ServiceBusProcessor current code snipet
public class ServiceBusProcessor: IAsyncDisposable{
public event Func<ProcessMessageEventArgs, Task> ProcessMessageAsync;
public event Func<ProcessErrorEventArgs, Task> ProcessErrorAsync;
public virtual Task StartProcessingAsync(CancellationToken cancellationToken = default){...}
}
proposed code snipet
public interface IProcessMessageEventArgs{
IServiceBusReceivedMessage Message {get;}
CancelationToken CancelationToken {get;}
}
public class ProcessMessageEventArgs : IProcessMessageEventArgs, EventArgs {
IServiceBusReceivedMessage IProcessMessageEventArgs.Message => this.Message;
public ServiceBusReceivedMessage Message {get;}
public CancelationToken CancelationToken {get;}
Task IProcessMessageEventArgs.CompleteMessageAsync(IServiceBusReceivedMessage message, CancellationToken cancellationToken = default){
...
}
public virtual Task CompleteMessageAsync(ServiceBusReceivedMessage message, CancellationToken cancellationToken = default) => CompleteMessageAsync(message, cancellationToken);
...
}
public interface IServiceBusProcessor: IAsyncDisposable{
public event Func<IProcessMessageEventArgs, Task> ProcessMessageAsync;
public event Func<IProcessErrorEventArgs, Task> ProcessErrorAsync;
public virtual Task StartProcessingAsync(CancellationToken cancellationToken = default){...}
}
public class ServiceBusProcessor: IServiceBusProcessor{
public virtual event Func<ProcessMessageEventArgs, Task> ProcessMessageAsync;
public virtual event Func<ProcessErrorEventArgs, Task> ProcessErrorAsync;
public virtual Task StartProcessingAsync(CancellationToken cancellationToken = default){...}
}
Issue Analytics
- State:
- Created 2 years ago
- Comments:13 (7 by maintainers)
Top GitHub Comments
There will be a beta version with these changes released next week.
@kelps the PR was merged in. Feel free to test it out using our dev feeds - https://github.com/Azure/azure-sdk-for-net/blob/master/CONTRIBUTING.md#nuget-package-dev-feed