在不使用魔术字符串的情况下将参数传递给 FakeItEasy-mock?

发布于 2024-11-30 17:19:53 字数 3879 浏览 1 评论 0原文

过去几年我一直在使用 Moq 来满足我的模拟需求,但在查看了 Moq 之后/code.google.com/p/fakeiteasy/">FakeItEasy 我想尝试一下。

我经常想测试是否使用正确的参数调用了一个方法,但我没有找到令人满意的方法来使用 FakeItEasy 来执行此操作。

我有以下代码要测试:

    public class WizardStateEngine : IWizardStateEngine
{
    private readonly IWorkflowInvoker _workflowInvoker;
    private List<CustomBookmark> _history;

    public WizardStateEngine(IWorkflowInvoker workflowInvoker)
    {
        _workflowInvoker = workflowInvoker;
    }

    public void Initialize(List<CustomBookmark> history)
    {
        _history = history;
    }

    public WizardStateContext Execute(Command command, WizardStateContext stateContext, CustomBookmark step)
    {
        Activity workflow = new MyActivity();
        var input = new Dictionary<string, object>();
        input["Action"] = command;
        input["Context"] = stateContext;
        input["BookmarkHistory"] = _history;

        var output = _workflowInvoker.Invoke(workflow, input);

        _history = output["BookmarkHistory"] as List<CustomBookmark>;

        return output["Context"] as WizardStateContext;
    }

    public List<CustomBookmark> GetBookmarkHistory()
    {
        return _history;
    }
}

我想编写一些测试来验证 _workflowInvoker.Invoke() 的输入。 我的 TestInitialize 方法设置所需的资源并将输入字典保存到 _workflowInvoker.Invoke() 作为本地字段 _wfInput。

    [TestInitialize]
    public void TestInitialize()
    {
        _wizardStateContext = new WizardStateContext();
        _workflowInvoker = A.Fake<IWorkflowInvoker>();
        _wizardStateEngine = new WizardStateEngine(_workflowInvoker);

        _outputContext = new WizardStateContext();
        _outputHistory = new List<CustomBookmark>();
        _wfOutput = new Dictionary<string, object>
                        {{"Context", _outputContext}, {"BookmarkHistory", _outputHistory}};

        _history = new List<CustomBookmark>();

        A.CallTo(() =>
                 _workflowInvoker.Invoke(A<Activity>.Ignored, A<Dictionary<string, object>>.Ignored))
            .Invokes(x => _wfInput = x.Arguments.Get<Dictionary<string, object>>("input"))
            .Returns(_wfOutput);

        _wizardStateEngine.Initialize(_history);
    }

设置后,我有多个这样的测试:

    [TestMethod]
    public void Should_invoke_with_correct_command()
    {
        _wizardStateEngine.Execute(Command.Start, null, null);

        ((Command) _wfInput["Action"]).ShouldEqual(Command.Start);
    }

    [TestMethod]
    public void Should_invoke_with_correct_context()
    {
        _wizardStateEngine.Execute(Command.Start, _wizardStateContext, null);

        ((WizardStateContext) _wfInput["Context"]).ShouldEqual(_wizardStateContext);
    }

    [TestMethod]
    public void Should_invoke_with_correct_history()
    {
        _wizardStateEngine.Execute(Command.Start, _wizardStateContext, null);

        ((List<CustomBookmark>) _wfInput["BookmarkHistory"]).ShouldEqual(_history);
    }

我不喜欢 TestInitialize 中用于获取传递的参数(或幻数)的魔术字符串“输入”。我可以在没有本地字段的情况下编写测试,如下所示:

    [TestMethod]
    public void Should_invoke_with_correct_context()
    {
        _wizardStateEngine.Execute(Command.Start, _wizardStateContext, null);

        A.CallTo(() =>
                 _workflowInvoker.Invoke(A<Activity>._,
                                         A<Dictionary<string, object>>.That.Matches(
                                             x => (WizardStateContext) x["Context"] == _wizardStateContext)))
            .MustHaveHappened();
    }

但我发现使用本地字段的测试更具可读性。

有没有什么方法可以在我的测试类中将输入设置为字段保存而无需幻数或字符串?

我希望问题中更新的示例能够说明为什么我想使用本地字段。如果我能找到一种很好的可读方式来完成它,我非常愿意在没有本地字段的情况下编写我的测试。

I have been using Moq for my mocking needs the last years, but after looking at FakeItEasy i wanted to give it a try.

I often want to test that a method have been called with the correct parameters, but i found no satisfactory way to do this with FakeItEasy.

I have the following code to test:

    public class WizardStateEngine : IWizardStateEngine
{
    private readonly IWorkflowInvoker _workflowInvoker;
    private List<CustomBookmark> _history;

    public WizardStateEngine(IWorkflowInvoker workflowInvoker)
    {
        _workflowInvoker = workflowInvoker;
    }

