# 《高性能Redis组件》测试验证-第01节:高性能Redis组件单元测试场景验证

作者:冰河
星球:http://m6z.cn/6aeFbs (opens new window)
博客:https://binghe.gitcode.host (opens new window)
文章汇总:https://binghe.gitcode.host/md/all/all.html (opens new window)
源码获取地址:https://t.zsxq.com/0dhvFs5oR (opens new window)

沉淀,成长,突破,帮助他人,成就自我。

  • 本章难度:★★☆☆☆
  • 本章重点:对高性能Redis组件实现的脱敏功能进行单元测试,从总体上理解高性能Redis组件防缓存击穿、穿透和雪崩问题的核心设计思想,并从全局视角了解高性能Redis组件的设计和架构思想,并能够将其灵活应用到自身实际项目中。

大家好,我是冰河~~

有一定工作经验的小伙伴都知道,在实际工作中,我们自己开发的功能在提测前,都是需要我们自己编写单元测试,并且需要所有单元测试都通过后才能提交测试的。高性能Redis组件亦是如此,我们开发完高性能Redis组件的核心功能后,也需要对功能进行单元测试。

# 一、前言

截止到目前,我们完成了高性能Redis组件的核心功能,包括:基础功能的设计与实现、分布锁的设计与实现、防缓存击穿、穿透和雪崩的核心设计与实现。这些落地实现的代码功能是否符合我们的预期,就需要针对落地实现的方法进行单元测试。所以,接下来,我们要对高性能Redis组件对外提供的接口方法进行单元测试。

# 二、本节诉求

对高性能Redis组件实现的脱敏功能进行单元测试,从总体上理解高性能Redis组件防缓存击穿、穿透和雪崩问题的核心设计思想,并从全局视角了解高性能Redis组件的设计和架构思想,并能够将其灵活应用到自身实际项目中。

# 三、单元测试验证

本节,会对高性能Redis组件接口对外提供的方法进行单元测试。

# 3.1 创建测试模型

为了更好的进行单元测试,在正式进行单元测试之前,创建单元测试的对象模型User类。User类的代码也比较简单,就是一个包含id和name的实体类。

源码详见:io.binghe.redis.plugin.test.bean.User。

public class User {

    private Long id;
    private String name;

    public User() {
    }

