简体   繁体   中英

How to mock a class that has parameters in the constructor using Mock.Of<> syntax?

This question has showed me how to mock a class that has parameters in the constructor. Here's a nice block post on the Mock.Of<> , but it doesn't show how to mock the constructor using function syntax.

public class MyClass
{
    public MyClass(IDependency1 dep1, IDependency2 dep2, IDependency3 dep3)
    {}

    public ReturnType MyNewMethod(Tyep1 t1, Type2 t2)
    {
       //1. call to ExistingMethod1();
       //2. call to ExistingMethod2();
       //3. call using the DbContext
       //4. call using the Logger
    }
}

I get something like this based of the first blog post.

var dep1 = new Mock<IDependency1>(); 
var dep2 = new Mock<IDependency2>();
var dep3 = new Mock<IDependency3>();

object[] arrParams = { dep1.Object, dep2.Object, dep3.Object }
var sut = new Mock<MyClass>(arrParams);

So how to mock a class that has parameters in the constructor using Mock.Of<> syntax?

EDIT

the new method will not only call existing methods , but also access the DbContext , the logger , and maybe other services. So, I need to mock everything but the method I am testing.

public class MyClass
{
    public MyClass(MyDbContext context, ISecurityService secService, ILogger logger)
    {}

    public ReturnType1 ExistingMethod1(Type1 t1){}

    public ReturnType2 ExistingMethod2(Type t){}

    public MyEntity MyNewMethod(Tyep1 t1, Type2 t2)
    {
       //1. call to ExistingMethod1(); --> I'll just setup the return value
       //2. call to ExistingMethod2(); --> I'll just setup the return value
       //3. call using the DbContext   --> ???
       //4. call using the Logger      --> ???

       var x = ExistingMethod1(t1);                //1.
       var y = ExistingMethod1(x);                 //2.

       var result context.MyEntities.              //3.
              .Where(e => e.id == y.MyEntityId)
              .ToList();

       return result;
    }
}

Mocking with Moq need the class to be mocked to have methods to be virtual or you can mock any interface. When you are mocking with moq, it will create a dynamic implementation on the fly and thus it does not depend on your implementation. In your case you can just do

public class MyClass
{
    public MyClass(IDependency1 dep1, IDependency2 dep2, IDependency3 dep3)
    {}

    public ReturnType MyNewMethod(Tyep1 t1, Type2 t2)
    {
       //1. call to ExistingMethod1(); --> I'll just setup the return value
       //2. call to ExistingMethod2(); --> I'll just setup the return value
       //3. call using the DbContext   --> ???
       //4. call using the Logger      --> ???
    }
}

    Mock<MyClass> mockedObj = new Mock<MyClass>();

    mockedObj.SetUp(x=>x.MyNewMethod()).Returns(objectOfReturnType);

Here you need to make MyNewMethod virtual. The return objectOfReturnType is an object you created as test object. so your method body detail is not required or needed. That is the idea of mocking, you are mocking your actual implementation with fake implementation ( which is the setup in this case). You can vary different return objects depending how you will test the class under test. I recommend you first to read unit testing 101.

Note that you are setting up how MyNewMethod behave. Your implementation might be doing lots of stuff but here what you care is its return. That is why the method also has to be virtual, it will be overriden by the Moq and return what you just setup. Internally that method might call different things...so you don't care

Also you should read the basic of Moq, you can find it here https://github.com/Moq/moq4/wiki/Quickstart

If you want to test MyClass you might want to mock the dependencies instead:

// Arrange
var mockDep1 = new Mock<IDependency1>();
var mockDep2 = new Mock<IDependency2>();
var mockDep3 = new Mock<IDependency3>();

var myTestInstance = new MyClass(mockDep1.Object, mockDep2.Object, mockDep3.Object);

// Act
var result = myTestInstance.DoSomething();

// Assert
Assert.AreEqual(result, myExpectedResult); // check the direct result
mockDep1.Verify(mock => mock.SomeMethodOnMock(It.IsAny<string>()), Times.Once); // SomeMethodOnMock was called once
// ... etc

On the other hand, if MyClass is the dependency you want to mock for another test object , then the best way to extract an interface from it (eg. IMyClass ) so you can test your class in a much cleaner way.

Moq allows you to mock non-sealed classes but it is rather a fail-safe if there is no other way as many irrelevant code may be executed during the test and you can setup/verify non-sealed virtual or abstract members only.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM