diff --git a/src/NServiceBus.Testing.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt b/src/NServiceBus.Testing.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt index f6862ab7..e1e272d2 100644 --- a/src/NServiceBus.Testing.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt +++ b/src/NServiceBus.Testing.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt @@ -58,6 +58,10 @@ namespace NServiceBus.Testing public void OnMessage(System.Action initializeMessage = null) { } public void OnMessage(TMessage message, string messageId) { } public void OnMessage(TMessage initializedMessage) { } + public System.Threading.Tasks.Task OnMessageAsync(string messageId, System.Action initializeMessage = null) { } + public System.Threading.Tasks.Task OnMessageAsync(System.Action initializeMessage = null) { } + public System.Threading.Tasks.Task OnMessageAsync(TMessage message, string messageId) { } + public System.Threading.Tasks.Task OnMessageAsync(TMessage initializedMessage) { } public NServiceBus.Testing.Handler SetIncomingHeader(string key, string value) { } public NServiceBus.Testing.Handler WithExternalDependencies(System.Action actionToSetUpExternalDependencies) { } } @@ -139,14 +143,24 @@ namespace NServiceBus.Testing public NServiceBus.Testing.Saga When(System.Func sagaIsInvoked) { } public NServiceBus.Testing.Saga When(System.Func> handlerSelector, TMessage message) { } public NServiceBus.Testing.Saga When(System.Func> handlerSelector, System.Action messageInitializer = null) { } + public System.Threading.Tasks.Task> WhenAsync(System.Func sagaIsInvoked) { } + public System.Threading.Tasks.Task> WhenAsync(System.Func> handlerSelector, TMessage message) { } + public System.Threading.Tasks.Task> WhenAsync(System.Func> handlerSelector, System.Action messageInitializer = null) { } public NServiceBus.Testing.Saga WhenHandling(System.Action initializeMessage = null) { } public NServiceBus.Testing.Saga WhenHandling(TMessage message) { } + public System.Threading.Tasks.Task> WhenHandlingAsync(System.Action initializeMessage = null) { } + public System.Threading.Tasks.Task> WhenHandlingAsync(TMessage message) { } public NServiceBus.Testing.Saga WhenHandlingTimeout(System.Action initializeMessage = null) { } public NServiceBus.Testing.Saga WhenHandlingTimeout(TMessage message) { } + public System.Threading.Tasks.Task> WhenHandlingTimeoutAsync(System.Action initializeMessage = null) { } + public System.Threading.Tasks.Task> WhenHandlingTimeoutAsync(TMessage message) { } public NServiceBus.Testing.Saga WhenReceivesMessageFrom(string client) { } public NServiceBus.Testing.Saga WhenSagaTimesOut(System.TimeSpan after) { } public NServiceBus.Testing.Saga WhenSagaTimesOut(System.DateTime at) { } public NServiceBus.Testing.Saga WhenSagaTimesOut() { } + public System.Threading.Tasks.Task> WhenSagaTimesOutAsync(System.TimeSpan after) { } + public System.Threading.Tasks.Task> WhenSagaTimesOutAsync(System.DateTime at) { } + public System.Threading.Tasks.Task> WhenSagaTimesOutAsync() { } public NServiceBus.Testing.Saga WithExternalDependencies(System.Action actionToSetUpExternalDependencies) { } } public class SentMessage : NServiceBus.Testing.OutgoingMessage diff --git a/src/NServiceBus.Testing/Handler.cs b/src/NServiceBus.Testing/Handler.cs index d4f36dcb..d6e7019d 100644 --- a/src/NServiceBus.Testing/Handler.cs +++ b/src/NServiceBus.Testing/Handler.cs @@ -1,6 +1,7 @@ namespace NServiceBus.Testing { using System; + using System.Threading.Tasks; using MessageInterfaces.MessageMapper.Reflection; /// @@ -266,9 +267,7 @@ public void OnMessage(string messageId, Action initializeMes /// public void OnMessage(Action initializeMessage = null) { - var message = messageCreator.CreateInstance(); - initializeMessage?.Invoke(message); - OnMessage(message); + OnMessageAsync(initializeMessage).GetAwaiter().GetResult(); } /// @@ -276,12 +275,53 @@ public void OnMessage(Action initializeMessage = null) /// setting the incoming headers and the message Id. /// public void OnMessage(TMessage message, string messageId) + { + OnMessageAsync(message, messageId).GetAwaiter().GetResult(); + } + + /// Activates the test that has been set up passing in a specific message to be used. + /// A message to be used with message handler. + /// + /// This is different from in a way that + /// it uses the message, and not calls to an action. + /// + /// ().OnMessage(message);]]> + public void OnMessage(TMessage initializedMessage) + { + OnMessageAsync(initializedMessage).GetAwaiter().GetResult(); + } + + /// + /// Activates the test that has been set up passing in the given message, + /// setting the incoming headers and the message Id. + /// + public Task OnMessageAsync(string messageId, Action initializeMessage = null) { testableMessageHandlerContext.MessageId = messageId; + return OnMessageAsync(initializeMessage); + } - OnMessage(message); + /// + /// Activates the test that has been set up passing in the given message. + /// + public Task OnMessageAsync(Action initializeMessage = null) + { + var message = messageCreator.CreateInstance(); + initializeMessage?.Invoke(message); + return OnMessageAsync(message); } + /// + /// Activates the test that has been set up passing in given message, + /// setting the incoming headers and the message Id. + /// + public Task OnMessageAsync(TMessage message, string messageId) + { + testableMessageHandlerContext.MessageId = messageId; + + return OnMessageAsync(message); + } + /// Activates the test that has been set up passing in a specific message to be used. /// A message to be used with message handler. /// @@ -289,12 +329,12 @@ public void OnMessage(TMessage message, string messageId) /// it uses the message, and not calls to an action. /// /// ().OnMessage(message);]]> - public void OnMessage(TMessage initializedMessage) + public async Task OnMessageAsync(TMessage initializedMessage) { var messageType = messageCreator.GetMappedTypeFor(initializedMessage.GetType()); var handleMethods = handler.GetType().CreateInvokers(messageType, typeof(IHandleMessages<>)); - handleMethods.InvokeSerially(handler, initializedMessage, testableMessageHandlerContext).GetAwaiter().GetResult(); + await handleMethods.InvokeSerially(handler, initializedMessage, testableMessageHandlerContext).ConfigureAwait(false); testableMessageHandlerContext.Validate(); } diff --git a/src/NServiceBus.Testing/Saga.cs b/src/NServiceBus.Testing/Saga.cs index 10eb007c..ffa02b16 100644 --- a/src/NServiceBus.Testing/Saga.cs +++ b/src/NServiceBus.Testing/Saga.cs @@ -254,8 +254,7 @@ public Saga ExpectNotPublish(Action check) /// public Saga WhenHandling(Action initializeMessage = null) { - var message = messageCreator.CreateInstance(initializeMessage); - return WhenHandling(message); + return WhenHandlingAsync(initializeMessage).GetAwaiter().GetResult(); } /// @@ -263,8 +262,7 @@ public Saga WhenHandling(Action initializeMessage = null) /// public Saga WhenHandling(TMessage message) { - var invokers = saga.GetType().CreateInvokers(typeof(TMessage), typeof(IHandleMessages<>)); - return When((s, c) => invokers.InvokeSerially(saga, message, c)); + return WhenHandlingAsync(message).GetAwaiter().GetResult(); } /// @@ -273,8 +271,7 @@ public Saga WhenHandling(TMessage message) /// public Saga WhenHandlingTimeout(Action initializeMessage = null) { - var message = messageCreator.CreateInstance(initializeMessage); - return WhenHandlingTimeout(message); + return WhenHandlingTimeoutAsync(initializeMessage).GetAwaiter().GetResult(); } /// @@ -282,8 +279,7 @@ public Saga WhenHandlingTimeout(Action initializeMessage /// public Saga WhenHandlingTimeout(TMessage message) { - var invokers = saga.GetType().CreateInvokers(typeof(TMessage), typeof(IHandleTimeouts<>)); - return When((s, c) => invokers.InvokeSerially(saga, message, c)); + return WhenHandlingTimeoutAsync(message).GetAwaiter().GetResult(); } /// @@ -293,7 +289,103 @@ public Saga WhenHandlingTimeout(TMessage message) /// public Saga When(Func sagaIsInvoked) { - sagaIsInvoked(saga, testContext).GetAwaiter().GetResult(); + return WhenAsync(sagaIsInvoked).GetAwaiter().GetResult(); + } + + /// + /// Uses the given delegate to select the message handler and invoking it with the given message. Checks all the expectations previously, and then clearing them for continued testing. + /// example: When(s => s.Handle, new MyMessage()) + /// + public Saga When(Func> handlerSelector, TMessage message) + { + return WhenAsync((s, context) => handlerSelector(s)(message, context)).GetAwaiter().GetResult(); + } + + /// + /// Uses the given delegate to select the message handler and invoking it with the specified message. Checks all the expectations previously, and then clearing them for continued testing. + /// example: When<MyMessage>(s => s.Handle, m => { m.Value = 42 }) + /// + public Saga When(Func> handlerSelector, Action messageInitializer = null) + { + return WhenAsync(handlerSelector, messageInitializer).GetAwaiter().GetResult(); + } + + /// + /// Expires requested timeouts for the saga by simulating that time has passed + /// and then clears out all previous expectations. + /// This will only invoke timeouts set with a argument. + /// + /// The amount of time that has passed to simulate. + public Saga WhenSagaTimesOut(TimeSpan after) + { + return WhenSagaTimesOutAsync(after).GetAwaiter().GetResult(); + } + + /// + /// Expires requested timeouts for the saga by simulating the passed in date and time + /// and then clears out all previous expectations. + /// This will only invoke timeouts set with a argument. + /// + /// The Date and time to simuluate. + public Saga WhenSagaTimesOut(DateTime at) + { + return WhenSagaTimesOutAsync(at).GetAwaiter().GetResult(); + } + + /// + /// Expires all requested timeouts for the saga and then clears out all previous expectations. + /// + public Saga WhenSagaTimesOut() + { + return WhenSagaTimesOutAsync().GetAwaiter().GetResult(); + } + + /// + /// Initializes the given message type and checks all the expectations previously set up, + /// and then clears them for continued testing. + /// + public Task> WhenHandlingAsync(Action initializeMessage = null) + { + var message = messageCreator.CreateInstance(initializeMessage); + return WhenHandlingAsync(message); + } + + /// + /// Checks all the expectations previously set up, and then clears them for continued testing. + /// + public Task> WhenHandlingAsync(TMessage message) + { + var invokers = saga.GetType().CreateInvokers(typeof(TMessage), typeof(IHandleMessages<>)); + return WhenAsync((s, c) => invokers.InvokeSerially(saga, message, c)); + } + + /// + /// Initializes the given timeout message type and checks all the expectations previously set up, + /// and then clears them for continued testing. + /// + public Task> WhenHandlingTimeoutAsync(Action initializeMessage = null) + { + var message = messageCreator.CreateInstance(initializeMessage); + return WhenHandlingTimeoutAsync(message); + } + + /// + /// Checks all the expectations previously set up, and then clears them for continued testing. + /// + public Task> WhenHandlingTimeoutAsync(TMessage message) + { + var invokers = saga.GetType().CreateInvokers(typeof(TMessage), typeof(IHandleTimeouts<>)); + return WhenAsync((s, c) => invokers.InvokeSerially(saga, message, c)); + } + + /// + /// Uses the given delegate to invoke the saga, checking all the expectations previously set up, + /// and then clearing them for continued testing. + /// example: When((saga, context) => s.Handle(new MyMessage(), context)) + /// + public async Task> WhenAsync(Func sagaIsInvoked) + { + await sagaIsInvoked(saga, testContext).ConfigureAwait(false); testContext.Validate(); testContext = new TestingContext(messageCreator, testContext.previousTimeouts.Concat(testContext.TimeoutMessages).ToArray()); @@ -305,20 +397,20 @@ public Saga When(Func sagaIsInvoked) /// Uses the given delegate to select the message handler and invoking it with the given message. Checks all the expectations previously, and then clearing them for continued testing. /// example: When(s => s.Handle, new MyMessage()) /// - public Saga When(Func> handlerSelector, TMessage message) + public Task> WhenAsync(Func> handlerSelector, TMessage message) { - return When((s, context) => handlerSelector(s)(message, context)); + return WhenAsync((s, context) => handlerSelector(s)(message, context)); } /// /// Uses the given delegate to select the message handler and invoking it with the specified message. Checks all the expectations previously, and then clearing them for continued testing. /// example: When<MyMessage>(s => s.Handle, m => { m.Value = 42 }) /// - public Saga When(Func> handlerSelector, Action messageInitializer = null) + public Task> WhenAsync(Func> handlerSelector, Action messageInitializer = null) { var message = (TMessage)messageCreator.CreateInstance(typeof(TMessage)); messageInitializer?.Invoke(message); - return When((s, context) => handlerSelector(s)(message, context)); + return WhenAsync((s, context) => handlerSelector(s)(message, context)); } /// @@ -327,11 +419,11 @@ public Saga When(Func argument. /// /// The amount of time that has passed to simulate. - public Saga WhenSagaTimesOut(TimeSpan after) + public async Task> WhenSagaTimesOutAsync(TimeSpan after) { - InvokeTimeouts(testContext.previousTimeouts + await InvokeTimeoutsAsync(testContext.previousTimeouts .Where(t => t.Within.HasValue) - .Where(t => t.Within <= after)); + .Where(t => t.Within <= after)).ConfigureAwait(false); return this; } @@ -342,11 +434,11 @@ public Saga WhenSagaTimesOut(TimeSpan after) /// This will only invoke timeouts set with a argument. /// /// The Date and time to simuluate. - public Saga WhenSagaTimesOut(DateTime at) + public async Task> WhenSagaTimesOutAsync(DateTime at) { - InvokeTimeouts(testContext.previousTimeouts + await InvokeTimeoutsAsync(testContext.previousTimeouts .Where(t => t.At.HasValue) - .Where(t => t.At <= at)); + .Where(t => t.At <= at)).ConfigureAwait(false); return this; } @@ -354,9 +446,9 @@ public Saga WhenSagaTimesOut(DateTime at) /// /// Expires all requested timeouts for the saga and then clears out all previous expectations. /// - public Saga WhenSagaTimesOut() + public async Task> WhenSagaTimesOutAsync() { - InvokeTimeouts(testContext.previousTimeouts); + await InvokeTimeoutsAsync(testContext.previousTimeouts).ConfigureAwait(false); return this; } @@ -515,17 +607,16 @@ public Saga ExpectSagaData(Func check) where TSag return this; } - void InvokeTimeouts(IEnumerable> messages) + async Task InvokeTimeoutsAsync(IEnumerable> messages) { - messages - .OrderBy(t => t.Within) - .ToList() - .ForEach(t => - { - var messageType = messageCreator.GetMappedTypeFor(t.Message.GetType()); - var invokers = saga.GetType().CreateInvokers(messageType, typeof(IHandleTimeouts<>)); - invokers.InvokeSerially(saga, t.Message, testContext).GetAwaiter().GetResult(); - }); + var timeoutHandlers = messages.OrderBy(t => t.Within).ToList(); + + foreach (var timeoutHandler in timeoutHandlers) + { + var messageType = messageCreator.GetMappedTypeFor(timeoutHandler.Message.GetType()); + var invokers = saga.GetType().CreateInvokers(messageType, typeof(IHandleTimeouts<>)); + await invokers.InvokeSerially(saga, timeoutHandler.Message, testContext).ConfigureAwait(false); + } testContext.Validate(); testContext = new TestingContext(messageCreator); diff --git a/src/NServiceBus.Testing/TypeExtensions.cs b/src/NServiceBus.Testing/TypeExtensions.cs index 1d5f0c99..65b2a3be 100644 --- a/src/NServiceBus.Testing/TypeExtensions.cs +++ b/src/NServiceBus.Testing/TypeExtensions.cs @@ -1,21 +1,21 @@ namespace NServiceBus.Testing - { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using System.Threading.Tasks; - - static class TypeExtensions - { - public static IEnumerable> CreateInvokers(this Type targetType, Type messageType, Type interfaceGenericType) - { - var interfaceTypes = targetType.GetInterfaces() - .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceGenericType) - .Where(i => i.GenericTypeArguments.First().IsAssignableFrom(messageType)); - - foreach (var interfaceType in interfaceTypes) - { +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using System.Threading.Tasks; + + static class TypeExtensions + { + public static IEnumerable> CreateInvokers(this Type targetType, Type messageType, Type interfaceGenericType) + { + var interfaceTypes = targetType.GetInterfaces() + .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceGenericType) + .Where(i => i.GenericTypeArguments.First().IsAssignableFrom(messageType)); + + foreach (var interfaceType in interfaceTypes) + { var methodInfo = targetType.GetInterfaceMap(interfaceType).TargetMethods.FirstOrDefault(); if (methodInfo == null) { @@ -35,14 +35,14 @@ public static IEnumerable> Cr yield return Expression.Lambda>(body, target, messageParam, contextParam).Compile(); } - } + } - public static async Task InvokeSerially(this IEnumerable> invokers, object instance, object message, IMessageHandlerContext context) - { + public static async Task InvokeSerially(this IEnumerable> invokers, object instance, object message, IMessageHandlerContext context) + { foreach (var invocation in invokers) { await invocation(instance, message, context).ConfigureAwait(false); } } - } - } + } +} \ No newline at end of file