1

I have an interface called localcache:

package localcache

type Cache interface {
    Set(k string, v interface{})  error
    Get(k string) (interface{}, error)
}

and another file containing its implementation

type cache struct {
    pool map[string]value
}

type value struct {
    data      interface{}
    expiredAt time.Time
}

func New() *cache {
    if cacheInstance == nil {
        once.Do(func() {
            cacheInstance = &cache{
                pool: make(map[string]value),
            }
            go cacheInstance.spawnCacheChcker()
        })
        return cacheInstance
    }
    return cacheInstance
}

func (c *cache) Set(k string, v interface{}) (e error) {
    expiredAt := time.Now().Add(expiredIn)
    c.pool[k] = value{data: v, expiredAt: expiredAt}
    e = nil
    return
}

func (c *cache) Get(k string) (v interface{}, e error) {
    v = c.pool[k].data
    e = nil
    return
}

func (c *cache) spawnCacheChcker() {
    for {
        for k, v := range c.pool {
            if !(v.expiredAt.Before(time.Now())) {
                continue
            }
            c.evictCache(k)
        }
        time.Sleep(checkInBetween)
    }
}

a cache will be expired 30 seconds after it's been set, how can I test this functionality? I'm using testify rn, my brute force solution was to time.Sleep in the test function, but I feel like this will prolong the entire test process, which is not the best practice.

Is there any ways to mock the expiredAt inside the Set function? Or is there any workaround that tests this better?

fanfan
  • 39
  • 1
  • 5
  • If you want to check keys to evict in a goroutine via spawnCacheChcker will be better use a mutex (rwlock maybe) when read / write / delete. – Tiago Peczenyj Mar 26 '22 at 05:43
  • Another approach is to only evict the key when you access it. Check if expired and delete if need. If you really need to use a goroutine, use a time Ticker instead Sleep and consider receive context.Context to avoid goroutine leaking (by cancel when it is not needed) – Tiago Peczenyj Mar 26 '22 at 05:46

1 Answers1

3

You can move time.Now() to be a field in cache struct like

type cache struct {
  nowFunc func() time.Time
}

then assign time.Now to it in the constructor. Within the test, just modify it to your mock function to return any time you want.

tony
  • 71
  • 1
  • 2