0

edit Correction:

My Error was that Instead of using ChatService.IChatService I was using ReflectorLike.ChatServiceReference.IchatService.

In other words , AFAIU I was referencing a reference to the Interface rather than the Interface it self. (If you find better way to explain that please suggest them I'll edit the answer later) If you run in the same error be sure that you use the correct Interface.

Issue:

The Error:

SetUp : System.InvalidOperationException : Cannot have two operations in the same contract with the same name, methods ClientConnectAsync and ClientConnect in type ReflectorLike.ChatReference.IChatService violate this rule. You can change the name of one of the operations by changing the method name or by using the Name property of OperationContractAttribute.

Summary: I tried to do this Recommended patterns for unit testing web services

But my mocked service throws me an exception because all my methods have twins with same name for instance it has both ClientConnect and ClientConnectAsync which violate WCF Service Rule

I have a service which interface is

namespace ChatService
{
    [ServiceContract]
    public interface IChatService
    {
        [OperationContract]
        ChatUser ClientConnect(string userName);

        [OperationContract]
        void SendNewMessage(ChatMessage newMessage);

        [OperationContract]
        List<ChatUser> GetAllUsers();

        [OperationContract]
        void RemoveUser(ChatUser user);

        [OperationContract]
        List<ChatMessage> GetNewMessages(ChatUser user);
    }


    [DataContract]
    public class ChatMessage
    {
        [DataMember]
        public ChatUser User { get; set; }

        [DataMember]
        public string Message { get; set; }

        private DateTime date;
        [DataMember]
        public DateTime Date
        {
            get { return date; }
            set { date = value; }
        }
    }

    /// <summary>
    /// 
    /// </summary>
    [DataContract]
    public class ChatUser
    {
        [DataMember]
        public string UserName { get; set; }

        [DataMember]
        public string IpAddress { get; set; }

        [DataMember]
        public string HostName { get; set; }


        public ChatUser(string userName)
        {
            this.UserName = userName;
        }

        public override string ToString()
        {
            return this.UserName;
        }
    }


}

I want to test my client using a mock service so I Test it with nunit and nsubstitute

    namespace ReflectorLike.Tests
{
    [TestFixture]
    internal class ChatHubTester
    {

        private ChatHub hub;
        private ServiceHost host;
        private IChatService myMockedService;
        [SetUp]
        public void SetUp()
        {
            Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add<ServiceContractAttribute>();
            myMockedService = Substitute.For<IChatService>();
            host = MockServiceHostFactory.GenerateMockServiceHost(myMockedService, new Uri("http://localhost:12345"), "ServiceEndPoint");
            host.Open();
            hub=new ChatHub();
        }

        [TearDown]
        public void TearDown()
        {
            host.Close();
        }




        [Test]
        public void SomeTest()
        {

            hub.Connect("Test");
        }
    }
}
Community
  • 1
  • 1
Lomithrani
  • 2,033
  • 3
  • 18
  • 24

3 Answers3

1

A mocked service for testing purposes would be a mock object with the same interface as your service, not necessarily fully implemented or an actual ServiceHost object as what you are trying to do.

I would recommend that all you do is pass the myMockedService object as your client code should depend on that interface alone - the underlying implementation is irrelevant (or should be) for unit testing purposes.

toadflakz
  • 7,764
  • 1
  • 27
  • 40
  • I'm not sure I fully understood your answer but I think I can't do what you suggested, as I'm testing a signalR hub which access the service, but the connection to my service is done through reference (I have no constructor where I connect to my service) I do it this way : private readonly ChatServiceClient client = new ChatServiceClient(); So I really need to the ServiceHost thing, but I get the error – Lomithrani May 21 '15 at 10:21
  • In order to make it testable you will need to abstract away the `ChatServiceClient` concrete class or create a wrapper that exposes `ChatServiceClient` object through `IChatService`. Your hub code should depend and make calls against an object of `IChatService` type. After all - what you are testing is that the hub calls the right methods on the service and responds to data from the service in the right way. Mocking the entire WCF stack is unnecessary and would be impossible to mock the returned values using NSubstitute in this way. Remember the 'D' of SOLID principles. – toadflakz May 21 '15 at 10:27
  • Ok I'll try to change all that, but I still can't understand why I get this error – Lomithrani May 21 '15 at 11:23
  • I should add that I got the code here and didn't make much changes : http://stackoverflow.com/questions/9587393/recommended-patterns-for-unit-testing-web-services – Lomithrani May 21 '15 at 11:56
  • That code is for testing WCF service code itself rather than testing code which uses a WCF service as what you've said your code is being used for with your SignalR hub. Unless I am misunderstanding what you are tying to do? – toadflakz May 21 '15 at 12:04
  • No you are not misunderstanding, but still this behavior seems odd, I wouldn't expect an exception even if the test doesn't do what I want. – Lomithrani May 21 '15 at 12:07
  • Plus you can see that he can call client.DoWork(); which is exactly what I want to do. He tests the client which is exactly what I want to do. – Lomithrani May 21 '15 at 12:15
1

I wrote the blog post Hosting a Mock as a WCF service which you looked at to create MockServiceHostFactory.

Two things:

  1. You don't need to call the line to exclude the ServiceContractAttribute from being copied to the mock object, this is handled for you by NSubstitute.
  2. There must be some code that you have not given us, I have taken your existing code and run it locally and it mocks fine.

Here is the code that I have that works.

class Foo
{
    [Test]
    public void Should_work()
    {
        var myWcfServiceMock = Substitute.For<IChatService>();
        var mockServiceHost = MockServiceHostFactory.GenerateMockServiceHost(myWcfServiceMock , new Uri("http://localhost:8001"), "MyService");
        mockServiceHost.Open();
        mockServiceHost.Close();
    }

    public static class MockServiceHostFactory
    {
        public static ServiceHost GenerateMockServiceHost<TMock>(TMock mock, Uri baseAddress, string endpointAddress)
        {
            var serviceHost = new ServiceHost(mock, new[] { baseAddress });

            serviceHost.Description.Behaviors.Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true;
            serviceHost.Description.Behaviors.Find<ServiceBehaviorAttribute>().InstanceContextMode = InstanceContextMode.Single;

            serviceHost.AddServiceEndpoint(typeof(TMock), new BasicHttpBinding(), endpointAddress);

            return serviceHost;
        }
    }
}

Can you show any missing bits of code that might be important especially if you have simplified it for the purpose of asking the question? Can you include the stack trace that shows which line you actually get the error on?

Bronumski
  • 14,009
  • 6
  • 49
  • 77
  • Thanks for your answer, As you can see in my answer I totally gave up and went for something else entirely. After a few second back on trying again I spotted my error. I was referencing the reference (does it make sense ? ) Instead of '#using myservice' I wrote '#using myasp.myserviceReference'. So I was using the wrong interface ... :( – Lomithrani May 29 '15 at 12:41
0

Following toadflakz advices I just mock all my service methods one by one (my service being small it's not that much of a work but with a bigger service you could be in trouble) I Needed to mock a lot of things, because of signalR, the service, the hub, the context ...:

namespace ReflectorLike.Tests
{
    [TestFixture]
    internal class ChatHubTester
    {
        [SetUp]
        public void SetUp()
        {
            var request = Substitute.For<IRequest>();
            request.User.Identity.Name.Returns("IdentityName");

            var clients = Substitute.For<IHubCallerConnectionContext<ExpandoObject>>();
            clients.Group("groupName").Returns(new ExpandoObject());

            var groupManager = Substitute.For<IGroupManager>();

            context = Substitute.For<HubCallerContext>(request, "123");
            context.ConnectionId.Returns(rank.ToString(CultureInfo.InvariantCulture));

            myMockedClient = Substitute.For<IChatService>();
            myMockedClient.When(x => x.RemoveUser(Arg.Any<ChatUser>())).DoNotCallBase();
            myMockedClient.When(x => x.SendNewMessage(Arg.Any<ChatMessage>())).DoNotCallBase();
            var testList = new List<ChatMessage> { new ChatMessage { Message = "Test Message", User = new ChatUser{ UserName = "LastUser"}} }.ToArray();
            myMockedClient.GetNewMessages(Arg.Any<ChatUser>()).Returns(testList);


            UpdateClientConnect(false);

            hub = Substitute.ForPartsOf<ChatHub>(myMockedClient, context, groupManager);
            hub.When(x => x.Broadcast(Arg.Any<ChatMessage>())).DoNotCallBase();
            hub.When(x => x.EmitTo(Arg.Any<string>(), Arg.Any<ChatMessage>())).DoNotCallBase();
        }

        public void UpdateClientConnect(bool last)
        {
            myMockedClient.ClientConnect(Arg.Any<string>()).Returns(new ChatUser { UserName = "TestUser" + rank }).AndDoes(x =>
                                                                                                                           {
                                                                                                                               context.ConnectionId
                                                                                                                                      .Returns(
                                                                                                                                               rank
                                                                                                                                                   .ToString
                                                                                                                                                   (CultureInfo
                                                                                                                                                        .InvariantCulture));
                                                                                                                               if (!last)
                                                                                                                               {
                                                                                                                                   rank ++;
                                                                                                                               }
                                                                                                                           });
        }

        private HubCallerContext context;
        private IChatService myMockedClient;
        private ChatHub hub;
        private static int rank;
        private const bool LAST = true;
        private const bool NOTLAST = false;

        [Test]
        public void Connect()
        {
            hub.Connect("0");
            UpdateClientConnect(LAST);
            hub.Connect("1");

            int i = 0;
            foreach (ICall call in hub.ReceivedCalls())
            {
                Assert.AreEqual("TestUser" + i + " connected", ((ChatMessage)(call.GetArguments()[0])).Message);
                Assert.AreEqual("SYSTEM", ((ChatMessage)(call.GetArguments()[0])).User.UserName);
                i++;
            }
            Assert.AreEqual(2, i); // 2 items
        }
    }
}
Community
  • 1
  • 1
Lomithrani
  • 2,033
  • 3
  • 18
  • 24