简体   繁体   English

如何使用 moq 对存储库和模拟数据库进行单元测试

[英]How to unit test a repository and mock db with moq

I am onboarding alone on an existing project that do not have any unit test.我独自在一个没有任何单元测试的现有项目上入职。 My first goal before any refactoring is to cover 100% of the code.在进行任何重构之前,我的第一个目标是覆盖 100% 的代码。 I would like to avoid any regression.我想避免任何回归。

I have read how do I mock sqlconnection or should I refactor the code?我已经阅读了如何模拟 sqlconnection 还是应该重构代码? but my case as you can see below is quite different cause I need to do more than stub simply the sqlConnection.但正如您在下面看到的那样,我的情况完全不同,因为我需要做的不仅仅是 sqlConnection 存根。 Basically, I need to mock the db.基本上,我需要模拟数据库。 I would like to know the best approach to achieve it.我想知道实现它的最佳方法。

(By the way, I do not want to use any ORM such as Entity). (顺便说一句,我不想使用任何 ORM 之类的实体)。 Below the code of the repository:在存储库的代码下方:

public class HotelRepository : IHotelRepository
{
    private readonly IDbConnection _dbConnection;
    private readonly ILogService _loggerService;

    public HotelRepository(IDbConnection dbConnection, ILogService loggerService)
    {
        _dbConnection = dbConnection;
        _loggerService = loggerService;
    }

    public HotelDo GetByRid(string rid)
    {
        return Find(rid).FirstOrDefault();
    }

    public List<HotelDo> Find(string text)
    {
        try
        {
            _dbConnection.Open();

            var items = new List<HotelDo>();
            using (var command = _dbConnection.CreateCommand())
            {
                command.CommandType = CommandType.StoredProcedure;
                command.CommandText = "dbo.HotelSearchByRidOrName";

                command.Parameters.Add(new SqlParameter("@Text", text));

                using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    while (reader.Read())
                    {
                        items.Add(new HotelDo()
                        {
                            Name = SqlExtension.ReaderToStringConverter(reader["Name"]),
                            Id = SqlExtension.ReaderToIntConverter(reader["Id"]),
                            Rid = SqlExtension.ReaderToStringConverter(reader["RIDHotel"]),
                            IdPms = SqlExtension.ReaderToNullableIntConverter(reader["IdPms"]),
                            LinkResaWeb = SqlExtension.ReaderToStringConverter(reader["LinkResaWeb"]),
                            LinkPms = SqlExtension.ReaderToStringConverter(reader["LinkPms"]),
                            IdBrand = SqlExtension.ReaderToNullableIntConverter(reader["IdBrand"]) ?? 0,
                            IsOnline = SqlExtension.ReaderToBoolConverter(reader["IsOnline"]) ?? false,
                            CodeCountry = SqlExtension.ReaderToStringConverter(reader["CodeCountry"])
                        });
                    }
                }
            }
            return items;
        }
        catch (Exception e)
        {
            var errorMessage = $"HotelRepository Find, text {text} ";
            _loggerService.Trace(LogSeverity.Error, errorMessage, e);

            throw new DalException() { Source = errorMessage, };
        }
        finally
        {
            _dbConnection.Close();
        }
    }

    public List<HotelDo> GetAll()
    {
        try
        {
            _dbConnection.Open();

            var items = new List<HotelDo>();
            using (var command = _dbConnection.CreateCommand())
            {
                command.CommandType = CommandType.StoredProcedure;
                command.CommandText = "dbo.HotelGetAll";

                using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    while (reader.Read())
                    {
                        bool.TryParse(reader["IsOnline"].ToString(), out var isOnline);

                        items.Add(new HotelDo()
                        {
                            Id = SqlExtension.ReaderToIntConverter(reader["Id"]),
                            Rid = SqlExtension.ReaderToStringConverter(reader["RIDHotel"]),
                            Name = SqlExtension.ReaderToStringConverter(reader["Name"]),
                            CodeCountry = SqlExtension.ReaderToStringConverter(reader["CodeCountry"]),
                            LinkPms = SqlExtension.ReaderToStringConverter(reader["LinkPms"]),
                            IdPms = SqlExtension.ReaderToNullableIntConverter(reader["IdPms"]),
                            IdBrand = SqlExtension.ReaderToNullableIntConverter(reader["IdBrand"]) ?? 0,
                            LinkResaWeb = SqlExtension.ReaderToStringConverter(reader["LinkResaWeb"]),
                            IsOnline = isOnline
                        });
                    }
                }
            }
            return items;
        }
        catch (Exception e)
        {
            var errorMessage = $"HotelRepository GetAllHotels";
            _loggerService.Trace(LogSeverity.Error, errorMessage, e);

            throw new DalException() { Source = errorMessage, };
        }
        finally
        {
            _dbConnection.Close();
        }
    }
}

