I am having 2 issues testing MassTransit consumers:
Sync issue
MessageData
The first one is like this:
var testConsumer = TestFactory.ForConsumer<ImageUploadConsumer>().New(
test =>
{
test.UseConsumerFactory(new InstanceConsumerFactory<ImageUploadConsumer>(ImageConsumer));
test.Publish(message, (scenario, context) =>
{
});
});
testConsumer.Execute(); //Is non blocking
The following line (below) fails, because this line:
moqFileMetaRepo.Verify(_ => _.Add(It.IsAny<IFileMeta>()),Times.Once );
is executed 9.9/10 before... this line ever did:
public async Task Consume(ConsumeContext<ImageUploadWithThumb> context)
My fix has been to do
moqFileMetaRepo
.Setup(repo => repo.Add(It.IsAny<IFileMeta>()))
.Callback(() => { AutoEvent.Set(); });
And call the following before the assert:
AutoEvent.WaitOne(TimeSpan.FromSeconds(10));
Which is really a lot of work. And makes TDD or Testing in general a hassle, which I fear is only going to get ignored over time.
MessageData issue is another one. Here's the payload I'm sending through
message = new ImageUploadWithThumb()
{
Id = Guid.NewGuid(),
FileName = "Test.jpg",
User = "Me",
Extension = "jpg",
OriginalImage = new ConstantMessageData<byte[]>(new Uri("https://g00gle.com"), new byte[] { 1, 2, 3 })
};
I'm expecting to get byte[] { 1, 2, 3 } on the other end without having to resort to creating an actual persistence.
Instead:
On the sender side the MessageData.Value resolves ok. The consumer totally bombs. Works in prod though =_= which is not where testing should be.
I really just want to mock and UnitTest my consumer w/o having to wrestle with the framework - preferably in under 5 mins or so. Is there a way out while sticking to MT3?
I would suggest looking at the MassTransit.TestFramework package. It does require NUnit, but you could always take the classes and port it to your own test framework.
All of the MassTransit unit tests are written using the fixtures in this framework. The original .Testing namespace is in a world of hurt right not, it didn't survive completely and I'm unsure it's actually working completely. It wasn't designed for async, so it was difficult to transition without trashing it entirely.
Related
I'm reading a book on Unit Testing, below is the quote and the code (not full code since it is easy to understand what the code does)
First, the author shows a mocking that doesn't lead to fragile tests:
[Fact]
public void Successful_purchase() {
var mock = new Mock<IEmailGateway>();
var sut = new CustomerController(mock.Object);
bool isSuccess = sut.Purchase(customerId: 1, productId: 2, quantity: 5);
Assert.True(isSuccess);
mock.Verify(x => x.SendReceipt("customer#email.com", "Shampoo", 5), Times.Once);
}
the author says:
The call to the SMTP service is a legitimate reason to do mocking. It doesn’t lead
to test fragility because you want to make sure this type of communication stays in
place even after refactoring. The use of mocks helps you do exactly that.
Then the author shows another example that leads to fragile tests:
[Fact]
public void Purchase_succeeds_when_enough_inventory() {
var storeMock = new Mock<IStore>();
storeMock.Setup(x => x.HasEnoughInventory(Product.Shampoo, 5)).Returns(true);
var customer = new Customer();
bool success = customer.Purchase(storeMock.Object, Product.Shampoo, 5);
Assert.True(success);
storeMock.Verify(x => x.RemoveInventory(Product.Shampoo, 5), Times.Once);
}
and the author says:
Unlike the communication between CustomerController and the SMTP service, the RemoveInventory() method call from Customer to Store doesn't cross the application boundary: both the caller and the recipient reside inside the application. Also, this method is neither an operation nor a state that helps the client achieve its goals.
I'm a little bit confused here, so we shouldn't test whether RemoveInventory is called? what happen the developer doesn't write the Purchase method correctly, which causes RemoveInventory method not being called on the IStore object, so the purchase will still success but the internal state is corrupted?
The idea is that, instead of checking the call to RemoveInventory, you need to check the final state of the store. Mocks are not appropriated here, unlike in the example with the SMTP Gateway.
The difference is that the communication with the store doesn't cross the application boundary, while the communication with the SMTP does.
Here is an article that describes this topic in more detail: When to mock
So I've been trying to write a test for mass transit using the in-memory feature. I wondered what peoples approach was to waiting for consumers to execute. In the example below a use a sleep or I've also tried a while loop, but not a fan of either, any better ideas? I basically want to check that the consumer is executed.
[Fact]
public async Task SomeTest()
{
var busControl = Bus.Factory.CreateUsingInMemory(cfg =>
{
cfg.ReceiveEndpoint("commands", ec =>
{
ec.LoadFrom(context);
});
});
var address = new Uri(bus.Address, "commands")
await sendEndpoint.Send(MyExampleCommand());
Thread.Sleep(2000);
//Check nsubstitute mock received
}
Look at the test harness features that are built into MassTransit. They should give you some good ideas of how to test them.
You can look at the harness tests to see how they should be used. Note that they work with any test framework, not just NUnit.
https://github.com/MassTransit/MassTransit/blob/develop/tests/MassTransit.Tests/Testing/ConsumerTest_Specs.cs
The Testing documentation explains how to use the test harnesses.
I have made one WCF service Method which is consuming third party service methods(call methodA, methodB, methodC) and here all three belongs to different services i.e. serviceA, serviceB, serviceC.
Each method accepting single input object for processing (not List of input object). but I have to work on multiple objects, so I am consuming this methods in for loop.
Now the problem is, suppose I have 3 objects to process with methodA, 2 objects to process with methodB and 5 objects to process with methodC and consider each method taking 1 sec to process then total time taken to process all is almost 10 seconds. To overcome this problem after googling I got options like threading and parallel-linq. of course I don't have enough knowledge about threading and its performance, I choose to stay away. Now with parallel linq I found performance is up. But still expectations are not satisfied (and sometime its throwing timeout exception).
So please advice what should i try now? whether to dive in threading or anything other to try?
As TPL in general or Parallel class are solutions too, I suggest you to try out the TPL Dataflow library as you have a data flowing across your application, and your code will be much more structured this way.
So you can create 3 ActionBlock<> objects, each for the services you have, and post data to them in your loop. Also you can add task continuation handler for them so you'll be notified then all of messages are consumed by the services. Also you can add a BufferBlock<T> and link it to other ones with filter function. The code will be something like this:
void ProducingMethod()
{
var serviceABlock = new ActionBlock<YourInputObject>(o =>
{
serviceA.Call(o);
});
serviceABlock.Completion.ContinueWith(t =>
{
sendNotifyA();
});
var serviceBBlock = new ActionBlock<YourInputObject>(o =>
{
serviceB.Call(o);
});
serviceBBlock.Completion.ContinueWith(t =>
{
sendNotifyB();
});
var serviceCBlock = new ActionBlock<YourInputObject>(o =>
{
serviceC.Call(o);
});
serviceCBlock.Completion.ContinueWith(t =>
{
sendNotifyC();
});
foreach (var objectToProcess in queue)
{
if (SendToA)
{
serviceABlock.SendAsync(objectToProcess);
}
else if (SendToB)
{
serviceBBlock.SendAsync(objectToProcess);
}
else if (SendToC)
{
serviceCBlock.SendAsync(objectToProcess);
}
}
}
I have a repository and a consumer of that repository in some legacy code.
The consumer makes multiple method calls to the repository.
Each method call returns a huge resultset.
I do have an integration test that checks how the consumer and repository behave together, but it isn't good enough for me - it relies on a database connection, is painfully slow, and doesn't help me know whether the test fails because of changes to the repository, database, or consumer.
I'd like to convert this integration test to test the consumer in isolation - independent of the implementation of the repository.
But because it is legacy code the behaviour of which I do not fully understand yet and because the resultsets are huge, I can't stub out the repository by hand. If it were possible to write this by hand it would look like
var mockRepository = new Mock<IRepository>();
mockRepository.SetUp(r => r.GetCustomersInMarket(marketId: 3))
.Returns(
new List<Customer> {
new Customer {
...
},
new Customer {
...
},
... x 100 // large dataset
});
mockRepository.SetUp(r => r.GetProductsInMarket(marketId: 3))
.Returns(
...
);
... x 15 // many different calls need to be set up
var testee = new Consumer(mockRepository.Object); // using dependency injection
var report = testee.GenerateReport();
AssertReportMatchesExpectations(report); // some assertions
So what I'd prefer to do is (once)
var recordingProxy = new RecordingProxy<IRepository>(new Repository());
var testee = new Consumer(recordingProxy);
var report = testee.GenerateReport();
var methodCallLog = recordingProxy.GetLog();
and thereafter
var methodCallLog = GetStoredMethodCallLog(); // load persisted log from file or similar
var replayingProxy = new ReplayingProxy<IRepository>(methodCallLog);
var testee = new Consumer(replayingProxy);
var report = testee.GenerateReport();
AssertReportMatchesExpectations(report); // some assertions
I have started working on a generic tool to act as a proxy and record and replay traffic that goes across interface boundaries to solve this problem in general.
Is there already anything like this out there?
Are there other ways to solve the problem of stubbing repositories
to return large datasets
when the content of the dataset can only be understood by observing existing behaviour (because the author's intent is not clear).
If not, I'll be posting my tool as an answer to this question.
I've been using Rx and more specifically ReactiveUI for a while in a project and have got myself into a situation where I think I need some advice.
The problem is that, given a command is executed (a button is clicked) I want to show a message box, one which the user will answer Yes or No. Depending on the answer, I then want to do some more stuff. As I'm using MVVM with unit tests I'd like the MessageBox to be testable; i.e. to be replaced by some other code. This is essentially what I've got.
In my view model:
this.ExternalObservable = this.SomeOperationCommand
.SelectMany(_ => this.UserWantsToContinueWithOperation())
.Where(x => x)
.Select(_ => this.SomeData)
.Where(x => x != null);
private IObservable<bool> UserWantsToContinueWithOperation() {
var subject = new Subject<bool>();
var box = new GuiMsgBox("Continue?",
result => {
subject.OnNext(result == System.Windows.MessageBoxResult.Yes);
});
MessageBus.Current.SendMessage(box);
return subject;
}
And the GuiMsgBox is essentially a wrapper around the System.Windows.MessageBox class which I listen to using the MessageBus in the UI and in my tests.
This all works fine when running the application, but in unit tests, as the bus is then using the Immediate scheduler, it's obviously not working the same way.
I feel there's some design glitch here, so any input on the actual problem; to show a message box, returning a result, which can be tested, would be greatly appreciated!
It's hard to tell without seeing more implementation details, but I would consider using TestScheduler instead. In RxUI.Testing, this is as easy as:
(new TestScheduler()).With(sched => {
// Write your test here, all the schedulers will be
// implicitly set to your 'sched' scheduler.
});
Here's an example of a bunch of MVVM-related tests, testing a Pomodoro timer:
https://github.com/xpaulbettsx/ReactiveUI/blob/master/ReactiveUI.Sample/ReactiveUI.Sample.Tests/ViewModels/BlockTimerViewModelTest.cs
Here's another good example of MVVM-based testing from my Rx book (sorry about the plug), specifically using CreateColdObservable in order to mock input (i.e. testing the scenario of "Click a button, wait 10 seconds, check result")
https://github.com/ProgRx/Chapter-9