-2

I have a lot of experience in Mocking in another languages like Ruby, but I am new with Moq in C#, I am trying to validate what for me is the most basic mock: to validate that a method is calling the proper method with the proper parameter.

I have this example Subject class

public class Subject
{
    int _age;

    public Subject(int age)
    {
        _age = age;
    }

    public void MainMethod()
    {
        if(_age <= 13)
        {
            KidMethod();
        }
        else
        {
            AdultMethod();
        }
    }

    public void KidMethod() {}
    public void AdultMethod() {}
}

And I am trying to create this Test:

[Test]
public void MainMethod_ShouldCallKidMethod_IfAgeBelow1()
{
    var subjectMock = new Mock<Subject>(12);
    subjectMock.Verify(subject => subject.KidMethod());
    subjectMock.MainMethod();
}

But obviously I am doing something wrong because I get this error:

error CS1061: 'Mock' does not contain a definition for 'MainMethod' and no accessible extension method 'MainMethod' accepting a first argument of type 'Mock' could be found (are you missing a using directive or an assembly reference?)

I think I am missing some basic understanding of Mocking with Moq solving this example will help me to get it.

I have been checking similar Questions in SO but all the ones I've checked were covering more specific cases and I didn't find anyone solving this general simple case.

Update 1

This is compiling:

[Test]
public void MainMethod_ShouldCallKidMethod_IfAgeBelow1()
{
    var subjectMock = new Mock<Subject>(12);
    subjectMock.Object.MainMethod();
    subjectMock.Verify(subject => subject.KidMethod());
}

But now I have the error:

System.NotSupportedException : Unsupported expression: subject => subject.KidMethod() Non-overridable members (here: Subject.KidMethod) may not be used in setup / verification expressions.

Looks like that Moq can not be used with methods that can't be overridden 1

Is there no way to do what I am trying?

fguillen
  • 36,125
  • 23
  • 149
  • 210
  • 1
    `Mock` doesn't give you an instance of `T`, I think there is a property on it called Object or something that will be of the type you want. Also I don't think you can Moq not virtual methods fyi – Dave Mar 24 '21 at 13:58
  • Is `Subject` just a simple POCO class that holds values? If so, you probably don't need Moq for that. Just instantiate the class and set some values. However, if you do want to mock out that class for some reason, @Dave is correct: you can only use Moq to mock out interfaces or virtual methods. – srk Mar 24 '21 at 14:02
  • In the question the Subject class is just an example, I am trying to test a more complex class. I was starting from the top and checking that the main method is making the proper decision when calling the secondary method... but this simple test validation looks like it is not possible with Moq? – fguillen Mar 24 '21 at 14:09
  • 1
    Did you tried calling method like this ? `subjectMock.Object.Method1();}`. Also put this call before you verify – Sohan Mar 24 '21 at 14:12
  • @Sohan definitely this put me in the right direction but them I was stuck with the problem that Moq can not be used with methods that can't be overridden: https://stackoverflow.com/a/56905918/316700 :? (check my update) – fguillen Mar 24 '21 at 14:15
  • That is correct, you cannot and no need to Moq interfaces. Simple create Moq for class you need to test/setup and use them – Sohan Mar 24 '21 at 14:20
  • 1
    @fguillen When testing in isolation, the subject under test is not usually mocked except in some extreme edge cases. The example appears to be oversimplified and is diluting what the actual problem may be. You need to clarify what it is you actually want to test with a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) that can be used to reproduce the problem, allowing a better understanding of what is actually being asked. – Nkosi Mar 24 '21 at 14:29
  • @Sohan can you explain me this? `Simple create Moq for class you need to test/setup and use them` – fguillen Mar 24 '21 at 14:34
  • Look at this article, this should help you https://www.infoworld.com/article/3264438/how-to-use-moq-to-ease-unit-testing-in-c.html#:~:text=You%20can%20use%20Moq%20to,should%20be%20marked%20as%20virtual. – Sohan Mar 24 '21 at 14:39
  • @Nkosi I have updated the example in the Question to represent better my real scenario in the most simple way – fguillen Mar 24 '21 at 14:40
  • 1
    You need to move the `MainMethod` invocation *before* the `Verify` invocation in your test. The `Verify` method checks method invocations that have already occurred. Typically it goes at the end of your test. – srk Mar 24 '21 at 14:52
  • @srk thanks this is helping, I changed the example code in the question. Still Moq is complaining about `Non-overridable members` – fguillen Mar 24 '21 at 14:56
  • 3
    @fguillen this is an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). MOQ is not the correct tool for this job. Is there anything in the called methods that can be used to verify expected behavior after invoking the member under test? In a test like this observing the subject's state can be used to determine behavior. That is why I asked to see better represented code of the actual problem. – Nkosi Mar 24 '21 at 15:15

2 Answers2

3

The point is that what you're asking for is not what you should be doing. Yes, mocking framework can verify method calls, on mocks. You mock dependencies.

You don't have a dependency, you want to test that a method on the class you want to test calls another method on the class you want to test.

That's not what mocking nor unit testing are for, you're doing the wrong thing here. A method calling another method on the same class is an implementation detail, and not something that you should test. What matters is the output of the method, or the state of the object after the method call. Test that.

See for example How to use Moq in unit test that calls another method in same class.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • I think I've understood, I should only test the out side changes. I have been using the intended kind of _incremental_ unit testing in other languages. Testing the output of the MainMethod is a very long run and if the result is not the expected I have to do a long debugging session. Having more detailed _inner_ unit tests has helped me in the past to identify internal step mistakes easily.. but I understand it is not the _academic_ way of doing unit testing. – fguillen Mar 24 '21 at 15:27
  • 2
    It's not academic as it is practical: if you notice that you have to rework a lot of code in order to properly test it, that code wasn't good to begin with. Instead of having one massive main method that does all the things, split it up into separate units that handle specific workloads. – CodeCaster Mar 24 '21 at 15:30
0

Your example doesn't make much sense to me: you are trying to mock out certain methods of the same class you are testing. Here is a more typical example.

Your class might use some interface:

public class Subject
{
    private readonly IHelper _helper;
    int _age;

    public Subject(IHelper helper, int age)
    {
        _helper = helper;
        _age = age;
    }

    public void MainMethod()
    {
        if(_age <= 13)
        {
            _helper.KidMethod();
        }
        else
        {
            _helper.AdultMethod();
        }
    }
}

So your test would mock out that interface. This way we are only testing Subject in isolation.

[Test]
public void MainMethod_ShouldCallKidMethod_IfAgeBelow1()
{
    // Arrange.
    var helperMock = new Mock<IHelper>();
    var subject = new Subject(helperMock.Object, 12);

    // Act.
    subject.MainMethod();

    // Assert.
    helperMock.Verify(h => h.KidMethod());
}
srk
  • 1,625
  • 1
  • 10
  • 26
  • Yes, I understand, Moq is useful when you are using dependency injection. In my case I am trying to verify a method call in the Subject instance itself, not in a parameter object. I know the mock concept has not sense here but mocking libraries (in my experience with [Mocha](https://github.com/freerange/mocha)) are also used to check method calls and not only to mocking implementation – fguillen Mar 24 '21 at 15:00