如何在单元测试中使用 Redis



>假设我的控制器类中有一个方法,用于更新 Redis 数据库中键、值对的分数。我想写一个单元测试来检查分数是否不为空并且递增 1。我只是想看看单元测试如何与 Redis 一起工作,以及如何从特定的键、值对中提取分数并检查其有效性。

控制器类//当用户刷新 api/do/v1/togglelike/{id}" 时,该用户的分数将以 redis 格式更新并递增 1;

[HttpGet]
        [Route("api/do/v1/togglelike/{id}")]
        public IHttpActionResult ToggleLike(String id)
        {
            var currentUser = "mike";

            var likeSet = redis1.SortedSetRangeByScore("likes:" + id);
            var likeStatus = redis1.SortedSetScore("likes:" + id, currentUser);
            //Current user has not yet liked the profile
            if (likeStatus == null)
            {
                redis1.SortedSetAdd("likes:" + id, currentUser, 1);
                return Ok("Like Added");
            }
            /*redis1.SortedSetAdd("likes:" + id, currentUser, 1);
            return Ok("Like Added");*/
            else
            {
                double counter = redis1.SortedSetIncrement("likes:" + id, currentUser, 1);
                 redis1.SortedSetAdd("likes:" + id, currentUser, counter);
                 return Ok("Like Added");
                /*redis1.SortedSetRemove("likes:" + id, currentUser);
                return Ok("Like Removed");*/
            }
        }

测试类:我想从键、值对中获取分数并检查它是否等于有效数字;

namespace VideoControllerTest
{
    [TestClass]
    public class VideoControllerTest
    {
         IDatabase redis1;
        public VideoControllerTest()
        {
            redis1 = RedisFactory.Connection.GetDatabase();
        }
        [TestMethod]
        public void VideoController_Adview()
        {
            //Arrange
            VideoController controller = new VideoController();
            //Act
            IHttpActionResult actionResult = controller.ToggleLike("video123");
            //Assert; Check to see the counter is incremented by 1 and is not null;
        }
    }
}

模拟 Redis 非常适合测试下一层,但这是一件复杂的事情,您可能会花费大量时间测试模拟而不是生产实现。

相反,请在运行测试的任何位置设置 Redis 的本地实例,为密钥添加前缀,然后使用SCAN清理它们。所以(在测试中使用StackExchange.Redis)...

// In your test setup
VideoController.Prefix = "test:";
// Then create your testing data in Redis
// In your code include the prefix
var likeSet = redis1.SortedSetRangeByScore(Prefix + "likes:" + id);
// In your test cleanup:
// Get the server, as SCAN/KEYS will only run across one server
var server = redis.GetServer("localhost", 6379);
// Get all the keys with the test prefix
var testKeys = server.Keys(pattern: "test:*");
 // Delete all the keys, with wait because we don't want fire&forget
Task.WaitAll((from k in testKeys select db.KeyDeleteAsync(k)).ToArray());

这在生产中是非常糟糕的做法,因为KEYSSCAN操作将只为一个 Redis 服务器运行,KEYS将同步运行并在运行时阻塞。不过,在单元测试中应该没问题,只要您在同一位置没有生产 Redis 即可。

为了能够对外部系统(在本例中为 redis 数据库)进行单元测试,您必须模拟外部系统。

如果redis1是一个接口,你可以很容易地用像 mock 这样的框架来模拟它,如果它是一个实现,这将很难,你必须用你自己的类包装它才能模拟它。

您需要将 IDatabase 传递到控制器中,所以我添加了另一个构造函数。

class VideoController
{
    private IDatabase redis1;
    public VideoController(IDatabase db)
    {
        this.redis1 = db;
    }
}

测试方法应如下

//note : library used for mocking is moq (https://github.com/Moq/moq4)
        [TestMethod]
        public void VideoController_Adview()
        {
        //Arrange
            Mock<IDatabase> mockRedis = new Mock<IDatabase>();
            //set existing score as null
            mockRedis.Setup(r => r.SortedSetScore(It.isAny<string>,It.isAny<string>)).Returns(null); 
            VideoController controller = new VideoController(mockRedis.Object);

            //Act
            IHttpActionResult actionResult = controller.ToggleLike("video123");
            //verify added once
            mockRedis.Verify(r => r.SortedSetAdd("likes:videio123",1), Times.Once());
        }

最新更新