This is the sixth day of my participation in the August More text Challenge. For details, see:August is more challenging

  • 📢 welcome to like: 👍 collect ⭐ message 📝 If there is any mistake please correct it, send roses, hand left fragrance!
  • 📢 This article was originally written by Webmote and originally published by CSDN.
  • 📢 author’s motto: life lies in tossing about, when you do not toss about life, life will start tossing you, let us work together! 💪 💪 💪

Preface 🎏

There are several mysterious concepts in unit testing. They are mocks. A Stub B. They sound similar and can be easily confused. Let’s uncover their mystery and explore their deep path through this article.

🎏 1. What is Fake?

Fake object, popular will be fake!

Is used instead of a fake implementation with “smart” objects. Usually a quick implementation, making it useful for different unit tests, but not for integration tests.

By far the most common example I’ve seen is in the data warehousing layer. Suppose I have a standard SQL Server repository that looks like this:

public interface IUserRepository
{
    void Insert(object user);
    List<object> GetAllUsers();
}

public class UserRepository : IUserRepository
{
    public List<object> GetAllUsers()
    {
        // Fetch the user set from the database.
    }

    public void Insert(object user)
    {
        // Insert the user into the database}}Copy the code

When it comes to the actual implementation part, it might include logic and methods to call the database.

We run into some problems when it comes to unit testing classes that might use IUserRepository, such as UserService. Because we don’t want our unit tests touching the database, frankly, we don’t really care about the Implementation of UserRepository.

So we create a pseudo object, instead of using the real object that was implemented directly:

public class FakeUserRepository : IUserRepository
{
    private List<object> _users = new List<object> ();public List<object> GetAllUsers()
    {
        return _users;
    }

    public void Insert(object user){ _users.Add(user); }}Copy the code

In our FAKE, we actually take the inserted user and add it to an internal list. When GetAllUsers is called, we return the same list. Now, whenever a unit test needs to call IUserRepository, we can supplement it in FakeUserRepository and “work” immediately.

The main thing here is to achieve business similarity!

This is a “real” implementation that actually acts like a repository, except that there is no actual database behind the scenes.

🎏 2. What is a Stub?

A stub is a returnHard-coded responseThe implementation of the.

Stubs don’t have any “intelligence.” Instead of bundling calls on an object, each method only returns a predefined, fixed response.

Let’s see how to create a stub for the above:

public class StubOneUserRepository : IUserRepository
{
    public List<object> GetAllUsers()
    {
        return new List<object> (); }public void Insert(object user)
    {
        // Do nothing}}Copy the code

It looks a bit like our dummy object, but… Not really.

This insertion does not affect GetAllUsers, which itself returns a default response without any content. Nothing I do with this object during testing changes its functionality.

Stubs are used to satisfy conditions inside the code, not to test functionality.

If my code calls “insert” on the repository, but I don’t really care what happens to the data for my particular test, then the stub makes sense, saving me the work of writing pseudo-object “smart” business.

The repository example is a bit odd, because the repository should always return dynamic data to test for various conditions in the code. So let me use another example where stubs are more likely to be needed in the real world.

Suppose you have an interface that tells users whether they are “authenticated.” It looks like this:

public interface IUserAuthenticatedCheck
{
    bool IsUserAuthenticated();
}
Copy the code

Now for our tests, the user always needs to be authenticated, perhaps to satisfy some basic framework conditions. The root can be defined like this:

public class StubUserAuthenticatedCheckTrue : IUserAuthenticatedCheck
{
  // Return verified ~~~
    public bool IsUserAuthenticated()= >true;
}
Copy the code

There is no smart algorithm for whether or not the user should be authenticated, no other value, just a straightforward “always return true” method.

Fixed, that’s what the stub is good at.

🎏 3. What is a Mock?

Simulation is aDefault object, dynamic response/behavior can be defined as part of the test and predefined.

They do not need to be specifically implemented or instantiated, and there is (generally) no need to share behavior between tests.

Where will we Mock? Is anywhere you want to be relatively dynamic, satisfying the conditions for a particular test.

Suppose I am writing a test that calls the following interface:

public interface IShopService
{
    bool CheckShopIsOpen(int shopId);
}
Copy the code

All we do is check whether the store is open or closed. The actual implementation class might call a database or some WebService/API, but we don’t want to make that part of the unit test.

If we use Fake objects here, we need to add some virtual methods to determine whether the store should be open or closed. Maybe it goes something like this:

public class FakeShopService : IShopService
{
    public bool ShouldShopBeOpen { get; set; }

    public bool CheckShopIsOpen(int shopId)
    {
        returnShouldShopBeOpen; }}Copy the code

Well, it seems complicated, but in order to be able to control whether a store is open or closed, we need to add new methods.

If you use Stub stubs, you must hard-code true/false responses into concrete classes. It might go something like this:

public class StubShopService : IShopService
{
    private Dictionary<int.bool> _shops = new Dictionary<int.bool> {{1.true },
        { 2.false}};public bool CheckShopIsOpen(int shopId)
    {
        return_shops[shopId]; }}Copy the code

This applies to predefined lists of ids and whether the store is open or closed.

But if you use it in a test and pass in an ID of 1, it’s not immediately clear from the test why you got a true response, so maybe you need to go back and look at your hardcode?

So how do you use mock objects to solve this problem? (Of course, you are advised to use the Moq library directly!) :

var _mockShopService = new Mock<IShopService>();
_mockShopService.Setup(x => x.CheckShopIsOpen(1)).Returns(true);
Copy the code

Right in the test code, when using the mock object with ID 1 CheckShopIsOpen, it is very clear that it returns true.

It’s also specific to the test, and doesn’t force us to hard-code anything anywhere, or create concrete classes.

When we have a test that requires store ID 1 to be false..

_mockShopService.Setup(x => x.CheckShopIsOpen(1)).Returns(false);
Copy the code

So easy!

🎏 4. When to Mock, Fake, and Stub

Everything is not absolute, you know!

  • Use Fake when you want a concrete implementation that can be reused, which is similar to the real implementation but is reusable across tests (for example, an in-memory database)
  • Stub Stub is used when you want to reuse hard-coded responses/implementations in tests
  • Use a Mock when you need to respond dynamically to a single test

However, the average test might not be as strict as this, and for the most part, I just Mock! Not to fight ~~~~

Well, hopefully you have a better understanding of what these test objects are for, and a better idea of when to use each one.

Finally, good luck!

🎏 05. Summary

Routine summary, rational view.

👓 all see this, still care to point a like?

👓 all points like, still care about a collection?

Do you care about a comment when you have collected 👓?