Search the web
Sign In
New User? Sign Up
nservicebus
? Already a member? Sign in to Yahoo!

Yahoo! Groups Tips

Did you know...
Want to share photos of your group with the world? Add a group photo to Flickr.

Best of Y! Groups

   Check them out and nominate your group.
Having problems with message search? Fill out this form to ensure your group is one of the first to be migrated to the new message search system.

Messages

  Messages Help
Advanced
Unit testing sagas   Message List  
Reply | Forward Message #208 of 4996 |

I’ve been speaking about how sagas are designed to be testable for a while now,

and several projects that use nServiceBus have been doing it,

but I’ve never actually sat down and did one myself – until this past week.

 

It was a pain in the ass.

 

And as developers go, I’m fairly proficient with the tools out there.

It’s as if the mocking tools weren’t designed with this sort of thing in mind.

 

So, this past weekend, I decided to do something about it.

And now you can find NServiceBus.Testing (implemented under /src/core).

When built, the libraries are in /build/testing.

 

You’ll notice that Rhino.Mocks is there too.

You hardly need to know rhino mocks to test sagas – it isn’t exposed in the API,

although the API is similar in style to the rhino callback and Expect.Call semantics.

When your tests fail, though, you will see rhino mocks error messages –

which aren’t  designed for these scenarios, but are pretty good once you get used to them.

 

Here’s an example business process (well, part of one), which can be found under /Samples/Saga:

 

1.       When we receive a CreateOrderMessage, whose “Completed” flag is true, we’ll send 2 AuthorizationRequestMessages to internal systems, one OrderStatusUpdatedMessage to the caller with a status “Received”, and a TimeoutMessage to the TimeoutManager requesting to be notified – so that the process doesn’t get stuck if one or both messages don’t get a response.

2.       When we receive an AuthorizationResponseMessage, we notify the initiator of the Order by sending them a OrderStatusUpdatedMessage with a status “Authorized1”.

3.       When we get “timed out” from the TimeoutManager, we check if at least one AuthorizationResponseMessage had arrived, and if so, publish an OrderAcceptedMessage, and notify the initator (again via the OrderStatusUpdatedMessage) this time with a status of “Accepted”.

 

This is tested as follows (suggestions welcome), the class “Saga” is that which ties together the expectations into the test scenario.

The delegates you see in the “ExpectXXX” calls are actually predicates, and are there to report on if the appropriate message (with the right data) was sent.

 

    public class OrderSagaTests

    {

        private OrderSaga orderSaga = null;

        private string timeoutAddress;

        private Saga Saga;

       

        [SetUp]

        public void Setup()

        {

            timeoutAddress = "timeout";

            Saga = Saga.Test(out orderSaga, timeoutAddress);

        }

 

        [Test]

        public void OrderProcessingShouldCompleteAfterOneAuthorizationAndOneTimeout()

        {

            Guid externalOrderId = Guid.NewGuid();

            Guid customerId = Guid.NewGuid();

            string clientAddress = "client";

 

            CreateOrderMessage createOrderMsg = new CreateOrderMessage();

            createOrderMsg.OrderId = externalOrderId;

            createOrderMsg.CustomerId = customerId;

            createOrderMsg.Products = new List<Guid>(new Guid[] { Guid.NewGuid() });

            createOrderMsg.Amounts = new List<float>(new float[] { 10.0F });

            createOrderMsg.Completed = true;

 

            TimeoutMessage timeoutMessage = null;

 

            Saga.WhenReceivesMessageFrom(clientAddress)

                .ExpectSend<AuthorizeOrderRequestMessage>(

                    delegate(AuthorizeOrderRequestMessage m)

                    {

                        return m.SagaId == orderSaga.Id;

                    })

                .ExpectSend<AuthorizeOrderRequestMessage>(

                    delegate(AuthorizeOrderRequestMessage m)

                    {

                        return m.SagaId == orderSaga.Id;

                    })

                .ExpectSendToDestination<OrderStatusUpdatedMessage>(

                    delegate(string destination, OrderStatusUpdatedMessage m)

                    {

                        return m.OrderId == externalOrderId && destination == clientAddress;

                    })

                .ExpectSendToDestination<TimeoutMessage>(

                    delegate(string destination, TimeoutMessage m)

                    {

                        timeoutMessage = m;

                        return m.SagaId == orderSaga.Id && destination == timeoutAddress;

                    })

                .When(delegate { orderSaga.Handle(createOrderMsg); });

 

 

            Assert.IsFalse(orderSaga.Completed);

 

 

            AuthorizeOrderResponseMessage response = new AuthorizeOrderResponseMessage();

            response.ManagerId = Guid.NewGuid();

            response.Authorized = true;

            response.SagaId = orderSaga.Id;

 

            Saga.ExpectSendToDestination<OrderStatusUpdatedMessage>(

                    delegate(string destination, OrderStatusUpdatedMessage m)

                    {

                        return (destination == clientAddress &&

                                m.OrderId == externalOrderId &&

                                m.Status == OrderStatus.Authorized1);

                    })

                .When(delegate { orderSaga.Handle(response); });

 

            Assert.IsFalse(orderSaga.Completed);

 

 

            Saga.ExpectSendToDestination<OrderStatusUpdatedMessage>(

                    delegate(string destination, OrderStatusUpdatedMessage m)

                    {

                        return (destination == clientAddress &&

                                m.OrderId == externalOrderId &&

                                m.Status == OrderStatus.Accepted);

                    })

                .ExpectPublish<OrderAcceptedMessage>(

                    delegate(OrderAcceptedMessage m)

                    {

                        return (m.CustomerId == customerId);

                    })

                .When(delegate { orderSaga.Timeout(timeoutMessage.State); });

 

            Assert.IsTrue(orderSaga.Completed);

        }

    }

 

Thoughts? Questions? Comments?

 

--
Udi Dahan - The Software Simplist
.NET Development Expert & SOA Specialist

 


No virus found in this outgoing message.
Checked by AVG Free Edition.
Version: 7.5.516 / Virus Database: 269.19.18/1255 - Release Date: 01/02/2008 09:59



Sun Feb 3, 2008 8:24 am

udidahan7
Offline Offline
Send Email Send Email

Forward
Message #208 of 4996 |
Expand Messages Author Sort by Date

I’ve been speaking about how sagas are designed to be testable for a while now, and several projects that use nServiceBus have been doing it, but I’ve...
Udi Dahan
udidahan7
Offline Send Email
Feb 3, 2008
8:24 am
Advanced

Copyright © 2009 Yahoo! Inc. All rights reserved.
Privacy Policy - Terms of Service - Guidelines - Help