How to unit test if consumer ran successfully in MassTransit - masstransit

Im trying to write a unit test that should validate if a consumer ran or not.
But for some reason the consumer never executes.
I have created a more simplified version of the problem using the Getting-Started-sample of MassTransit.
This is "my" code:
[TestClass]
public class UnitTest1
{
private ITestHarness _testHarness;
[TestInitialize]
public void Initialize()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddMassTransitTestHarness(busRegistrationConfigurator =>
{
busRegistrationConfigurator.AddConsumer<MessageConsumer>();
});
var serviceProvider = serviceCollection.BuildServiceProvider();
_testHarness = serviceProvider.GetRequiredService<ITestHarness>();
}
[TestMethod]
public async Task TestMethod1()
{
await _testHarness.Bus.Publish(new Message { Text = "Hello, world!" });
(await _testHarness.Published.Any<Message>()).Should().BeTrue();
(await _testHarness.Consumed.Any<Message>()).Should().BeTrue();
}
}
The following assertion:
(await _testHarness.Consumed.Any<Message>()).Should().BeTrue();
Fails, since it always returns False.
I guess that I somehow need to await the consumer to execute? And I might be missing even more..
Been having a look at the documentations of MassTransit regarding testing, but Im not sure if the sample provided in the docs apply to this scenario (?), since the sample provided involves a request and response.
Would very much appreciate any help!

You're missing one line:
await _testHarness.Start();
Of course, you'll need to be sure and Dispose the container to ensure the bus is stopped and cleaned up.

Related

MassTransit failing to consume

I'm switching some code which uses MassTransit (v7.2.2 on .NET 5) to use a more declarative format (and away from multiple calls to ReceiveEndpoint()) and ideally to using ConsumerDefinitions for the configuration (though not part of this example for simplicity), along with some Dependency Injection with Quartz.NET (yanked from this example, though it running 3.3.3), in doing so I find now that my Consumers are not consuming, despite messages being sent and examples referenced. Take the following standing up of the MassTransit service:
var hostBuilder = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddMassTransit(mt =>
{
mt.AddBus(provider => Bus.Factory.CreateUsingInMemory(cfg =>
{
//cfg.AutoStart = true; //No change when on
cfg.UseInMemoryOutbox();
cfg.ConfigureEndpoints(provider);
}));
mt.AddConsumer<TheMessageConsumer>();
services.AddMediator(cfg =>
{
cfg.AddConsumer<TheMessageConsumer>();
});
});
services.AddMassTransitHostedService();
});
var host = hostBuilder.Build();
var busControl = host.Services.GetService<IBusControl>();
busControl.Start(); //Just in case
var message = new TheMessage() { Message = $"<Message-{DateTime.Now.ToLongTimeString()}>" };
Console.WriteLine($"Sending: {message.Message}");
await busControl.Publish(message);
host.Run();
For note, the breaking out of the message sending here is to simplify my repro, as in my full code base, it's being sent by a Quartz fired job.
For this example, the message & receiver are also quite simple:
public class TheMessage
{
public string Message { get; set; }
}
public class TheMessageConsumer : IConsumer<TheMessage>
{
public Task Consume(ConsumeContext<TheMessage> context)
{
Console.WriteLine($"Message received: {context.Message.Message}");
return Task.CompletedTask;
}
}
The bus is started, either in the case I explicitly start it, the AutoStart flag is set, or the MassTransitHostedService does it, yet the message doesn't get received. Ditto if I have the full example with Quartz firing off a job with messages much later.
Can someone suggest what I am missing?
The code you posted is seriously a hodgepodge of snippets, none of which make any sense when used together. For example, AddBus is deprecated, and mediator has no business being in that project at all.
I'd suggest using one of the MassTransit Templates to create a new project from scratch (you may need to up the NuGet versions to 7.2.2).
Watch this video which explains the templates and how to use them.
With your comment, and updated question, you really only need the following:
var hostBuilder = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddMassTransit(mt =>
{
mt.AddConsumer<TheMessageConsumer>();
mt.UsingInMemory((context,cfg) =>
{
cfg.UseInMemoryOutbox();
cfg.ConfigureEndpoints(context);
}));
});
services.AddMassTransitHostedService();
});
var host = hostBuilder.Build();
await host.RunAsync();
If you want to test it, and send messages in the same process, simply add a BackgroundService (after AddMassTransitHostedService) to publish your messages.
You shouldn't publish until after the bus has been started.

Masstransit unti tests problem and ISendEndpointProvider within DI

