-1

I followed this question Using Moq to mock only some methods but still threw out an exception.

Here is my main class. Foo acts as a decorator.

public class Foo {
    public Bar _b {get; set;}

    public Foo(Bar b) {
        this._b = b;
    }

    public bool MyMethod(){
        
        return ComplexMethod(_b.name);
    }

    public bool ComplexMethod(){
        ...
    }
}

Test class

[TestClass]
public class Foo {
    [TestMethod]
    public void TestFoo() {
        var b = new Bar() {name = "name"};
        var mock = new Mock<Foo>(b);
        mock.CallBase = true;
        mock.Setup(x => x.ComplexMethod()).Returns(true);
        var result = mock.Object.MyMethod();
        ...
    }
}

Threw out exception:

System.NotSupportedException: 'Unsupported expression: x => x.ComplexMethod() Non-overridable members (here: Foo.ComplexMethod) may not be used in setup / verification expressions.'

rj487
  • 4,476
  • 6
  • 47
  • 88
  • 4
    You can only mock virtual methods or interfaces. Make the method virtual or abstract out an interface – pinkfloydx33 Jun 16 '21 at 22:28
  • There are two types of mocking frameworks: constrained and unconstrained. The former: Moq, NSubstitute, FakeItEasy - allow you to fake only virtual members. The latter: TypeMock, JustMock, MS Fakes, Prig, Ionad.Fody, Pose, Harmony, MethodRedirect - allow you to fake anything you want: non-virtual, private and static members, sealed classes and so on. – Alexander Petrov Jun 19 '21 at 15:07

1 Answers1

0

You should mock the dependencies not the actual object. So, if you want to assess MyMethod of Foo then you should mock Bar.

In order to be able to mock Bar's name member:

  • Either you should mark it as virtual or abstract (ref)
  • Or you should define an interface

Let's see the latter case:

public interface IBar
{
   string Name { get; }
}

public class Bar: IBar
{
   public string Name { get; set; }
}

public class Foo 
{
    private readonly IBar _b;
    
    public Foo(IBar b) {
        this._b = b;
    }
    ...
}

With this setup your unit test could look like this:

[TestMethod]
public void GivenABar_WhereTheNameIsEmpty_WhenICallMyMethod_ThenItReturnsTrue() 
{
    //Arrange
    var barMock = new Mock<IBar>();
    barMock.SetupGet(x => x.Name).Returns("");

    //Act
    var SUT = new Foo(barMock.Object);
    var result = SUT.MyMethod();

    //Assert
    barMock.VerifyGet(m => m.Name, Times.Once);
    Assert.True(result);
}
[TestMethod]
public void GivenABar_WhereTheNameIsNotEmpty_WhenICallMyMethod_ThenItReturnsFalse() 
{
    //Arrange
    var barMock = new Mock<IBar>();
    barMock.SetupGet(x => x.Name).Returns("test");

    //Act
    var SUT = new Foo(barMock.Object);
    var result = SUT.MyMethod();

    //Assert
    barMock.VerifyGet(m => m.Name, Times.Once);
    Assert.False(result);
}

UPDATE: Mocking one of the SUT's methods

If you want to mock only just the ComplexMethod of Foo then you can use moq's As.

const bool expectedResult = true;
var fooMock = new Mock<Foo>(barInstance).As<IFoo>();
fooMock.Setup(f => f.ComplexMethod()).Returns(expectedResult);

Please note that this technique works only if Foo implements IFoo, which exposes ComplexMethod as well

public interface IFoo
{
    bool MyMethod();
    bool ComplexMethod();
    ...
}

public class Foo: IFoo
{
    ...
}
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
  • hmm, I don't understand, but the method I want to mock-up is located in Foo. In you example, I don't see where you mock `ComplexMethod` – rj487 Jun 17 '21 at 21:58
  • @CodaChang In your question you are calling the Foo's MyMethod in your test. In the provided Foo implementation this method has nothing to do with ComplexMethod. So, if you want to test MyMethod, which does not rely on ComplexMethod then why do you want to mock it? – Peter Csala Jun 18 '21 at 05:32
  • Sorry, I didn't make my question clear, inside MyMethod, it calls `ComplexMethod`, that's the whole point of mocking. – rj487 Jun 18 '21 at 21:20
  • @CodeChang What do want to test on `MyMethod`? It is just a wrapper around `ComplexMethod`. Do want to make sure that it does not alter the result of `ComplexMethod`? Or what exactly do you want to assess? – Peter Csala Jun 19 '21 at 13:15
  • @CodaChang I've just updated my post please check it. – Peter Csala Jun 19 '21 at 13:25
  • I mean it's just an example, in the real `MyMethod`, it calls `ComplexMethod` and then utilizes the result and do some stuff. Just want to mock the return value of `ComplexMethod`, so I don't have to waste time calling `ComplexMethod` every time. – rj487 Jun 23 '21 at 05:00
  • @CodaChang You can do that with the `As` method. The updated version of the post contains a sample. – Peter Csala Jun 23 '21 at 06:07
  • Thanks, mocking in C# is way more complex than python or ruby. – rj487 Jun 23 '21 at 19:25