Mock Setup It.any for any expression return fixture value

Viewed 53

I would like to do a Mock Test and define as below :

public interface INode {
   public int Id { get; set; }
   public string NodeName { get; set; }
   public IEnumerable<INode> Childrens { get; set; }
}
    
public interface INodeQuery {
   public IEnumerable<INode> GetNodes();
}
public class NodeProcessService {
   readonly INodeQuery nodeQuery;
    
   public NodeProcessService(INodeQuery nodeQuery) {
       this.nodeQuery = nodeQuery;
   }
   public string currentNodeName { get;set;}

        
   public int Handler() {
       var getNode = nodeQuery.GetNodes()
                .Select(p => p.Childrens.FirstOrDefault(c => c.NodeName == currentNodeName))
                .FirstOrDefault(p => p != null);
    //do something...
   }
}

In the test, I want to do the Mock nodeQuery which any expression will be return fixure value, such like this:

public NodeServiceTests()
{
   fixture.Customize(new AutoMoqCustomization());
   nodeQuery = fixture.Freeze<Mock<INodeQuery>>();
   sut = fixture.Create<NodeProcessService>();
}

[Fact]
public void SimpleTest()
{
    var fakeNode = fixture.Create<INode>();
    nodeQuery.Setup(s => s.GetNodes().FirstOrDefault(It.IsAny<Func<INode, bool>>())).Returns(fakeNode);
    
    var result = sut.Handler();
}

but I've got error as below code :

    nodeQuery.Setup(s => s.GetNodes().FirstOrDefault(It.IsAny<Func<INode, bool>>())).Returns(fakeNode)
or 

nodeQuery.Setup(s => s.GetNodes().FirstOrDefault()).Returns(fakeNode)

Can you advice how to do it , which I want to mock nodeQuery.GetNodes() whatever any expression func return a single fixture value.

Thank you for all advise,

2 Answers

You wish to escape or ignore parameters passed to Select and FirstOrDefault method. That wouldn't be possible until you mock the behavior of Select and FirstOrDefault method of IEnumerable<INode>.

It is easier to setup the mock data which can give you desired result even after execution of the real Select and FirstOrDefault methods.

Following is the mock data I came up with which you can set as a result of GetNodes method.

[Fact]
public void SimpleTest()
{
    var node = new Node { Id = 1, NodeName = "parent" };

    var child = new Node  {Id =11, NodeName="main" };
    node.Childrens = new List<INode>{child};

    var nodes = new List<INode> {node};

    nodeQuery.Setup(s => s.GetNodes()).Returns(nodes);

    var result = sut.Handler();
}

Edit : Based on your feedback about currentNodeName not being static in FirstOrDefault method, I suggest following test approach.

You should not try to fake or setup LINQ methods. They are extension methods. They do not belong to any interface or class. So faking them or setting up their bhehavior will not work at all.

Also you are testing the code of NodeProcessService class. You should not fake any part of it's code. Only dependency behavior should be faked. If you fake the NodeProcessService code, that means you are not actually unit testing real code and its behavior but you are testing the fake code.

You have currentNodeName property in NodeProcessService class. And value of this property is used in FirstOrDefault criteria of p.Childrens.

So when you run the test, you should set currentNodeName property value and use the same value in creating fake nodes too. This will make sure that the logic of Handler method is executed as exactly it is written.

// this test is to call Handler method and it should return the 
// children nodes whose name is "main".
[Fact]
public void TestChildNodesReturnWhenPresent()
{
    var currentNodeName = "main"; //this can be any thing you want.

    sut.currentNodeName = currentNodeName;

    var node = new Node { Id = 1, NodeName = "parent" };

    var child = new Node  {Id =11, NodeName= currentNodeName };
    node.Childrens = new List<INode>{child};

    var nodes = new List<INode> {node};

    nodeQuery.Setup(s => s.GetNodes()).Returns(nodes);

    var result = sut.Handler();

   Assert.IsNotNull(result);
}

// this test is to call Handler method and it should return null 
// when children not with specified name does not exist.
[Fact]
public void TestNullReturnedWhenChildreNodeNotPresent()
{
    var currentNodeName = "main"; //this can be any thing you want.

    sut.currentNodeName = currentNodeName;

    var node = new Node { Id = 1, NodeName = "parent" };

    var child = new Node  {Id =11, NodeName= "childNode" };
    node.Childrens = new List<INode>{child};

    var nodes = new List<INode> {node};

    nodeQuery.Setup(s => s.GetNodes()).Returns(nodes);

    var result = sut.Handler();

   Assert.IsNull(result);
}

As Chetan wrote in the comments, you should mock only GetNodes():

nodeQuery.Setup(s => s.GetNodes()).Returns(new[] { fakeNode });

The mock will return a collection consisting of only the fakeNode.

Related