I want to unit test services which uses Masstransit(rmq) to send messages. According to the doc, there is InMemoryTestHarness class for this. But I can't figure out how can I use it in my scenario.
I use AbpBoilerplate framework, so first of all I need to register Masstransit within Castle Windsor. For test module I do it smth like this:
var harness = new InMemoryTestHarness();
harness.Start().Wait(TimeSpan.FromSeconds(1));//harness.Bus is not null now
IocManager.IocContainer.Register(Component.For<InMemoryTestHarness>().Instance(harness));
IocManager.IocContainer.Register(Component.For<IBus, IBusControl>().Instance(harness.Bus));
Service I want to test is following:
public class ProcessingService
{
private readonly ProjectService _projectService;
//NOTE previously there was IBus interface...
private readonly ISendEndpointProvider _provider;
public ProcessingService(ISendEndpointProvider provider, [NotNull] ProjectService projectService )
{
_ProjectService = ProjectService ?? throw new ArgumentNullException(nameof(ProjectService));
_provider = provider;
}
public async Task ProcessFileAsync([NotNull] fileInput FileInput,
[NotNull] IUserProcessingSettings userProcessingSettings)
{
if (fileInput == null) throw new ArgumentNullException(nameof(fileInput));
if (userProcessingSettings == null) throw new ArgumentNullException(nameof(userProcessingSettings));
var entity = await _projectService.GetSomeEntityAsync();
ProcessFileCommand msg = new ProcessFileCommand(entity);
await _provider.Send(msg).ConfigureAwait(false);
}
}
The first problem with code above is that for now if I use ISendEndpointProvider instead of IBus as suggeted in the doc it is simply cannot resolve dependecies, no ISendEndpointProvider registered. And I'm not quite understand how to handle it within InMemoryTestHarness abstraction...
Here is the test method and consumer class:
[Fact]
public async Task Simple_Masstranist_Consumer_Test1()
{
//it was injected and started in ABP test module
var harness = Resolve<InMemoryTestHarness>();
//NOTE which one is correct option -- handler or consumer?
var consumerHarness = harness.Consumer<MyConsumer>();
// await harness.SubscribeHandler<MyConsumer>().ConfigureAwait(false);
try
{
var processingService = Resolve<ProcessingService>();
var emptyFileImput = FileInput.EmptyFileInput(1);
await ProcessingService.ProcessFileAsync(emptyFileImput).ConfigureAwait(false);
//NOTE test hangs on this command
consumerHarness.Consumed.Select<ProcessFileCommand>().Any().ShouldBeTrue();
// the consumer publish the event
harness.Published.Select<ProcessFileCommand>().Any().ShouldBeTrue();
}
finally
{
//already started...
await harness.Stop().ConfigureAwait(false);
}
}
public class MyConsumer : IConsumer<ProcessFileCommand>
{
public Task Consume(ConsumeContext<ProcessFileCommand> context)
{
context.Message.ShouldNotBeNull();
context.Message.EntityId.ShouldBe(1);
context.Message.FilePath.ShouldBe("some_path");
return Task.FromResult(true);
}
}
My expectations for InMemoryTestHarness is that it is a wrapper of InMemory bus suitable for tests, so when my service send some message, I expect that it would be reflected in proper structures of InMemoryTestHarness. When I've used IBus instead of ISendEndpointProvider test code simply hang on
consumerHarness.Consumed.Select().Any().ShouldBeTrue(); line of code. It seems that it blocks for some event which will neven occur.
So, my questions are:
Is it correct to use InMemoryTestHarness to mock message queue in my scenario, maybe I should configure manually InMemoryBus for that?
2)If I want to use ISendEndpointProvider within DI, what should I register?
Thanks in advance
Update:
var testHarness = new InMemoryTestHarness();
IocManager.IocContainer.Register(Component.For<InMemoryTestHarness>().UsingFactoryMethod(kernel =>
{
var busRegistrationContext = kernel.Resolve<IRegistration>();
testHarness.OnConfigureInMemoryBus +=
configurator => configurator.ConfigureEndpoints(busRegistrationContext);
return testHarness;
}).LifestyleSingleton());
You can use the built-in container configuration with the test harness, I've included an example of how to do it with Castle Windsor below. This will ensure all required types are registered in the container, including IBus, ISendEndpointProvider, and IPublishEndpoint.
You do not need to start the bus, the harness starts and creates it for you.
Also, do not access IBusControl prior to resolving and starting the test harness, or the BusControl property on the harness will not be initialized, returning null.
[Test]
public async Task Should_startup_the_container_with_the_harness()
{
var container = new WindsorContainer()
.Register(Component.For<InMemoryTestHarness>().UsingFactoryMethod(kernel =>
{
var testHarness = new InMemoryTestHarness();
var busRegistrationContext = kernel.Resolve<IBusRegistrationContext>();
testHarness.OnConfigureInMemoryBus += configurator => configurator.ConfigureEndpoints(busRegistrationContext);
return testHarness;
}).LifestyleSingleton())
.AddMassTransit(x =>
{
x.AddConsumer<PingConsumer>();
x.AddBus(context => context.GetRequiredService<InMemoryTestHarness>().BusControl);
});
var harness = container.Resolve<InMemoryTestHarness>();
await harness.Start();
try
{
var bus = container.Resolve<IBus>();
await bus.Publish(new PingMessage());
Assert.That(await harness.Consumed.Any<PingMessage>());
}
finally
{
await harness.Stop();
container.Dispose();
}
}
This won't setup Consumer, Saga, or other harnesses, but it will configure endpoints on the test harness.

