Click here to Skip to main content
15,886,724 members
Articles / Programming Languages / C#

Mocking Expressions

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
8 Aug 2020CPOL3 min read 8.3K   2  
Mocking/stubbing lambda expressions to have a bit more control over our unit tests
Here we look at: What is an Expression and how to use it. Then we look at some examples of expressions like Simple, Reusable, and Advanced. We also look at a mocking scenario where I tell the stubbing framework to expect any kind of expression. Then finally we look at the solution, where I show how we can write more specific unit tests that are more resilient to changes or false negatives.

For this post, please keep in mind that I’m by no means versed in expression trees so I will try my best to explain.

What Is an Expression and How to Use It

Going by the explanation defined here, an Expression<T> is a strongly typed lambda expression which treats the lambda expression as structured data instead of just being a pointer to a function.

The reason one would use an Expression is so that Linq can be manipulated, read, or constructed at runtime. Such features are usually used in database providers to translate normal LINQ expressions into database queries, for example in Linq-to-SQL.

Examples

Please note that these examples are from the book “C# 8.0 in a Nutshell”, from which the samples can be found in LinqPad.

Simple Expression

The simple example is as follows:

C#
Expression<Func<int>> return5 = () => 5;
Func<int> compiled = return5.Compile();
Console.WriteLine(compiled());

This snippet compiles a declared function and executes it.

Reusable Expression

C#
Expression<Func<string, string, bool>> expression = (x, y) => x.StartsWith(y);

var compiled = expression.Compile();

Console.WriteLine(compiled("First", "Second"));
Console.WriteLine(compiled("First", "Fir"));

In this example, an expression is compiled and executed twice.

Advanced Expression

This example builds an expression by hand and then compiles it for its use.

C#
ParameterExpression param = Expression.Parameter(typeof(int), "i");
Expression<Func<int, bool>> isOdd =
    Expression.Lambda<Func<int, bool>>(
    Expression.Equal(
      Expression.And(
          param,
          Expression.Constant(1, typeof(int))),
      Expression.Constant(1, typeof(int))),
    new ParameterExpression[] { param });

Func<int, bool> isOddCompiledExpression = isOdd.Compile(); 
// this is the equivalent of the expression "i => (i & 1) == 1"

Console.WriteLine("With a compiled expression tree:");
for (int i = 0; i < 10; i++)
{
  if (isOddCompiledExpression(i))
      Console.WriteLine(i + " is odd");
  else
      Console.WriteLine(i + " is even");
}

In this example, the expression gets built programmatically to determine if a number is odd or even. Even though it’s quite verbose and occupies a big chunk of code just to create a simple expression, the power here comes from the fact that we can break up that expression into smaller parts and build an expression dynamically.

The Mocking Scenario

I’ve created an interface over a repository so that I can swap out implementations, and I also wanted the interface to be open so that I can dynamically create a repository for any data type I would need.

The interface is just a standard generic repository interface that will receive the T type via dependency injection. In my case, I use stashbox (maybe I’ll write a post about stashbox, but I’ve been putting it off due to the sheer amount of functionality, and the fact that it’s already pretty well documented).

C#
public interface IStorageRepository<T> where T: class
{
    IEnumerable<T> GetAll();
    IEnumerable<T> GetAllWithInclude(params Expression<Func<T, object>>[] includes);
    IEnumerable<T> Find(Expression<Func<T, bool>> expression, 
                        int skip = 0, int limit = int.MaxValue);
    Task UsertAsync(T user);
    T FindOne(Expression<Func<T, bool>> expression);
    Task DeleteAsync(Expression<Func<T, bool>> predicate);
}

Because the implementation of this interface is meant to be injected, it also means it can be mocked/stubbed for unit tests. One solution for this (before I found the solution that follows) was to tell the stubbing framework to expect any kind of expression, in my case I use NSubstitute in combination with stashbox but for this example, I’m only going to use NSubstitute. Here’s an example of it:

