Home > Mobile >  Mock Method call from another Method using Moq
Mock Method call from another Method using Moq

Time:10-25

I am trying to create Unit test for below two methods using MsTest. I am fairly new to this, and so far have referred to different posts on the Topic.

Code Requirement

  1. Create a Timer Based Function (Azure)
  2. Execute Method 1 and Method 2 in the order to get the Output.

Test Requirement

  1. Ability to be able to create Unit test cases for each Class/Method with no external dependency (Fake/Mock)
  2. To Fit this Code under Test can be update as code is not yet Live.
  3. Open to other tools/Nugets beyond Moq to support the Test requirement.

When I try to run the Unit test, it does not mock Method 2 instead executes it. I need help in debugging the code.

 public class Job: IJob
 {
    //Method 1
    public List<TableEntity> GetJob()
    {
        var a = Get<T>("static value"); //Mock this to Test Method GetJob
        return a.Result;
    }

   //Method 2
    public async Task<List<T>> Get<T>(string tableName) where T : ITableEntity, new()
    {
        var t = new List<T>();
        //add data to T
        return t;

    }
}

Interface

public interface IJob
{
    List<TableEntity> GetJob();

    Task<List<T>> Get<T>(string tableName) where T : ITableEntity, new();

}

Test Code

    private readonly Mock<IJob> _mockIJob = new Mock<IJob>();
    readonly Job _job = new Job();

    public void NotThrow_Error_When_JobFound()
    {
        //Arrange
        var jobs = new J.TableEntity()
        {
            FolderName = "FolderName",
            Timestamp = DateTimeOffset.Now
        };

        var jobList = Task.FromResult(new List<TableEntity>() { jobs });
        
        _mockIJob.Setup(c => c.Get<TableEntity>(""))
                .Returns(jobList);

        //Act
        var actualResult = _job.GetJob();

        //Assert
        Assert.AreEqual(jobList, actualResult);

    }

CodePudding user response:

It will not be possible the way you approach it. What you are attempting is to execute a test on an instance of a concrete class and, at the same time, to mock the instance.

I am not going to deliberate on whether GetJob method should exist in the current class or be moved elsewhere. For the solution in the current form the simplest way to achieve testability (and perhaps reduce duplication in the future) would be by declaring GetJob as extension method on IJob interface:

public static class IJobExtensions
{
    public static List<TableEntity> GetJob(this IJob job)
    {
        var a = job.Get<T>("static value"); //Mock this to Test Method GetJob
        return a.Result;
    }
}

The unit test would then look like this:

private readonly Mock<IJob> _mockIJob = new Mock<IJob>();

public void NotThrow_Error_When_JobFound()
{
    //Arrange
    var jobs = new J.TableEntity()
    {
        FolderName = "FolderName",
        Timestamp = DateTimeOffset.Now
    };

    var jobList = Task.FromResult(new List<TableEntity>() { jobs });
    
    _mockIJob.Setup(c => c.Get<TableEntity>(""))
            .Returns(jobList);

    //Act
    var actualResult = _mockIJob.Object.GetJob();

    //Assert
    Assert.AreEqual(jobList, actualResult);
}

CodePudding user response:

If I understand your problem correctly then you are looking for partial mocking. You want to use GetJob as it is but you want to mock Get<T>.

Prerequisite

The Get<T> should be marked as virtual to be able to mock it.

Test

//Arrange
...
var jobPartialMock = new Mock<Job>();
jobPartialMock.CallBase = true; //Use GetJob as it is
jobPartialMock
   .Setup(job => job.Get<TableEntity>(It.IsAny<string>()))
   .ReturnsAsync(new List<TableEntity>() { jobs }); // Mock `Get<T>`

//Act
var actual = jobPartialMock.Object.GetJob();

//Assert
...

CodePudding user response:

I think when simplifying the code some things got lost in translation (e.g, var a = Get<T>("static value"); no idea what T refers here) but with small modifications I think you would be able to achieve what you are trying.

  1. In order for the mock to be used you need the mocked object instance so you will need to remove reference to concrete instance:
    private readonly Mock<IJob> _mockIJob = new Mock<IJob>();
    // readonly Job _job = new Job(); // This should not be used.
  1. In order to use a concrete method1 with the mocked method2, you will need to mock the Job concrete class.
private readonly Mock<Job> _mockJob = new Mock<Job>();
  1. In order for the proxy class to "take over" a method it should be declared as virtual:
//Method 2
public virtual List<T> Get<T>(string tableName) where T : TableEntity, new()
{
    var t = new List<T>();
    //add data to T
    return t;

}
  1. While mocking the method you should use the It.IsAny<string>() specification otherwise your test method is using a mock with "" parameter whereas the method1 is using "static value" parameter, so it will not match the proxy method.

so putting this all together:

private readonly Mock<Job> _mockJob = new Mock<Job>();

[Fact]
public void NotThrow_Error_When_JobFound()
{
    //Arrange
    var jobs = new TableEntity()
    {
        FolderName = "FolderName",
        Timestamp = DateTimeOffset.Now
    };

    var jobList = new List<TableEntity>() { jobs };

    _mockJob.Setup(c => 
            c.Get<TableEntity>(It.IsAny<string>()))
        .Returns(() =>
        {
            // easier to visualize the breakpoint.
            return jobList;
        });

    //Act
    var actualResult = _mockJob.Object.GetJob();

    //Assert
    Assert.Equal(jobList, actualResult);
}

Hope it helps.

  • Related