Test Doubles in Testing: Difference Between Stub, Fake and Mock

This is another post within the group of posts I have about testing.

 

In previous posts we saw what it is and how to create mocks, but this is not our only option when we want to simulate functionalities or service responses. 

In this post we will see the different options and their details

 

 

1 - What are test doubles?

Honestly, I don't even know how to properly call what I'm going to explain in English. But basically, creating a double means writing code that acts in a particular way, which is different from the original implementation.

 

There are several ways to achieve the result we expect, and in this post, we will see them. 

test doubles

In our case, we have code that inserts into a database and this code is behind an interface called IArticulosRepository.

public interface IArticulosRepository
{
    int InsertarArticulo(string contenido, string titulo, int autorId);
    Articulo GetArticulo(int id);
}

 

 

2 - What is a mock?

The video we saw about mock went into a lot of detail, and I don't want to repeat myself. 

 

But to sum it up, it’s about adding configuration in the test so that certain methods act in a specific way. This configuration is set at the individual test level. Obviously, you can program it so that several tests have this configuration, but normally they are individual. In C#, we usually use the Moq library.

 

Here you can see an example, with the previous interface.

Mock<IArticulosRepository> articuloRepo = new Mock<IArticulosRepository>();
Mock<IAutorRepository> autorRepo = new Mock<IAutorRepository>();
autorRepo.Setup(a => a.AutorValido(It.IsAny<int>())).Returns(true);

articuloRepo.Setup(a => a.InsertarArticulo(contenido, titulo, autorId)).Returns(1);
articuloRepo.Setup(a => a.GetArticulo(1)).Returns(new Articulo()
{
    Autor = new Autor()
    {
        AutorId = autorId,
        Nombre = "test"
    },
    Contenido = contenido,
    Fecha = DateTime.UtcNow,
    Id = 1,
    Titulo = titulo
});

We have to define the response of each method, including the parameters. For example, if we know the code will query IDs 1 and 2, we need to mock GetArticulos for 1 and 2.

Note: it can be configured so that GetArticulos(It.IsAny<Int>).return(..) returns the same result for both, but that's not usually the norm. 

 

If you want more details about mocks, I recommend you take a look at my post about mocks in unit tests.

 

 

3 - What is a Stub in tests?

The second way we can create a double is by creating a stub.

Creating a stub means creating a class that always returns the same thing.

 

For example, in our case, we implement the interface in a class and always return the same information except for the id, which is the one we pass as a parameter to the method:

public class ArticulosStub : IArticulosRepository
{
    public int InsertarArticulo(string contenido, string titulo, int autorId)
    {
        return 1;
    }

    public Articulo GetArticulo(int id)
    {
        return new Articulo()
        {
            Autor = new Autor()
            {
                AutorId = 1,
                Nombre = "test"
            },
            Contenido = "contenido",
            Fecha = DateTime.UtcNow,
            Id = id,
            Titulo = "titulo"
        };
    }
}

This means, it doesn't matter if a particular id exists or not, the information from GetArticulos will always be "correct".

 

 

4 - What is a Fake in tests?

Finally, let's see what a fake is. A fake is simply a complete implementation of the interface, but it obviously only works within the tests. For example, in our case, when we call InsertarArticulo we can do it into a list and then when we run GetArticulo we read it from the list.

public class FakeArticulos : IArticulosRepository
{
    public List<Articulo> Articulos { get; set; }

    public int currentIdentifier { get; private set; }
    
    public FakeArticulos()
    {
        Articulos = new List<Articulo>();
        currentIdentifier = 0;
    }


    public int InsertarArticulo(string contenido, string titulo, int autorId)
    {
        Articulo articulo = new Articulo()
        {
            Autor = new Autor()
            {
                AutorId = autorId,
                Nombre = "test"
            },
            Contenido = contenido,
            Fecha = DateTime.UtcNow,
            Id = ++currentIdentifier,
            Titulo = titulo
        };

        Articulos.Add(articulo);
        
        return articulo.Id;
    }

    public Articulo GetArticulo(int id)
        => Articulos.FirstOrDefault(articulo => articulo.Id == id);

}

 

Creating fakes is not an easy task. Depending on the class you want to implement, it can be quite complex compared to creating a mock, for example.

 

 

5 - Mock vs Stub vs Fake, which one to choose?

It will depend on the situation, but personally, I recommend using mocks whenever possible. For example, to test a functionality or a use case, it is easier to reuse mocks when necessary than to implement everything with fakes, since in general it requires less maintenance. 

 

Honestly, I don't really use stubs in the real world, for the same reason as before, it is easier to see and understand the functionality of a test with a stub

 

In real-world scenarios, fakes are usually made when we have tests that affect others or as part of a system that allows us or facilitates us to create integration tests. For example, if we have a microservices system and for asynchronous communication, instead of making calls with a producer-consumer pattern we simulate it in memory, and we also simulate those generated events. 

 

So we should use whatever works best for each situation, but be careful with Fakes and duplicating functionalities, it can be a very deep rabbit hole.

 

This post was translated from Spanish. You can see the original one here.
If there is any problem you can add a comment bellow or contact me in the website's contact form

Uso del bloqueador de anuncios adblock

Hola!

Primero de todo bienvenido a la web de NetMentor donde podrás aprender programación en C# y .NET desde un nivel de principiante hasta más avanzado.


Yo entiendo que utilices un bloqueador de anuncios como AdBlock, Ublock o el propio navegador Brave. Pero te tengo que pedir por favor que desactives el bloqueador para esta web.


Intento personalmente no poner mucha publicidad, la justa para pagar el servidor y por supuesto que no sea intrusiva; Si pese a ello piensas que es intrusiva siempre me puedes escribir por privado o por Twitter a @NetMentorTW.


Si ya lo has desactivado, por favor recarga la página.


Un saludo y muchas gracias por tu colaboración

© copyright 2025 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café