    public void Initialize(List<CustomBookmark> history)
    {
        _history = history;
    }

    public WizardStateContext Execute(Command command, WizardStateContext stateContext, CustomBookmark step)
    {
        Activity workflow = new MyActivity();
        var input = new Dictionary<string, object>();
        input["Action"] = command;
        input["Context"] = stateContext;
        input["BookmarkHistory"] = _history;

        var output = _workflowInvoker.Invoke(workflow, input);

        _history = output["BookmarkHistory"] as List<CustomBookmark>;

        return output["Context"] as WizardStateContext;
    }

    public List<CustomBookmark> GetBookmarkHistory()
    {
        return _history;
    }
}

I want to write some tests that verifies the input to _workflowInvoker.Invoke().
My TestInitialize method sets up the needed resources and save the input dictionary to _workflowInvoker.Invoke() as a local field _wfInput.

    [TestInitialize]
    public void TestInitialize()
    {
        _wizardStateContext = new WizardStateContext();
        _workflowInvoker = A.Fake<IWorkflowInvoker>();
        _wizardStateEngine = new WizardStateEngine(_workflowInvoker);

        _outputContext = new WizardStateContext();
        _outputHistory = new List<CustomBookmark>();
        _wfOutput = new Dictionary<string, object>
                        {{"Context", _outputContext}, {"BookmarkHistory", _outputHistory}};

        _history = new List<CustomBookmark>();

        A.CallTo(() =>
                 _workflowInvoker.Invoke(A<Activity>.Ignored, A<Dictionary<string, object>>.Ignored))
            .Invokes(x => _wfInput = x.Arguments.Get<Dictionary<string, object>>("input"))
            .Returns(_wfOutput);

        _wizardStateEngine.Initialize(_history);
    }

After the setup i have multiple tests like this:

    [TestMethod]
    public void Should_invoke_with_correct_command()
    {
        _wizardStateEngine.Execute(Command.Start, null, null);

        ((Command) _wfInput["Action"]).ShouldEqual(Command.Start);
    }

    [TestMethod]
    public void Should_invoke_with_correct_context()
    {
        _wizardStateEngine.Execute(Command.Start, _wizardStateContext, null);

        ((WizardStateContext) _wfInput["Context"]).ShouldEqual(_wizardStateContext);
    }

    [TestMethod]
    public void Should_invoke_with_correct_history()
    {
        _wizardStateEngine.Execute(Command.Start, _wizardStateContext, null);

        ((List<CustomBookmark>) _wfInput["BookmarkHistory"]).ShouldEqual(_history);
    }

I do not like the magic string "input" in the TestInitialize for getting the passed argument (or magic number). I can write the tests without the local field like this:

    [TestMethod]
    public void Should_invoke_with_correct_context()
    {
        _wizardStateEngine.Execute(Command.Start, _wizardStateContext, null);

        A.CallTo(() =>
                 _workflowInvoker.Invoke(A<Activity>._,
                                         A<Dictionary<string, object>>.That.Matches(
                                             x => (WizardStateContext) x["Context"] == _wizardStateContext)))
            .MustHaveHappened();
    }

But i find the tests with the local field more readable.

Are there any way to setup saving of the input as a field i my test class without magic numbers or strings?

I hope the updated example in the question shows why i would like to use the local field. I am more than willing to write my tests without the local field if i can find a nice readable way to do it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

素染倾城色 2024-12-07 17:19:53
A.CallTo(() => service.DoSomething(A<int>.That.Matches(x => x == 100)))
 .MustHaveHappened();
A.CallTo(() => service.DoSomething(A<int>.That.Matches(x => x == 100)))
 .MustHaveHappened();
各自安好 2024-12-07 17:19:53

我同意达林所说的一切,做你正在做的事情似乎是一种不好的做法。你说在这个简单的例子中它看起来“愚蠢”,你能提供一个看起来很聪明的例子吗?

无论如何,以下测试将具有与 Moq 测试完全相同的行为:

[Test]
public void Should_do_something_with_correct_input()
{
    int inputNumber = 0;

    var service = A.Fake<IService>();
    A.CallTo(() => service.DoSomething(A<int>._))
        .Invokes((int x) => inputNumber = x);

    var system = new System(service);
    system.InvokeService();

    inputNumber.ShouldEqual(100);
}

I agree with everything Darin says, it seems like a bad practice to do what you're doing. You say that it looks "stupid" in this trivial example, could you provide an example where it looks smart?

Anyhow, the following test would have exactly the same behaviour as the Moq-test:

[Test]
public void Should_do_something_with_correct_input()
{
    int inputNumber = 0;

    var service = A.Fake<IService>();
    A.CallTo(() => service.DoSomething(A<int>._))
        .Invokes((int x) => inputNumber = x);

    var system = new System(service);
    system.InvokeService();

    inputNumber.ShouldEqual(100);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文