Home > Back-end >  How to manually stub out a function in C# unit testing
How to manually stub out a function in C# unit testing

Time:05-31

I'm trying to create my own Mock function. I know Moq exists, but I really want to accomplish this.

I have no idea how I'd create a Mock function to stub out User#Foobar's calls to Product#Price in order to unit test User#Foobar:

using System;

public class MyUnitTest
{
    // CODE BEING TESTED
    class Product {
        public string Price() {
            return "I WANT TO STUB THIS OUT, DONT WANT TO SEE THIS STRING";
        }
    }
    class User {
        public string Foobar() {
            Product product = new Product();
            return $"Foobar, Product#price returns: {product.Price()}";
        }    
    }
    //////////////////////
    
    // CODE THAT DOES THE TEST
    public void Mock(string mockedClass, string mockedFunction, string mockedReturn)
    {
        // no idea what to put here something to do with an interface
    }
    
    public void RunTests()
    {
        Mock("Product","Price","MOCKED DATA");
        string expected = new User().Foobar();
        Assert(expected, "Foobar, Product#price returns: MOCKED DATA");
    }
        
    public void Assert(string actual, string expected){
        Console.WriteLine(actual == expected);
    }
    //////////////////////

    // How do I stub out Product to return "MOCKED DATA"?
}
                    
public class Program
{
    public static void Main(){
        new MyUnitTest().RunTests();
    }
}

I know how to do it in Ruby, but not in C#.

Here's a NET fiddle of the problem:

https://dotnetfiddle.net/8Re2jb

What should this Mock function do to intercept User#Foobar's call to Product#Price and return the string "MOCKED DATA"?

public void Mock(string mockedClass, string mockedFunction, string mockedReturn)
{
}

CodePudding user response:

You should approach this by using dependency injection. The User object shouldn't know the concrete type of the Product. Instead, you should encapsulate Product behind an IProduct interface and pass to the User constructor either an IProduct or some kind of IProduct factory.

For example:

public interface IProduct
{
    public string Price();
}

public interface IUser
{
    public string FooBar();
}

public class User: IUser
{
    public User(Func<IProduct> productFactory)
    {
        _productFactory = productFactory;
    }

    public string FooBar()
    {
        var product = _productFactory();
        return $"Foobar, Product#price returns: {product.Price()}";
    }

    readonly Func<IProduct> _productFactory;
}

Then you can create your own "mock" product (which we should really call a stub):

public class MockProduct : IProduct
{
    public MockProduct(string mockPrice)
    {
        _mockPrice = mockPrice;
    }

    public string Price()
    {
        return _mockPrice;
    }

    readonly string _mockPrice;
}

Then in your test you can create the User and pass to it a factory method to create a mock IProduct:

Func<IProduct> mockProductFactory = () => new MockProduct("MOCKED DATA");

User user = new User(mockProductFactory);

// Test user now.

Although this is really a form of stubbing (a term you already used) rather than mocking - mocking is generally done with a mocking framework and uses reflection to avoid having to manually implement all the methods in an interface.

  • Related