Thank you for your help谢谢您的帮助

So based on what you've got I've set up a test frame for you to follow.因此,根据您所拥有的,我设置了一个测试框架供您遵循。 I've removed less important components for breviety.为了简洁起见,我删除了不太重要的组件。

Before you you jump in I just want to give my 2 cents, if you don't know how to do this sort of thing you seem to be more or less in a junior position or less experiance with C# and also found your self in a more or less mature company that doesn't care about the development deparment, since an uncovered project just get's thrown your way says you don't have alot of resources to go about to imrpove the code base.在你加入之前,我只想给我 2 美分,如果你不知道如何做这种事情,你似乎或多或少是初级 position 或更少的 C# 经验,并且还发现你自己在或多或少不关心开发部门的成熟公司,因为一个未发现的项目刚刚被抛出,说你没有很多资源来 go 来改进代码库。

  1. Test only the things that have business value (can you put a price on the piece of logic if it brakes)只测试具有商业价值的东西(如果它刹车的话,你能不能给它定价)
  2. Delivering stuff faster will make you look better as a programmer (noone in the business gives a damn about test covarage)更快地交付东西会让你作为一名程序员看起来更好(业务中没有人对测试协方差感兴趣)
  3. Study hard, get your experiance, good reputation and don't be afraid to get the hell out of there as soon as you start getting bored.努力学习,获得你的经验,良好的声誉,一旦你开始感到无聊,不要害怕离开那里。

Main主要的

void Main()
{
    var idIndex = 0;
    var ids = new string[] { "1", "2" };

    var mockDataReader = new Mock<IDataReader>();
    mockDataReader.SetupSequence(x => x.Read()).Returns(true).Returns(true).Returns(false);
    mockDataReader.SetupGet(x => x["Id"]).Returns(() => ids[idIndex]).Callback(() => idIndex++);

    var mockParameters = new Mock<IDataParameterCollection>();

    var mockCommand = new Mock<IDbCommand>();
    mockCommand.SetupGet(x => x.Parameters).Returns(mockParameters.Object);
    mockCommand.Setup(x => x.ExecuteReader(CommandBehavior.CloseConnection)).Returns(mockDataReader.Object);

    var mockConnection = new Mock<IDbConnection>();
    mockConnection.Setup(x => x.CreateCommand()).Returns(mockCommand.Object);

    var repo = new HotelRepository(mockConnection.Object);

    var result = repo.Find("search");

    Assert.Equal("1", result[0].Id);
    Assert.Equal("2", result[1].Id);
}

Repository存储库

public class HotelRepository
{
    private readonly IDbConnection _dbConnection;

    public HotelRepository(IDbConnection dbConnection)
    {
        _dbConnection = dbConnection;
    }

    public List<Pony> Find(string text)
    {
        _dbConnection.Open();

        var items = new List<Pony>();
        using (var command = _dbConnection.CreateCommand())
        {
            command.CommandType = CommandType.StoredProcedure;
            command.CommandText = "dbo.HotelSearchByRidOrName";

            command.Parameters.Add(new SqlParameter("@Text", text));

            using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
            {
                while (reader.Read())
                {
                    items.Add(new Pony
                    {
                        Id = reader["Id"].ToString()
                    });
                }
            }
        }
        return items;
    }
}

Just a dumb o'l pony只是一匹愚蠢的小马

public class Pony {
    public string Id { get; set; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM