diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
index f2bf459dc5036786550377139715f730e8c60521..55d0c98d442c209011cc57afae290c8e937070dc 100644
--- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
+++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
@@ -77,6 +77,7 @@
     <Compile Include="TimeoutsTest.cs" />
     <Compile Include="NUnitVersionTest.cs" />
     <Compile Include="ChannelTest.cs" />
+    <Compile Include="MockServiceHelper.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>
diff --git a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
new file mode 100644
index 0000000000000000000000000000000000000000..25afa30bba7b8bba0712576d059ab0802c94f6a0
--- /dev/null
+++ b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
@@ -0,0 +1,244 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+using NUnit.Framework;
+
+namespace Grpc.Core.Tests
+{
+    /// <summary>
+    /// Allows setting up a mock service in the client-server tests easily.
+    /// </summary>
+    public class MockServiceHelper
+    {
+        public const string ServiceName = "tests.Test";
+
+        public static readonly Method<string, string> UnaryMethod = new Method<string, string>(
+            MethodType.Unary,
+            ServiceName,
+            "Unary",
+            Marshallers.StringMarshaller,
+            Marshallers.StringMarshaller);
+
+        public static readonly Method<string, string> ClientStreamingMethod = new Method<string, string>(
+            MethodType.ClientStreaming,
+            ServiceName,
+            "ClientStreaming",
+            Marshallers.StringMarshaller,
+            Marshallers.StringMarshaller);
+
+        public static readonly Method<string, string> ServerStreamingMethod = new Method<string, string>(
+            MethodType.ServerStreaming,
+            ServiceName,
+            "ServerStreaming",
+            Marshallers.StringMarshaller,
+            Marshallers.StringMarshaller);
+
+        public static readonly Method<string, string> DuplexStreamingMethod = new Method<string, string>(
+            MethodType.DuplexStreaming,
+            ServiceName,
+            "DuplexStreaming",
+            Marshallers.StringMarshaller,
+            Marshallers.StringMarshaller);
+
+        readonly string host;
+        readonly ServerServiceDefinition serviceDefinition;
+
+        UnaryServerMethod<string, string> unaryHandler;
+        ClientStreamingServerMethod<string, string> clientStreamingHandler;
+        ServerStreamingServerMethod<string, string> serverStreamingHandler;
+        DuplexStreamingServerMethod<string, string> duplexStreamingHandler;
+
+        Server server;
+        Channel channel;
+
+        public MockServiceHelper(string host = null)
+        {
+            this.host = host ?? "localhost";
+
+            serviceDefinition = ServerServiceDefinition.CreateBuilder(ServiceName)
+                .AddMethod(UnaryMethod, (request, context) => unaryHandler(request, context))
+                .AddMethod(ClientStreamingMethod, (requestStream, context) => clientStreamingHandler(requestStream, context))
+                .AddMethod(ServerStreamingMethod, (request, responseStream, context) => serverStreamingHandler(request, responseStream, context))
+                .AddMethod(DuplexStreamingMethod, (requestStream, responseStream, context) => duplexStreamingHandler(requestStream, responseStream, context))
+                .Build();
+
+            var defaultStatus = new Status(StatusCode.Unknown, "Default mock implementation. Please provide your own.");
+
+            unaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
+            {
+                context.Status = defaultStatus;
+                return "";
+            });
+
+            clientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
+            {
+                context.Status = defaultStatus;
+                return "";
+            });
+
+            serverStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) =>
+            {
+                context.Status = defaultStatus;
+            });
+
+            duplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) =>
+            {
+                context.Status = defaultStatus;
+            });
+        }
+
+        /// <summary>
+        /// Returns the default server for this service and creates one if not yet created.
+        /// </summary>
+        public Server GetServer()
+        {
+            if (server == null)
+            {
+                server = new Server
+                {
+                    Services = { serviceDefinition },
+                    Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
+                };
+            }
+            return server;
+        }
+
+        /// <summary>
+        /// Returns the default channel for this service and creates one if not yet created.
+        /// </summary>
+        public Channel GetChannel()
+        {
+            if (channel == null)
+            {
+                channel = new Channel(Host, GetServer().Ports.Single().BoundPort, Credentials.Insecure);
+            }
+            return channel;
+        }
+
+        public CallInvocationDetails<string, string> CreateUnaryCall(CallOptions options = null)
+        {
+            options = options ?? new CallOptions();
+            return new CallInvocationDetails<string, string>(channel, UnaryMethod, options);
+        }
+
+        public CallInvocationDetails<string, string> CreateClientStreamingCall(CallOptions options = null)
+        {
+            options = options ?? new CallOptions();
+            return new CallInvocationDetails<string, string>(channel, ClientStreamingMethod, options);
+        }
+
+        public CallInvocationDetails<string, string> CreateServerStreamingCall(CallOptions options = null)
+        {
+            options = options ?? new CallOptions();
+            return new CallInvocationDetails<string, string>(channel, ServerStreamingMethod, options);
+        }
+
+        public CallInvocationDetails<string, string> CreateDuplexStreamingCall(CallOptions options = null)
+        {
+            options = options ?? new CallOptions();
+            return new CallInvocationDetails<string, string>(channel, DuplexStreamingMethod, options);
+        }
+
+        public string Host
+        {
+            get
+            {
+                return this.host;
+            }
+        }
+
+        public ServerServiceDefinition ServiceDefinition
+        {
+            get
+            {
+                return this.serviceDefinition;
+            }
+        }
+      
+        public UnaryServerMethod<string, string> UnaryHandler
+        {
+            get
+            {
+                return this.unaryHandler;
+            }
+            set
+            {
+                unaryHandler = value;
+            }
+        }
+
+        public ClientStreamingServerMethod<string, string> ClientStreamingHandler
+        {
+            get
+            {
+                return this.clientStreamingHandler;
+            }
+            set
+            {
+                clientStreamingHandler = value;
+            }
+        }
+
+        public ServerStreamingServerMethod<string, string> ServerStreamingHandler
+        {
+            get
+            {
+                return this.serverStreamingHandler;
+            }
+            set
+            {
+                serverStreamingHandler = value;
+            }
+        }
+
+        public DuplexStreamingServerMethod<string, string> DuplexStreamingHandler
+        {
+            get
+            {
+                return this.duplexStreamingHandler;
+            }
+            set
+            {
+                duplexStreamingHandler = value;
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs
index fc395b0acdab7b6451ce3518e4205a63a0ac3d44..239fc95cb6a761c49918bf1a703d2db4eb9c2e74 100644
--- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs
+++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs
@@ -48,38 +48,15 @@ namespace Grpc.Core.Tests
     /// </summary>
     public class TimeoutsTest
     {
-        const string Host = "localhost";
-        const string ServiceName = "tests.Test";
-
-        static readonly Method<string, string> TestMethod = new Method<string, string>(
-            MethodType.Unary,
-            ServiceName,
-            "Test",
-            Marshallers.StringMarshaller,
-            Marshallers.StringMarshaller);
-
-        static readonly ServerServiceDefinition ServiceDefinition = ServerServiceDefinition.CreateBuilder(ServiceName)
-            .AddMethod(TestMethod, TestMethodHandler)
-            .Build();
-
-        // provides a way how to retrieve an out-of-band result value from server handler
-        static TaskCompletionSource<string> stringFromServerHandlerTcs;
-
+        MockServiceHelper helper = new MockServiceHelper();
         Server server;
         Channel channel;
 
         [SetUp]
         public void Init()
         {
-            server = new Server
-            {
-                Services = { ServiceDefinition },
-                Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
-            };
-            server.Start();
-            channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure);
-
-            stringFromServerHandlerTcs = new TaskCompletionSource<string>();
+            server = helper.GetServer();
+            channel = helper.GetChannel();
         }
 
         [TearDown]
@@ -98,40 +75,44 @@ namespace Grpc.Core.Tests
         [Test]
         public void InfiniteDeadline()
         {
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => {
+                Assert.AreEqual(DateTime.MaxValue, context.Deadline);
+                return "PASS";
+            });
+
             // no deadline specified, check server sees infinite deadline
-            var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions());
-            Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE"));
+            Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
 
             // DateTime.MaxValue deadline specified, check server sees infinite deadline
-            var callDetails2 = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions());
-            Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails2, "RETURN_DEADLINE"));
+            Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.MaxValue)), "abc"));
         }
 
         [Test]
         public void DeadlineTransferredToServer()
         {
-            var remainingTimeClient = TimeSpan.FromDays(7);
-            var deadline = DateTime.UtcNow + remainingTimeClient;
-            Thread.Sleep(1000);
-            var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline));
-
-            var serverDeadlineTicksString = Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE");
-            var serverDeadline = new DateTime(long.Parse(serverDeadlineTicksString), DateTimeKind.Utc);
-
-            // A fairly relaxed check that the deadline set by client and deadline seen by server
-            // are in agreement. C core takes care of the work with transferring deadline over the wire,
-            // so we don't need an exact check here.
-            Assert.IsTrue(Math.Abs((deadline - serverDeadline).TotalMilliseconds) < 5000);
+            var clientDeadline = DateTime.UtcNow + TimeSpan.FromDays(7);
+
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => {
+                // A fairly relaxed check that the deadline set by client and deadline seen by server
+                // are in agreement. C core takes care of the work with transferring deadline over the wire,
+                // so we don't need an exact check here.
+                Assert.IsTrue(Math.Abs((clientDeadline - context.Deadline).TotalMilliseconds) < 5000);
+                return "PASS";
+            });
+            Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: clientDeadline)), "abc");
         }
 
         [Test]
         public void DeadlineInThePast()
         {
-            var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: DateTime.MinValue));
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => {
+                await Task.Delay(60000);
+                return "FAIL";
+            });
 
             try
             {
-                Calls.BlockingUnaryCall(callDetails, "TIMEOUT");
+                Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.MinValue)), "abc");
                 Assert.Fail();
             }
             catch (RpcException e)
@@ -144,12 +125,14 @@ namespace Grpc.Core.Tests
         [Test]
         public void DeadlineExceededStatusOnTimeout()
         {
-            var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5));
-            var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline));
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => {
+                await Task.Delay(60000);
+                return "FAIL";
+            });
 
             try
             {
-                Calls.BlockingUnaryCall(callDetails, "TIMEOUT");
+                Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc");
                 Assert.Fail();
             }
             catch (RpcException e)
@@ -162,12 +145,20 @@ namespace Grpc.Core.Tests
         [Test]
         public void ServerReceivesCancellationOnTimeout()
         {
-            var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5));
-            var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline));
+            string receivedCancellation = "NO";
+
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => {
+                // wait until cancellation token is fired.
+                var tcs = new TaskCompletionSource<object>();
+                context.CancellationToken.Register(() => { tcs.SetResult(null); });
+                await tcs.Task;
+                receivedCancellation = "YES";
+                return "";
+            });
 
             try
             {
-                Calls.BlockingUnaryCall(callDetails, "CHECK_CANCELLATION_RECEIVED");
+                Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc");
                 Assert.Fail();
             }
             catch (RpcException e)
@@ -175,38 +166,7 @@ namespace Grpc.Core.Tests
                 // We can't guarantee the status code is always DeadlineExceeded. See issue #2685.
                 Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal });
             }
-            Assert.AreEqual("CANCELLED", stringFromServerHandlerTcs.Task.Result);
-        }
-            
-        private static async Task<string> TestMethodHandler(string request, ServerCallContext context)
-        {
-            if (request == "TIMEOUT")
-            {
-                await Task.Delay(60000);
-                return "";
-            }
-
-            if (request == "RETURN_DEADLINE")
-            {
-                if (context.Deadline == DateTime.MaxValue)
-                {
-                    return "DATETIME_MAXVALUE";
-                }
-
-                return context.Deadline.Ticks.ToString();
-            }
-
-            if (request == "CHECK_CANCELLATION_RECEIVED")
-            {
-                // wait until cancellation token is fired.
-                var tcs = new TaskCompletionSource<object>();
-                context.CancellationToken.Register(() => { tcs.SetResult(null); });
-                await tcs.Task;
-                stringFromServerHandlerTcs.SetResult("CANCELLED");
-                return "";
-            }
-
-            return "";
+            Assert.AreEqual("YES", receivedCancellation);
         }
     }
 }