C#
public class Tests
{
    [Test]
    public void UsingAnyArgs()
    {
        IStorageRepository<TestClass> testClassRepositorySub = 
            Substitute.For<IStorageRepository<TestClass>>();

        TestClass testClass = new TestClass
        {
            Id = 1
        };

        testClassRepositorySub
            .Configure()
            .FindOne(Arg.Any<Expression<Func<TestClass, bool>>>())
            .Returns(testClass);

        TestClass expectedTestClass = testClassRepositorySub.FindOne
                                      (class1 => class1.Id == testClass.Id);

        Assert.That(testClass.Id, Is.EqualTo(expectedTestClass.Id));
    }

    public class TestClass
    {
        public int Id { get; set; }
    }
}

As we can see, this works for simple scenarios where we don’t care what the expression is but we want to retrieve a specific instance for our testing purposes.

The problem arises when we want to run the query multiple times with different expressions like a different Id.

The Solution

While having encountered that very scenario in which I needed to run multiple queries with different results, I stumbled upon this answer on stackoverflow which helped me find out about this project and its associated nuget package.

The new approach with the addition of multiple queries is as follows:

C#
[Test]
public void UsingSpecificExpressions()
{
    IStorageRepository<TestClass> testClassRepositorySub =
        Substitute.For<IStorageRepository<TestClass>>();

    TestClass testClass1 = new TestClass
    {
        Id = 1
    };

    TestClass testClass2 = new TestClass
    {
        Id = 1
    };

    testClassRepositorySub
        .Configure()
        .FindOne(Arg.Is<Expression<Func<TestClass, bool>>>(expression =>
            Lambda.Eq(expression, tc => tc.Id == testClass1.Id)))
        .Returns(testClass1);
    testClassRepositorySub
        .Configure()
        .FindOne(Arg.Is<Expression<Func<TestClass, bool>>>(expression =>
            Lambda.Eq(expression, tc => tc.Id == testClass2.Id)))
        .Returns(testClass2);

    TestClass expectedTestClass1 = testClassRepositorySub.FindOne
                                   (class1 => class1.Id == testClass1.Id);
    TestClass expectedTestClass2 = testClassRepositorySub.FindOne
                                   (class2 => class2.Id == testClass2.Id);

    Assert.That(testClass1.Id, Is.EqualTo(expectedTestClass1.Id));
    Assert.That(testClass2.Id, Is.EqualTo(expectedTestClass2.Id));
}

As we can see, there is more to write and I’m sure there are even more ways to clean it up but it gets the job done.

There’s a bonus, some of you might have noticed the IEnumerable<T> GetAllWithInclude(params Expression<Func<T, object>>[] includes) method on the interface, well, since params translate into an array, we can use it as follows:

C#
[Test]
public void UsingSpecificExpressionsFromArrays()
{
    IStorageRepository<TestClass> testClassRepositorySub =
        Substitute.For<IStorageRepository<TestClass>>();

    TestClass testClass = new TestClass
    {
        Id = 1
    };

    testClassRepositorySub
        .Configure()
        .GetAllWithInclude(Arg.Is<Expression<Func<TestClass, object>>[]>(expression =>
            Lambda.Eq(expression[0], tc => tc.InnerTestClass)))
        .Returns(new[] {testClass});

    TestClass expectedTestClass = testClassRepositorySub.GetAllWithInclude
                                  (class1 => class1.InnerTestClass).Single();

    Assert.That(testClass.Id, Is.EqualTo(expectedTestClass.Id));
}

public class TestClass
{
    public int Id { get; set; }
    public InnerTestClass InnerTestClass { get; set; }
}

Now we can write more specific unit tests that are more resilient to changes or false negatives.

Conclusion

Even though now I have to rewrite my unit tests that use Args.Any(), at least now I have more control over what is being tested. I would have liked for the test setup to be a little less verbose, and I’m sure some of you would have better implementations, but at least it is a start.

I hope you enjoyed this winding path of frameworks and problem solving and that it helps you as it did me.

Until next time, happy coding!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Romania Romania
When asked, I always see myself as a .Net Developer because of my affinity for the Microsoft platform, though I do pride myself by constantly learning new languages, paradigms, methodologies, and topics. I try to learn as much as I can from a wide breadth of topics from automation to mobile platforms, from gaming technologies to application security.

If there is one thing I wish to impart, that that is this "Always respect your craft, your tests and your QA"

Comments and Discussions

 
-- There are no messages in this forum --