C#Moq - Mocking Action <t>参数

时间:2017-02-23 12:05:22

标签: c# unit-testing action moq

我一直在使用moq设置,我希望得到你的帮助,感谢任何帮助。

基本代码

public interface IMessageHub
{
    void Subscribe<T>(string topic, Action<T> callback);
}

拥有消息中心,您可以在其中订阅主题,如果有消息已发布,则会使用消息调用回调。

每条消息都来自

public class MessageBase { }

例如,我创建了2种消息类型:

public class RefreshMessage : MessageBase { }

public class CancelMessage : MessageBase { }

在代码的某些部分,消息有2个订阅

...
MessageHub.Subscribe<RefreshMessage>("Refresh", RefreshMethod);
MessageHub.Subscribe<CancelMessage>("Cancel", CancelMethod);
...

回调方法是私有的,所以我只能通过单元测试中的设置来获取它们。

private void RefreshMethod(RefreshMessage message) { ... }
private void CancelMethod(CancelMessage message) { ... }

这些部分是由框架提供的,所以我无法改变它们。

单元测试

在单元测试中,我想用Moq模拟MessageHub,并为每个动作模拟Subscribe方法,并获得调用subscribe方法的回调方法。

通常,我必须为每个消息类型创建2个设置,如下所示:

var messageHubMock = new Mock<IMessageHub>();
var methods = new List<dynamic>();

//setup for RefreshMessage
messageHubMock.Setup(_=>_.Subscribe(It.IsAny<string>,It.IsAny<Action<RefreshMessage>>())
.Callback<string,Action<RefreshMessage>>((s,a)=> methods.Add(a));

//setup for CancelMessage
messageHubMock.Setup(_=>_.Subscribe(It.IsAny<string>,It.IsAny<Action<CancelMessage>>())
.Callback<string,Action<CancelMessage>>((s,a)=> methods.Add(a));

设置问题

问题是,如何在1个设置中进行2设置?

以下设置不起作用,但显示我尝试过的那些。

messageHubMock.Setup(_=>_.Subscribe(It.IsAny<string>,It.IsAny<Action<object>>())
.Callback<string,Action<object>>((s,a)=> methods.Add(a));

messageHubMock.Setup(_=>_.Subscribe(It.IsAny<string>,It.IsAny<Action<dynamic>>())
.Callback<string,Action<dynamic>>((s,a)=> methods.Add(a));

messageHubMock.Setup(_=>_.Subscribe(It.IsAny<string>,It.IsAny<object>())
.Callback<string,object>((s,a)=> methods.Add(a));
//does not compile
messageHubMock.Setup(_=>_.Subscribe(It.IsAny<string>,It.IsAny<Action>())
.Callback<string,Action>((s,a)=> methods.Add(a));

messageHubMock.Setup(_=>_.Subscribe(It.IsAny<string>,It.IsAny<Action<MessageBase>>())
.Callback<string,Action<MessageBase>>((s,a)=> methods.Add(a));

所有这些都不适合表达式,所以即使在代码中调用了subscibre,Moq也不会在设置上运行Callback,因为subscribe是用“不同”参数调用的。

我想我需要在此创建自己的表达式过滤器逻辑。 你能帮助我,如何创建这个设置,只有1个设置?

1 个答案:

答案 0 :(得分:0)

有时,Moq始终是这项工作的最佳工具。请尝试使用简单的存根。我仍然在寻找一种在Moq中实现它的方法,但现在应该可以使用。

[TestClass]
public class UnitTest {
    [TestMethod]
    public void _Mock_Action_T_Without_Moq() {
        //Arrange
        var callbacks = new Dictionary<string, dynamic>();
        var messageHubMock = new MessageHubStub(callbacks);

        //Act
        var sut = new Sut(messageHubMock.Object);

        //Assert
        Assert.IsTrue(callbacks.Count == 2);
        Assert.IsNotNull(callbacks["Refresh"]);
        Assert.IsNotNull(callbacks["Cancel"]);
    }


    public class Sut {
        public Sut(IMessageHub MessageHub) {
            MessageHub.Subscribe<RefreshMessage>("Refresh", RefreshMethod);
            MessageHub.Subscribe<CancelMessage>("Cancel", CancelMethod);
        }

        private void CancelMethod(CancelMessage obj) {
            throw new NotImplementedException();
        }

        private void RefreshMethod(RefreshMessage obj) {
            throw new NotImplementedException();
        }
    }

    public class MessageHubStub : IMessageHub {
        private Dictionary<string, dynamic> callbacks;


        public MessageHubStub(Dictionary<string, dynamic> callbacks) {
            this.callbacks = callbacks;
        }

        public IMessageHub Object { get { return this; } }

        public void Subscribe<T>(string topic, Action<T> callback) {
            callbacks.Add(topic, callback);
        }
    }

    public interface IMessageHub {
        void Subscribe<T>(string topic, Action<T> callback);
    }

    public class MessageBase { }

    public class RefreshMessage : MessageBase { }

    public class CancelMessage : MessageBase { }
}

此外,如果每条消息都来自MessageBase,请考虑将其作为集线器的约束

public interface IMessageHub {
    void Subscribe<T>(string topic, Action<T> callback) where T : MessageBase;
}