    public User(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return JSONUtil.toJsonStr(this);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 3.2 编写单元测试

创建DistributeCacheServiceTest类编写单元测试方法。

DistributeCacheServiceTest类的源码详见:io.binghe.redis.plugin.test.DistributeCacheServiceTest。

@SpringBootTest
@RunWith(SpringRunner.class)
public class DistributeCacheServiceTest {

    @Autowired
    private DistributeCacheService distributeCacheService;


    @Test
    public void testQueryWithPassThrough(){
        User user = distributeCacheService.queryWithPassThrough("pass:through:", 1002852L, User.class,  this::getUser, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(user));
    }
    @Test
    public void testQueryWithPassThroughWithoutArgs(){
        User user = distributeCacheService.queryWithPassThroughWithoutArgs("pass:through001:", User.class,  this::getUserWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(user));
    }

    @Test
    public void testQuerySimpleDataWithPassThrough(){
        Integer id = distributeCacheService.queryWithPassThrough("pass:through2:", 100285210, Integer.class,  this::getId, 60L, TimeUnit.SECONDS);
        System.out.println(id);
    }
    @Test
    public void testQuerySimpleDataWithPassThroughWithoutArgs(){
        Integer id = distributeCacheService.queryWithPassThroughWithoutArgs("pass:through2002:",  Integer.class,  this::getIdWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(id);
    }

    @Test
    public void testQueryWithPassThroughList(){
        List<User> list = distributeCacheService.queryWithPassThroughList("pass:through:list:", null, User.class, this::getUserList, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }
    @Test
    public void testQueryWithPassThroughListWithoutArgs(){
        List<User> list = distributeCacheService.queryWithPassThroughListWithoutArgs("pass:through:list003:", User.class, this::getUserListWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }

    @Test
    public void testQuerySimpleDataWithPassThroughList(){
        List<Integer> list = distributeCacheService.queryWithPassThroughList("pass:through:list2:", 100285211, Integer.class, this::getIds, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }
    @Test
    public void testQuerySimpleDataWithPassThroughListWithoutArgs(){
        List<Integer> list = distributeCacheService.queryWithPassThroughListWithoutArgs("pass:through:list2004:", Integer.class, this::getIdsWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }

    @Test
    public void testQueryWithLogicalExpire(){
        User user = distributeCacheService.queryWithLogicalExpire("logical:expire:", 1002852L, User.class,  this::getUser, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(user));
    }

    @Test
    public void testQueryWithLogicalExpireWithoutArgs(){
        User user = distributeCacheService.queryWithLogicalExpireWithoutArgs("logical:expire005:", User.class,  this::getUserWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(user));
    }

    @Test
    public void testQuerySimpleDataWithLogicalExpire(){
        Integer id = distributeCacheService.queryWithLogicalExpire("logical:expire2:", 100285212, Integer.class,  this::getId, 60L, TimeUnit.SECONDS);
        System.out.println(id);
    }
    @Test
    public void testQuerySimpleDataWithLogicalExpireWithoutArgs(){
        Integer id = distributeCacheService.queryWithLogicalExpireWithoutArgs("logical:expire2006:", Integer.class,  this::getIdWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(id);
    }

    @Test
    public void testQueryWithLogicalExpireList(){
        List<User> list = distributeCacheService.queryWithLogicalExpireList("logical:expire:list:", null, User.class, this::getUserList, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }
    @Test
    public void testQueryWithLogicalExpireListWithoutArgs(){
        List<User> list = distributeCacheService.queryWithLogicalExpireListWithoutArgs("logical:expire:list007:", User.class, this::getUserListWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }

    @Test
    public void testQuerySimpleDataWithLogicalExpireList(){
        List<Integer> list = distributeCacheService.queryWithLogicalExpireList("logical:expire:list2:", 100285213, Integer.class, this::getIds, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }
    @Test
    public void testQuerySimpleDataWithLogicalExpireListWithoutArgs(){
        List<Integer> list = distributeCacheService.queryWithLogicalExpireListWithoutArgs("logical:expire:list2008:", Integer.class, this::getIdsWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }

    @Test
    public void testQueryWithMutex(){
        User user = distributeCacheService.queryWithMutex("mutex:", 1002852L, User.class,  this::getUser, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(user));
    }
    @Test
    public void testQueryWithMutexWithoutArgs(){
        User user = distributeCacheService.queryWithMutexWithoutArgs("mutex009:", User.class,  this::getUserWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(user));
    }

    @Test
    public void testQuerySimpleDataWithMutex(){
        Integer id = distributeCacheService.queryWithMutex("mutex2:", 100285214, Integer.class,  this::getId, 60L, TimeUnit.SECONDS);
        System.out.println(id);
    }
    @Test
    public void testQuerySimpleDataWithMutexWithoutArgs(){
        Integer id = distributeCacheService.queryWithMutexWithoutArgs("mutex2010:", Integer.class,  this::getIdWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(id);
    }

    @Test
    public void testQueryWithMutexList(){
        List<User> list = distributeCacheService.queryWithMutexList("mutex:list:", null, User.class, this::getUserList, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }
    @Test
    public void testQueryWithMutexListWithoutArgs(){
        List<User> list = distributeCacheService.queryWithMutexListWithoutArgs("mutex:list011:", User.class, this::getUserListWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }

    @Test
    public void testQuerySimpleDataWithMutexList(){
        List<Integer> list = distributeCacheService.queryWithMutexList("mutex:list2:", 123, Integer.class, this::getIds, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }

    @Test
    public void testQuerySimpleDataWithMutexListWithoutArgs(){
        List<Integer> list = distributeCacheService.queryWithMutexListWithoutArgs("mutex:list2012:", Integer.class, this::getIdsWithoutArgs, 60L, TimeUnit.SECONDS);
        System.out.println(JSONUtil.toJsonStr(list));
    }

    /**
     * 模拟带参数从数据库查询对象
     */
    public User getUser(Long id){
        return new User(id, "binghe");
    }

    /**
     * 默认不带参数从数据库查询对象
     */
    public User getUserWithoutArgs(){
        return new User(1L, "binghe");
    }

    /**
     * 模拟带参数查询从数据库对象列表
     */
    public List<User> getUserList(String type){
        return Arrays.asList(
                new User(1L, "binghe001"),
                new User(2L, "binghe002"),
                new User(3L, "binghe003")
        );
    }

    /**
     * 模拟不带参数从数据库查询对象列表
     */
    public List<User> getUserListWithoutArgs(){
        return Arrays.asList(
                new User(1L, "binghe001"),
                new User(2L, "binghe002"),
                new User(3L, "binghe003")
        );
    }

    /**
     * 模拟带参数从数据库查询简单数据类型数据
     */
    public Integer getId(Integer id){
        return id;
    }

    /**
     * 模拟不带参数从数据库查询简单数据类型数据
     */
    public Integer getIdWithoutArgs(){
        return 0;
    }

    /**
     * 模拟带参数从数据库查询简单数据类型数据列表
     */
    public List<Integer> getIds(Integer id){
        return Arrays.asList(0,0,0);
    }

    /**
     * 模拟不带参数从数据库查询简单数据类型数据列表
     */
    public List<Integer> getIdsWithoutArgs(){
        return Arrays.asList(0,0,0);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

# 四、测试验证

测试验证的主要步骤如下所示。

(1)启动Redis

启动Redis,这里我直接在本地启动一个Redis服务,IP地址为127.0.0.1,端口为6379。

(2)配置application.yml

在项目的src/main/resources目录下创建application.yml文件,配置如下内容。

spring:
  application:
    name: redis-plugin

  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    password:
    timeout: 30000
    lettuce:
      pool:
        enabled: true
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: 5000
    address: redis://127.0.0.1:6379

cache:
  type:
    distribute: redis

distribute:
  type:
    lock: redisson

redis:
  arrange:
    type: single # single or cluster
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

(3)运行单元测试

运行DistributeCacheServiceTest类的所有代码,运行结果如下所示。


从测试结果来看,高性能Redis组件对外提供的接口方法所有单元测试用例全部验证通过,并且打印的结果数据也符合我们的预期。

注意:这里,我们是实际访问Redis进行的单元测试,在实际工作中,编写单元测试一般是通过Mock方式进行的,单元测试代码不允许依赖外部环境资源,例如,不允许依赖数据库、Redis等。关于如何使用Mock编写单元测试,后续我会单独开一个专栏专门讲解,这里就不再赘述。

# 五、本节总结

本节,主要对高性能Redis组件提供的核心功能进行了单元测试,主要是对高性能Redis组件对外提供的接口方法进行了全面的单元测试,测试结果符合我们的预期。

最后,可以在评论区写下你学完本章节的收获,祝大家都能学有所成,我们一起搞定高性能Redis组件。

# 六、写在最后

在冰河的知识星球除了热更的AI大模型外,还有其他十几个项目,像实战AI大模型、手写高性能敏组件、手写线程池、手写高性能SQL引擎、手写高性能Polaris网关、手写高性能熔断组件、手写通用指标上报组件、手写高性能数据库路由组件、手写分布式IM即时通讯系统、手写Seckill分布式秒杀系统、手写高性能RPC、实战高并发设计模式、简易商城系统等等。

这些项目的需求、方案、架构、落地等均来自互联网真实业务场景,让你真正学到互联网大厂的业务与技术落地方案,并将其有效转化为自己的知识储备。

值得一提的是:冰河自研的Polaris高性能网关比某些开源网关项目性能更高,目前正在热更AI大模型项目,也正在实现MCP,实战AI大模型正在热更中,全程带你分析原理和手撸代码。

你还在等啥?不少小伙伴经过星球硬核技术和项目的历练,早已成功跳槽加薪,实现薪资翻倍,而你,还在原地踏步,抱怨大环境不好。抛弃焦虑和抱怨,我们一起塌下心来沉淀硬核技术和项目,让自己的薪资更上一层楼。


目前,领券加入星球就可以跟冰河一起学习《DeepSeek大模型》、《手写高性能脱敏组件》、《手写线程池》、《手写高性能SQL引擎》、《手写高性能Polaris网关》、《手写高性能RPC项目》、《分布式Seckill秒杀系统》、《分布式IM即时通讯系统》《手写高性能通用熔断组件项目》、《手写高性能通用监控指标上报组件》、《手写高性能数据库路由组件》、《手写简易商城脚手架项目》、《Spring6核心技术与源码解析》和《实战高并发设计模式》,从零开始介绍原理、设计架构、手撸代码。

花很少的钱就能学这么多硬核技术、中间件项目和大厂秒杀系统与分布式IM即时通讯系统,比其他培训机构不知便宜多少倍,硬核多少倍,如果是我,我会买他个十年!

加入要趁早,后续还会随着项目和加入的人数涨价,而且只会涨,不会降,先加入的小伙伴就是赚到。

另外,还有一个限时福利,邀请一个小伙伴加入,冰河就会给一笔 分享有奖 ,有些小伙伴都邀请了50+人,早就回本了!

# 七、其他方式加入星球

  • 链接 :打开链接 http://m6z.cn/6aeFbs 加入星球。
  • 回复 :在公众号 冰河技术 回复 星球 领取优惠券加入星球。

特别提醒: 苹果用户进圈或续费,请加微信 hacker_binghe 扫二维码,或者去公众号 冰河技术 回复 星球 扫二维码加入星球。

好了,今天就到这儿吧,我是冰河,我们下期见~~