Jedis-Mock

Java reimplementation of Redis/Valkey for white box testing

Ivan Ponomarev

ivan

Ivan Ponomarev

  • Team Lead @ Synthesized.io

  • Teaching CS @ Northeastern University London

2. What is Redis/Valkey?

logo
valkey logo
  • Remote Dictionary Server

  • In-memory key–value database

  • Different kinds of abstract data structures

  • Not open source since 2024 with OOS forks available, but it’s a separate story…​

5. Scale of Users

Twitter, AirBnB, Tinder, Yahoo, Adobe, Hulu, Amazon…​

7. So how do we test?

Testcontainers, of course!

testcontainers transparent

(In fact, Redis is one of the first demo examples for Testcontainers ever)

8. Redis mocks in different programming languages

9. Why mock, if we have Testcontainers?

  • Still, Testcontainers require Docker and some time to download and run the container.
    Jedis-Mock requires Java only.

  • Sometimes we want to manage the reply:

    • imitate a system or network failure

    • give a deterministic answer for non-deterministic query

10. Why mock, if we have Testcontainers?

  • Sometimes we need to check the actual queries being sent

Diagram

11. JedisMock history

  • 2017: Filipe Peliz Pinto Teixeira forked an abandoned POC repository.

  • 2019: I tried to use it in my project and soon gave up

  • 2021-now: actively developed by MIPT students under my supervision

  • 2023: I have full rights for maintaing & publishig new versions

redis maintainers

12. JedisMock: who uses it?

  • Known OSS dependents: Slack API client, dropwizard.io

users

13. JedisMock: who uses it?

  • Stable flow of incoming issues and requests.

  • 180+ stars on GitHub — please give it a star!

issues

14. Some technical details of importance

  • Redis is written in C (155 KLOC of C code)

loc

15. Some technical details of importance

  • Redis/Valkey heavily relies on system calls and runs on Unix-like systems only.

  • Redis/Valkey uses a single thread to process all the requests (thus isolation).

16. JedisMock: the idea

sak

sak

Redis data type

Java data type

HMAP

HashMap

LIST

ArrayList

SET

HashSet

ZSET

TreeSet+HashMap

Bit operations

BitSet

17. JedisMock: networking&threading

  • java.net.ServerSocket

  • Each incoming client occupies a thread in CachedThreadPool

  • All the threads synchronize on a single lock before making changes

  • Blocking operations (like BLPOP) use wait/notify

18. So how do we make it similar to real Redis?

  • Redis: 246 commands of varying popularity (Pareto 80/20 principle applies)

  • Jedis-mock: 166 commands supported (67% and the number is growing)

distr1

19. So how do we make it similar to real Redis?

  • Redis: 246 commands of varying popularity (Pareto 80/20 princple applies)

  • Jedis-mock: 166 commands supported (67% and the number is growing)

pareto

20. How do we count it?

  • Redis COMMAND command lists all the supported commands (currently 225 of them)

  • Classes annotated @Command(<COMMAND_NAME>) implement commands in Jedis-mock

21. An automatically built fit-gap report

supported redis operations

22. What makes us sure that commands work correctly?

Comparison testing

@TestTemplate
public void whenHSettingOnTheSameKeys_EnsureReturnTypeIs1WhenKeysAreNew(Jedis jedis) {
    assertEquals(1L, jedis.hset(HASH, FIELD_1, VALUE_1));
    assertEquals(0L, jedis.hset(HASH, FIELD_1, VALUE_1));
}

23. What makes you sure that commands work correctly?

Comparison testing

comparison

24. Native Redis E2E tests

  • Written in Tcl

test {INCR over 32bit value} {
    r set novar 17179869184
    r incr novar
} {17179869185}
  • 3932 tests in 200 Tcl scripts

  • we only run a subset of them, gradually increasing the number of successful "native" tests

25. Errors found by running "native" tests

SET  key1 stringvalue     // puts a string to key1
HSET key2 subkey1 avalue  // puts a hashmap to key2

GET  key1                 // returns stringvalue
GET  key2                 // error: WRONGTYPE

MGET key1 key2            // ????

26. Errors found by running "native" tests

LPOP  mylist     // returns the first element
                 // or null, if the list is empty

BLPOP mylist 1   // returns the first element
                 // or waits for it for 1 second maximum

BLPOP mylist 0   // ????

27. What next / open questions?

  • How closely a test mock should mock 'the real thing'?

  • Are those "bugs" Jedis-mock problems or Redis spec problems / possible source of bad practices?

  • Some day we will successfully run 100% of the tests. What happens when somebody finds a bug in Jedis-mock?

28. Thanks for listening!