同一合同中不能有两个同名操作:自动生成的异步方法

Cannot have two operations in the same contract with the same name : Auto Generated Async Method

编辑更正:

我的错误是,我没有使用 ChatService.IChatService,而是使用了 ReflectorLike.ChatServiceReference.IchatService。

换句话说,AFAIU 我引用了对接口的引用,而不是接口本身。 (如果您找到更好的解释方式,请建议他们,我稍后会编辑答案) 如果您 运行 遇到同样的错误,请确保您使用了正确的界面。

问题:

错误:

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.

总结: 我试着这样做 Recommended patterns for unit testing web services

但是我的模拟服务抛出异常,因为我的所有方法都有同名的双胞胎,例如它同时具有违反 WCF 服务规则的 ClientConnect 和 ClientConnectAsync

我有一个接口是

的服务
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;
        }
    }


}

我想使用模拟服务测试我的客户端,所以我使用 nunit 和 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");
        }
    }
}

用于测试目的的模拟服务将是一个与您的服务具有相同接口的模拟对象,不一定完全实现或实际的 ServiceHost 对象作为您正在尝试做的。

我建议您只传递 myMockedService 对象,因为您的客户端代码应该仅依赖于该接口 - 底层实现与单元测试目的无关(或应该)。

按照toadflakz的建议,我只是一个一个地模拟我所有的服务方法(我的服务很小,工作量不大,但如果服务更大,你可能会有麻烦) 我需要模拟很多东西,因为 signalR、服务、集线器、上下文......:[=​​12=]

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
        }
    }
}

我写了博客 post Hosting a Mock as a WCF service,您查看后创建了 MockServiceHostFactory

两件事:

  1. 您不需要调用该行来排除 ServiceContractAttribute 被复制到模拟对象,这由 NSubstitute 为您处理。
  2. 一定有一些代码你没有给我们,我已经把你现有的代码和运行放在本地,它模拟得很好。

这是我的有效代码。

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;
        }
    }
}

您能否显示任何可能重要的缺失代码,尤其是如果您为了提出问题而对其进行了简化?您能否包含堆栈跟踪以显示您实际在哪一行收到错误?