Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock a method which also belongs to the target class itself?

Let's say we are testing a class C which has 2 methods M1 and M2 where M1 calls M2 when executed.

Testing M2 is ok, but how can we test M1? The difficulty is that we need to mock M2 if I'm not misunderstanding things.

If so, how can we mock another method while testing a method defined in the same class?

[Edit]
Class C has no base classes.

like image 818
Nam G VU Avatar asked Oct 11 '10 15:10

Nam G VU


People also ask

How do you mock a method from the same class?

Mocking is done when you invoke methods of a class that has external communication like database calls or rest calls. Through mocking you can explicitly define the return value of methods without actually executing the steps of the method.

How do you mock a function of a class in Jest?

Jest can be used to mock ES6 classes that are imported into files you want to test. ES6 classes are constructor functions with some syntactic sugar. Therefore, any mock for an ES6 class must be a function or an actual ES6 class (which is, again, another function). So you can mock them using mock functions.

How do you mock this method in Jest?

There are two ways to mock functions: Either by creating a mock function to use in test code, or writing a manual mock to override a module dependency.

Can I mock private methods using Moq?

Moq supports mocking protected methods. Changing the methods to protected , instead of private , would allow you to mock their implementation.


3 Answers

You can do this by setting the CallBase property on the mock to true.

For example, if I have this class:

public class Foo
{
    public virtual string MethodA()
    {
        return "A";
    }
    public virtual string MethodB()
    {
        return MethodA() + "B";
    }
}

I can setup MethodA and call MethodB:

[Fact]
public void RunTest()
{
    Mock<Foo> mockFoo = new Mock<Foo>();
    mockFoo.Setup(x => x.MethodA()).Returns("Mock");
    mockFoo.CallBase = true;

    string result = mockFoo.Object.MethodB();

    Assert.Equal("MockB", result);
}
like image 163
Jeff Ogata Avatar answered Sep 28 '22 00:09

Jeff Ogata


You should let the call to M1 pass through to a real instance of the M2 method.

In general, you should be testing the black box behaviour of your classes. Your tests shouldn't care that M1 happens to call M2 - this is an implementation detail.

This isn't the same as mocking external dependencies (which you should do)...


For example, say I have a class like this:

class AccountMerger
{
    public AccountMerger(IAccountDao dao) 
    {
        this.dao = dao;
    }     

    public void Merge(Account first, Account second, MergeStrategy strategy) 
    {
        // merge logic goes here...
        // [...]
        dao.Save(first);
        dao.Save(second);
    }

    public void Merge(Account first, Account second) 
    {
        Merge(first, second, MergeStrategy.Default);
    }

    private readonly IAccountDao dao;
}

I want my tests to show that:

  1. Calling Merge(first, second, strategy) results in two accounts getting saved that have been merged using the supplied rule.

  2. Calling Merge(first, second) results in two accounts getting saved that have been merged using the default rule.

Note that both of these requirements are phrased in terms of inputs and effects - in particular, I don't care how the class achieves these results, as long as it does.

The fact that the second method happens to use the first isn't something I care about, or even that I want to enforce - if I do so, I'll write very brittle tests. (There's even an argument that if you've messed about with the object under test using a mocking framework, you're not even testing the original object any more, so what are you testing?) This is an internal dependency that could quite happily change without breaking the requirements:

    // ...
    // refactored AccountMerger methods
    // these still work, and still fulfil the two requirements above

    public void Merge(Account first, Account second, MergeStrategy strategy) 
    {
        MergeAndSave(first, second, strategy ?? MergeStrategy.Default);
    }

    public void Merge(Account first, Account second) 
    {
        // note this no longer calls the other Merge() method
        MergeAndSave(first, second, MergeStrategy.Default);
    }

    private void MergeAndSave(Account first, Account second, MergeStrategy strategy) 
    {
        // merge logic goes here...
        // [...]
        dao.Save(first);
        dao.Save(second);
    }

    // ...

As long as my tests only check inputs and effects, I can easily make this kind of refactoring - my tests will even help me to do so, as they make sure I haven't broken the class while making changes.

On the other hand, I do about the AccountMerger using the IAccountDao to save accounts following a merge (although the AccountMerger shouldn't care about the implementation of the DAO, only that it has a Save() method.) This DAO is a prime candidate for mocking - an external dependency of the AccountMerger class, feeling an effect I want to check for certain inputs.

like image 33
Dan Vinton Avatar answered Sep 27 '22 23:09

Dan Vinton


You shouldn't mock methods in the target class, you should only mock external dependencies.

If it seems to make sense to mock M2 while testing M1 it often means that your class is doing too many things. Consider splitting the class and keeping M2 in one class and move M1 to a higher level class, which would use the class containing M2. Then mocking M2 is easy, and your code will actually become better.

like image 37
Grzenio Avatar answered Sep 27 '22 23:09

Grzenio