For example, map access like this:
func (pool *fPool) fetch(url string) *ResultPromise {
pool.cacheLock.RLock()
if rp, pres := pool.cache[url]; pres {
pool.cacheLock.RUnlock()
return rp
}
pool.cacheLock.RUnlock()
pool.cacheLock.Lock()
if rp, pres := pool.cache[url]; pres {
pool.cacheLock.Unlock()
// Skip adding url if someone snuck it in between RUnlock an Lock
return rp
}
rp := newPromise()
pool.cache[url] = rp
pool.cacheLock.Unlock()
pool.c <- fetchWork{rp, url}
return rp
}
Here, the contents of the second if
condition are not covered. However, by placing breakpoints it's trivial to end up in that block.
The example isn't contrived, because:
- If we skip the
RLock
, the map will be unnecessarily locked when the workload is mostly reads. - If we skip the second
if
,the most expensive work (handled bypool.c <- fetchWork{rp, url}
in this case) can happen more than once for the same key, which is unacceptable.
I. Mocking
pool.cacheLock.Lock()
One way to cover that branch would be to mock
pool.cacheLock.Lock()
, and the mocked version could insert theurl
into the map. So checking again after this call, it would be found and execution would enter the body of the 2ndif
statement.Mocking by using interface
One way to mock
pool.cacheLock.Lock()
would be to makepool.cacheLock
an interface, and in tests you can set a mocked value whoseLock()
method will do the "dirty insert" into the map.Here's a simplified version of your code that uses an interface for
pool.cacheLock
:Its normal usage would be:
And a test case that will trigger the body of the 2nd
if
:Mocking by using a function field
Another way to mock
pool.cacheLock.Lock()
would be to "outsource" this functionality to a field of function type, which tests can replace to a function which–besides calling this–also does the "dirty insert".Again your simplified example:
Normal usage would be:
And a test case that will trigger the body of the 2nd
if
:II. Using a simple
test
flagThe idea here is that to support easy testing you build a simple
test
flag into the implementation offPool
(e.g. it can be a field offPool
), and the code you want to test deliberately checks for this flag:Now if you want to test the body of the 2nd
if
, all you gotta do is: