Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

How to create integration tests

Manuel Riezebosch edited this pageMar 18, 2022 ·9 revisions

As you can probably imagine, writing integration tests is slightly more involved than writing ordinary unit tests. By nature, they contain more moving parts, so we need to handle some more problems, e.g. regarding timing and stuff.

Rebus is pretty nice though, because it has in-mem implementations of its transport and storage for subscriptions, sagas, and timeouts.

So, basically you can start a full bus running on in-mem persistence all the way through like this:

[Test]publicvoidMyTest(){usingvaractivator=newBuiltinHandlerActivator();usingvarbus=Configure.With(activator).Transport(t=>t.UseInMemoryTransport(newInMemNetwork(),"queue-name")).Subscriptions(s=>s.StoreInMemory()).Sagas(s=>s.StoreInMemory()).Timeouts(t=>t.StoreInMemory()).Start();// exercise bus here}

If you let two bus instances share theInMemNetwork passed toUseInMemoryTransport, they can communicate, so that's how you can run realistic integration tests on your build server without any message broker available, and without the concurrency and state management problems that that would incur.

Similarly, if you pass anInMemorySubscriberStore instance toStoreInMemory under theSubscriptions configurer, you can integration test a simpleSystem.String pub/sub scenario like this:

[Test]publicasyncTaskSubscriberGetsPublishedStrings(){varnetwork=newInMemNetwork();varsubscriberStore=newInMemorySubscriberStore();usingvarpublisherActivator=newBuiltinHandlerActivator();usingvarsubscriberActivator=newBuiltinHandlerActivator();usingvareventWasReceived=newManualResetEvent(initialState:false);usingvarpublisher=Configure.With(publisherActivator)// rebus v7+: Configure.OneWayClient().Transport(t=>t.UseInMemoryTransport(network,"publisher")).Subscriptions(s=>s.StoreInMemory(subscriberStore)).Start();subscriberActivator.Handle<string>(async message=>eventWasReceived.Set());varsubscriber=Configure.With(subscriberActivator).Transport(t=>t.UseInMemoryTransport(network,"subscriber")).Subscriptions(s=>s.StoreInMemory(subscriberStore)).Start();awaitsubscriber.Subscribe<string>();awaitpublisher.Publish("HEJ MED DIG MIN VEN");Assert.That(eventWasReceived.WaitOne(TimeSpan.FromSeconds(5)),Is.True,"Did not receive the published event within 5 s timeout");}

in this case using aManualResetEvent to block and wait for the subscriber to receive the published string, failing with anAssertionException if it's not received within a 5 s timeout.

Another great option is to use theHypothesist asynchronous assertion framework with a providedadapter for Rebus:

[Test]publicasyncTaskSubscriberGetsPublishedStrings(){// Arrangevarhypothesis=Hypothesis.For<string>().Any(x=>x=="HEJ MED DIG MIN VEN");// hypothesist for async assertionsusingvaractivator=newBuiltinHandlerActivator().Register(hypothesis.AsHandler);// adapater to register the hypothesis as handler// remaining setup equals previous example, except for the manual reset event.awaithypothesis.Validate(TimeSpan.FromSeconds(5));}

While this might seem fairly straightforward, here's a little word of warning: Writing integration tests like this can quickly become complicated, so my advice is to exercise the same amount of care and discipline as you would with your production code. But that actually holds for all of your testing code – the same patterns and anti-patterns apply there too, because why wouldn't they? 😉

One of the important distinctions to make early on in my experience, is between code that would want to share between your system and your tests, and code you want to be able to swap out with test code, like the example above.

I often end up with using configuration code that looks like this:

Configure.With(activator).Transport(t=>{if(Backdoor.Network!=null){t.UseInMemoryTransport(Backdoor.Network,"queue-name");}else{t.UseMsmq("queue-name");}}).Subscriptions(s=>{if(Backdoor.SubscriberStore!=null){s.StoreInMemory(Backdoor.SubscriberStore);}else{s.StoreInSqlServer(connectionString,"Subscriptions",isCentralized:true);}}).Start();

and then I have aBackdoor lying around in the project that looks somewhat like this:

internalstaticclassBackdoor{internalstaticInMemNetworkNetwork;internalstaticInMemorySubscriberStoreSubscriberStore;publicstaticvoidReset(){Network=null;SubscriberStore=null;}publicstaticvoidEnableTestMode(){Network=newInMemNetwork();SubscriberStore=newInMemorySubscriberStore();}}

Combined with[InternalsVisibleTo("MyTestProject")] I can then

Backdoor.EnableTestMode();

and

Backdoor.Reset();

before and after each test in myBusIntegrationTestFixtureBase.

This way, the configuration code I use in my integration tests is the same as the configuration code I use in my production system, save for the few lines that configure which queue and subscription storage I'm using.

We do our best to ensure that the documentation wiki is up-to-date - please let us know if you think there's something we should do better.

Basic stuff

Configuration

Scenarios

Areas

Transports (not a full list)

Customization

Pipelines

Prominent application services

Clone this wiki locally


[8]ページ先頭

©2009-2025 Movatter.jp