Xamarin TouchRunner Hangs calling IOS 3rd Party Binding

I have a 3rd party API IOS Binding which I am trying to test (more like an integration test) using TouchRunner.
An example API Method is this -
_client.AuthenticateWithUsername(username, token,
() => { // Success Callback },
() => { // NoConnection Callback },
(obj) => { // Other Error Callback });
The API when called goes off and does some work in the background then eventually makes one of the callbacks above, I would like to control the flow of the unit test using something like -
How can I unit test async methods on the UI Thread with Xamarin iOS TouchRunner
Unfortunately, when I insert the AutoResetEvent code, TouchRunner just hangs and never returns to the GUI.
I have also tried to use a TaskCompletionSource as follows -
public async Task<AuthResponse> AuthenticateUserAsync(string username, string password)
{
TaskCompletionSource<AuthResponse> tcs = new TaskCompletionSource<AuthResponse>();
AuthResponse response = new AuthResponse { Success = false };
LoginResponse loginResponse = await LoginUser(username, password);
_client.AuthenticateWithUsername(username, loginResponse.token,
() =>
{
response.Success = true;
Console.WriteLine("Auth");
tcs.SetResult(response);
},
() => { tcs.SetResult(response); },
obj => { tcs.SetResult(response); },
obj => { tcs.SetResult(response); });
return await tcs.Task;
}
[Test]
public async void AuthenticateUserAsyncTest()
{
var auth = await AuthenticateUserAsync(_username, _password);
Assert.IsTrue(auth.Success);
}
The debugger stepped through fine until the return await tcs.Task, but then results in a similar HUNG runner.
How can I work out why the hang is happening?
As this was not working, I then resorted to code like this -
_client.AuthenticateWithUsername(_username, loginResponse.token,
() =>
{
Assert.Pass("This crashes the runner");
Assert.True(true); // This DOES NOT!
},
() =>
{
// This will crash runner also
Assert.Fail("NoConnection");
},
(InvalidTokenError obj) =>
{
Assert.Fail("InvalidToken" + obj.Description);
},
(ClientError obj) =>
{
Assert.Fail("ClientError" + obj.Description);
});
As you can see, the flow ends up (understandably), run test, runs client call, end of test method completes which shows test as success, then the callback returns and the asserts get called, which crash the app, which we assume is because the runner has already completed the test, why one assert works and other crashes I do not know.
So,
Am I approaching this the right way?
Could something be happening in the 3rd Party API that will cause these approaches to hang? and how would I debug that?
Thanks #Nkosi, that is a good suggestion, I forgot to add that during my original testing that when I ran the code with async Task rather than void I got an immediate block from TouchRunner without even adding any other code other than the API call! This was a red flag I suppose, but using async void "seemed" to allow "standard" async testing, so I progressed and then ended up in the loop above.
As TouchRunner has not been updated in a very long time I have just spent time re-creating the test project using XUnit after various suggestions to try it in the forums and on stack.
https://github.com/xunit/devices.xunit - runners for Xamarin IOS + Android
https://xunit.github.io/docs/comparisons - to port NUnit syntax
Some other useful links are -
https://xunit.github.io/docs/getting-started-devices.html
https://gregshackles.com/testing-xamarin-apps-getting-started-with-xunit/
https://oren.codes/2014/07/10/getting-started-with-xunit-for-xamarin/
RESULT: I am very pleased to say all the above code now works for both the TaskCompletionSource and the AutoResetTask scenarios
I can now safely test my event based API :)
I just wanted to ensure other users are aware of this.
Thanks for your help.
One observation is that the test should be async Task and not async void ie
public async Task AuthenticateUserAsyncTest() {
//...code removed for brevity.
}
async void is a fire and forget so any exceptions thrown wont happen in the current context so they wont be caught.
Reference Async/Await - Best Practices in Asynchronous Programming

using signalR .net core client

I have set up a signalR website .net core. My function in my hub is:
public async Task Notify(int id) {
await Clients.All.InvokeAsync("Notified", id);
}
I have also tested this with the following js:
let connection = new signalR.HubConnection(myURL);
connection.on('Notified', data => {
console.log(4, data);
});
connection.start();
The js code seems to work fine and I see the log when I try connection.Invoke('Notify').
Now I have a console app that can needs to make the invoke. I am trying this in two ways and don't mind either solution:
1. A mvc controller within the signalR website that can take the id and invoke 'Notified'.
2. Use the client library Microsoft.AspNetCore.SignalR.Client in the console app.
The way 1 I have only done in classic asp.net like this:
GlobalHost.ConnectionManager.GetHubContext(hubName)
But couldn't find a way to do this in .net core.
Way 2 I have used the library and tried this so far:
var con = new HubConnectionBuilder();
con.WithUrl(myURL);
var connection = con.Build();
connection.InvokeAsync("Notify",args[0]).Wait();
This is the closest I have come to create a connection in the same way as the js code. However this code throws a null pointer when calling connection.InvokeAsync. The connection object is not null. It seems to be an internal object that is null. According to the stack trace the exception is thrown when a MoveNext() function is internally called.
Well looks like both are not currently possible. As of now I just used a forced way which is hopefully temporary.
I have created and used the following base class for hubs:
public abstract class MyHub : Hub
{
private static Dictionary<string, IHubClients> _clients = new Dictionary<string, IHubClients>();
public override Task OnConnectedAsync()
{
var c = base.OnConnectedAsync();
_clients.Remove(Name);
_clients.Add(Name, Clients);
return c;
}
public static IHubClients GetClients(string Name) {
return _clients.GetValueOrDefault(Name);
}
}
GlobalHost is gone. You need to inject IHubContext<THub> like in this sample.
This can be a bug in SignalR alpha1. Can you file an issue on https://github.com/aspnet/signalr and include a simplified repro?

Recommended way to test Scheduler/Throttle

I'm in the process of rewriting one little WPF-App I wrote to make use of ReactiveUI, to get a feeling about the library.
I really like it so far!
Now I've stumbled upon the Throttle method and want to use it when applying a filter to a collection.
This is my ViewModel:
namespace ReactiveUIThrottle
{
public class MainViewModel : ReactiveObject
{
private string _filter;
public string Filter { get => _filter; set => this.RaiseAndSetIfChanged(ref _filter, value); }
private readonly ReactiveList<Person> _persons = new ReactiveList<Person>();
private readonly ObservableAsPropertyHelper<IReactiveDerivedList<Person>> _filteredPersons;
public IReactiveDerivedList<Person> Persons => _filteredPersons.Value;
public MainViewModel()
{
Filter = string.Empty;
_persons.AddRange(new[]
{
new Person("Peter"),
new Person("Jane"),
new Person("Jon"),
new Person("Marc"),
new Person("Heinz")
});
var filterPersonsCommand = ReactiveCommand.CreateFromTask<string, IReactiveDerivedList<Person>>(FilterPersons);
this.WhenAnyValue(x => x.Filter)
// to see the problem
.Throttle(TimeSpan.FromMilliseconds(2000), RxApp.MainThreadScheduler)
.InvokeCommand(filterPersonsCommand);
_filteredPersons = filterPersonsCommand.ToProperty(this, vm => vm.Persons, _persons.CreateDerivedCollection(p => p));
}
private async Task<IReactiveDerivedList<Person>> FilterPersons(string filter)
{
await Task.Delay(500); // Lets say this takes some time
return _persons.CreateDerivedCollection(p => p, p => p.Name.Contains(filter));
}
}
}
The filtering itself works like a charm, also the throttling, when using the GUI.
However, I'd like to unittest the behavior of the filtering and this is my first attempt:
[Test]
public void FilterPersonsByName()
{
var sut = new MainViewModel();
sut.Persons.Should().HaveCount(5);
sut.Filter = "J";
sut.Persons.Should().HaveCount(2);
}
This test fails because the collection still has 5 people.
When I get rid of the await Task.Delay(500) in FilterPersons then the test will pass, but takes 2 seconds (from the throttle).
1) Is there a way to have the throttle be instant within the test to speed up the unittest?
2) How would I test the async behavior in my filter?
I'm using ReactiveUI 7.x
Short answers:
Yes, by making sure you're using CurrentThreadScheduler.Instance when running under test
Instead of using CurrentThreadScheduler, use a TestScheduler and manually advance it
The longer answer is that you need to ensure your unit tests can control the scheduler being used by your System Under Test (SUT). By default, you'll generally want to use CurrentThreadScheduler.Instance to make things happen "instantly" without any need to advance the scheduler manually. But when you want to write tests that do validate timing, you use a TestScheduler instead.
If, as you seem to be, you're using RxApp.*Scheduler, take a look at the With extension method, which can be used like this:
(new TestScheduler()).With(sched => {
// write test logic here, and RxApp.*Scheduler will resolve to the chosen TestScheduler
});
I tend to avoid using the RxApp ambient context altogether for the same reason I avoid all ambient contexts: they're shared state and can cause trouble as a consequence. Instead, I inject an IScheduler (or two) into my SUT as a dependency.

Resources