How to use moq to test a concrete method in an abs

2019-02-06 02:01发布

In the past when I wanted to mock an abstract class I'd simply create a mocked class in code that extended the abstract class, then used that class in my unit testing...

public abstract class MyConverter : IValueConverter
{
    public abstract Object Convert(...) { ... };

    public virtual Object ConvertBack(...) { ... }
}

private sealed class MockedConverter : MyConverter { ... }

[TestMethod]
public void TestMethod1()
{
    var mock = new MockedConverter();

    var expected = ...;
    var actual = mock.ConvertBack(...);

    Assert.AreEqual(expected, actual);
}

I want to get into the habit of using Moq instead. I'm not sure how I'd go about using Moq to test the default return value of my abstract class. Any advice here?

3条回答
老娘就宠你
2楼-- · 2019-02-06 02:04

Have you read any of the getting-started guides for Moq? It's pretty simple:

var mock = new Mock<MyConverter>();
var expected = ...;
mock.Setup(m => m.ConvertBack(...)).Returns(expected);
var actual = m.Object.ConvertBack(...);
Assert.AreEqual(expected, actual);

But of course, this is a poor example because you're not actually allowing it to test any real classes. Mocking is useful for providing a mock to a real class that you want to unit test, and which you expect will call a method that you have mocked.

Update

After reading your question again (with the title updated by Anthony Pegram), I'm wondering if you're trying to test a real implementation of ConvertBack by mocking the implementation of Convert. If this is the case, I have a couple of observations:

  1. ConvertBack should probably not be declared virtual, at least for the sake of this example,
  2. You might want to refactor your code so that Convert and ConvertBack are part of different services: I'm sensing a code smell possibly arising from a lack of separation of concerns.

If you're sure you need to do this, it should still be relatively simple:

var mock = new Mock<MyConverter>() {CallBase = true}; // hat tip: adrift
mock.Setup(m => m.Convert(...)).Returns(...);
var expected = ...;
var actual = m.Object.ConvertBack(...);
Assert.AreEqual(expected, actual);
查看更多
贪生不怕死
3楼-- · 2019-02-06 02:29

If you set CallBase to true, it will invoke the base class implementation.

var mock = new Mock<MyConverter> { CallBase = true };

See the Customizing Mock Behavior Customizing Mock Behaviour section of the Quick Start.

Invoke base class implementation if no expectation overrides the member (a.k.a. "Partial Mocks" in Rhino Mocks): default is false.

查看更多
混吃等死
4楼-- · 2019-02-06 02:30

You can setup a Mock on an abstract class just like on an interface. In order to test the abstract implementation you need to set the mock object to call the base method for any functions not defined:

var mock = new Mock<MyConverter>();
mock.CallBase = true;
Assert.AreEqual(expected value,mock.Object.ConvertBack(...));
查看更多
登录 后发表回答