wpf 使用 Moq 模拟 ViewModel 进行单元测试?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28942670/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 12:59:56  来源:igfitidea点击:

Mocking a ViewModel for unit testing with Moq?

c#wpfwcfunit-testingmoq

提问by Hardgraf

New to unit testing. I have a WPF client app hooked into a WCF service via basicHttpbinding. Everything works great. I'm using simple constructor Dependency Injection in my viewModel, passing in an IServiceChannelwhich I then call me service methods on e.g:

单元测试的新手。我有一个 WPF 客户端应用程序通过basicHttpbinding. 一切都很好。我在我的 viewModel 中使用简单的构造函数依赖注入,传入一个IServiceChannel然后我调用我的服务方法,例如:

IMyserviceChannel = MyService;

public MyViewModel(IMyServiceChannel myService)
{
   this.MyService = myService;  
}

Private void GetPerson()
{
  var selectedPerson = MyService.GetSelectedPerson();
}

I have then added an MS Test project in the client app and I'm trying to use Moq to mock my service:

然后我在客户端应用程序中添加了一个 MS 测试项目,我正在尝试使用 Moq 来模拟我的服务:

  [TestMethod]
    public void GetArticleBody_Test_Valid()
    {
        // Create channel mock
        Mock<IIsesServiceChannel> channelMock = new Mock<IIsesServiceChannel>(MockBehavior.Strict);    

        // setup the mock to expect the Reverse method to be called
        channelMock.Setup(c => c.GetArticleBody(1010000008)).Returns("110,956 bo/d, 1.42 Bcfg/d and 4,900 bc/d. ");

        // create string helper and invoke the Reverse method
        ArticleDataGridViewModel articleDataGridViewModel = new ArticleDataGridViewModel(channelMock.Object);
        string result = channelMock.GetArticleBody(1010000008);
        //Assert.AreEqual("cba", result);

        //verify that the method was called on the mock
        channelMock.Verify(c => c.GetArticleBody(1010000008), Times.Once());
    }

The test is failing with a System.NullReferenceException. Object reference not set to an instance of an object.at the method invocation here:

测试失败并System.NullReferenceException. Object reference not set to an instance of an object.在此处调用方法:

 string result = articleDataGridViewModel.IsesService.GetArticleBody(1010000008);

so I'm wandering whether this is the best way to approach or am I better somehow mocking an isolated part of the viewModel which is applicable to the test?

所以我在徘徊这是最好的方法还是我最好以某种方式嘲笑适用于测试的 viewModel 的孤立部分?

采纳答案by Thomas Lielacher

The NullReferenceExceptionis mybe thrown because you use MockBehavior.Strict. The documentation says:

NullReferenceException是 mybe 因为你使用MockBehavior.Strict. 文档说:

Causes this mock to always throw an exception for invocations that don't have a corresponding setup.

导致此模拟始终为没有相应设置的调用抛出异常。

Maybe the constructor of ArticleDataGridViewModelcalls other methods of the service which you haven't set up. Another issue is, that you are calling the mocked method directly. Instead you should call a method of your view model, which calls this method.

也许构造函数ArticleDataGridViewModel调用了您尚未设置的服务的其他方法。另一个问题是,您直接调用模拟方法。相反,您应该调用视图模型的一个方法,该方法调用此方法。

[TestMethod]
public void GetArticleBody_Test_Valid()
{
    // Create channel mock
    Mock<IIsesServiceChannel> channelMock = new Mock<IIsesServiceChannel>();    

    // setup the mock to expect the Reverse method to be called
    channelMock.Setup(c => c.GetArticleBody(1010000008)).Returns("110,956 bo/d, 1.42 Bcfg/d and 4,900 bc/d. ");

    // create string helper and invoke the Reverse method
    ArticleDataGridViewModel articleDataGridViewModel = new ArticleDataGridViewModel(channelMock.Object);
    string result = articleDataGridViewModel.MethodThatCallsService();
    //Assert.AreEqual("cba", result);

    //verify that the method was called on the mock
    channelMock.Verify(c => c.GetArticleBody(1010000008), Times.Once());
}

Besides that I think there is no problem with your approach. Maybe the view model violates the single responsibility principle and does more than it should, but that's hard to tell on the basis of your code example.

除此之外,我认为你的方法没有问题。也许视图模型违反了单一职责原则并且做的比它应该做的更多,但是根据您的代码示例很难说。

EDIT: Here's a full example of how you could test something like this:

编辑:这是一个完整的例子,说明如何测试这样的东西:

public interface IMyService
{
    int GetData();
}

public class MyViewModel
{
    private readonly IMyService myService;

    public MyViewModel(IMyService myService)
    {
        if (myService == null)
        {
            throw new ArgumentNullException("myService");
        }

        this.myService = myService;
    }

    public string ShowSomething()
    {
        return "Just a test " + this.myService.GetData();
    }
}

class TestClass
{
    [TestMethod]
    public void TestMethod()
    {
        var serviceMock = new Mock<IMyService>();
        var objectUnderTest = new MyViewModel(serviceMock.Object);

        serviceMock.Setup(x => x.GetData()).Returns(42);

        var result = objectUnderTest.ShowSomething();

        Assert.AreEqual("Just a test 42", result);
        serviceMock.Verify(c => c.GetData(), Times.Once());
    }
}

回答by C Bauer

Without access to your viewmodel, there's only so much help that we can provide you.

无法访问您的视图模型,我们只能为您提供这么多帮助。

However, this code:

但是,这段代码:

Mock<IIsesServiceChannel> channelMock = new Mock<IIsesServiceChannel>(MockBehavior.Strict);
...
ArticleDataGridViewModel articleDataGridViewModel = new ArticleDataGridViewModel(channelMock.Object);
...
string result = articleDataGridViewModel.IsesService.GetArticleBody(1010000008);

Does not set up your IsesService. If it is not set up in your constructor, that means the IsesService is a null reference. You can't call a method on a null object.

不设置您的 IsesService。如果它没有在你的构造函数中设置,那意味着 IsesService 是一个空引用。您不能在空对象上调用方法。

回答by Scott Nimrod

Consider mocking out at a higher level of abstraction then the tight coupling you have with the tool your using.

考虑在更高的抽象级别进行模拟,然后是与您使用的工具的紧密耦合。

Perhaps your view-model should rely on services and not a detail of the tool that your using (i.e. IIsesServiceChannel).

也许您的视图模型应该依赖于服务而不是您使用的工具的详细信息(即 IIsesServiceChannel)。

Here's an example:

下面是一个例子:

Construct testable business layer logic

构建可测试的业务层逻辑