axios-cache-adapter icon indicating copy to clipboard operation
axios-cache-adapter copied to clipboard

Add option to turn off caching / reset cache between tests

Open ahayes91 opened this issue 3 years ago • 4 comments

I'm using axios-cache-adapter with a localForage store as per the example at https://www.npmjs.com/package/axios-cache-adapter#use-localforage-as-cache-store. I'm also using Mock Service Worker to mock responses to API endpoints in my unit and integration tests.

https://mswjs.io/docs/faq#why-do-i-get-stale-responses-when-using-react-queryswretc gives examples of how to reset the cache in between tests using other caching frameworks.

Is there a way to do something similar in axios-cache-adapter? For now, the only solution I can see is to mock axios-cache-adapter in my tests, similar to what I've mentioned in https://github.com/RasCarlito/axios-cache-adapter/issues/212. Ideally though I would prefer to be able to reset the cache between tests, rather than mocking the module altogether.

ahayes91 avatar Feb 26 '21 10:02 ahayes91

@ahayes91 total coincidence that I'm doing something very similar at the moment and saw this post like 2 minutes after you wrote it!

I had the exact same issue in my code base. The way I got around this was to fire off different routes per test. It was a bit easier for me to do that because in our code base we're writing a custom HTTP client that will handle caching of certain requests for us, so I was able to simply use different routes for each test. This isn't ideal though, and it falls apart if you have to test multiple requests against the same routes, which may well be the case.

Another thing you could do is in your own interface for whatever you're building, you could add a parameter that allows you to turn the cache off, but again, if this isn't functionality you'd actually want to push out to consumers, this might not be a good solution as you'd be changing your interface for the tests (if it's functionality you want though this would do it).

Another final solution you could do is to have separate files for individual tests, as they will run in their own process.

Oh one final thing I thought of - if you happen to be using local storage for caching, then you could have a local storage mock that wipes out the cache before each new test.

None of these solutions are ideal, but the issue of course at the moment is that the caching behaviour is working correctly, but it's all in-memory by default which means if the tests exist in the same file the cache will persist as it currently stands.

citypaul avatar Feb 26 '21 10:02 citypaul

@citypaul Thanks so much for the suggestions! Great minds obviously.

This isn't ideal though, and it falls apart if you have to test multiple requests against the same routes, which may well be the case.

Yeah this is exactly the issue for me, I've got different responses based on search query parameters, but I think Mock Service Worker can't really differentiate endpoints solely based on query params if you know what I mean: https://mswjs.io/docs/recipes/query-parameters

Another thing you could do is in your own interface for whatever you're building, you could add a parameter that allows you to turn the cache off, but again, if this isn't functionality you'd actually want to push out to consumers, this might not be a good solution as you'd be changing your interface for the tests (if it's functionality you want though this would do it).

Not a bad idea but I have a bit of reticence in changing production code just to suit tests 🤔

Another final solution you could do is to have separate files for individual tests, as they will run in their own process.

This would be a lot of overhead for tests on things that use server-side pagination, sorting, etc. with search query parameters, not ideal either I think.

Oh one final thing I thought of - if you happen to be using local storage for caching, then you could have a local storage mock that wipes out the cache before each new test.

Totally separate issue, but I didn't find the localstorage driver with localForage that effective in production actually - dropInstance() didn't seem to work with my named store, and that's a critical piece for us to be able to clear the cache on logout of the application. I also found WEBSQL driver didn't work at all with caching as I mention in https://github.com/RasCarlito/axios-cache-adapter/issues/233. My driver setup, with some customisation, is as follows (I'm still humming and ha-ing about whether I really need memoryDriver or not, and I think I probably need to remove LOCALSTORAGE based on the above, but I'm still testing our implementation across our supported browser matrix):

  forageStore = localforage.createInstance({
    name: FORAGE_STORE_NAME,
    storeName: USER_ID,
    driver: [
      localforage.INDEXEDDB,
      localforage.LOCALSTORAGE,
      /* eslint-disable-next-line no-underscore-dangle */
      memoryDriver._driver,
    ],
  });
  cache = setupCache({
    maxAge: NO_CACHE,
    // Allow requests with query parameters (query params will appear in cache keys)
    exclude: { query: false },
    store: forageStore,
    key: setLocalForageKey,
  });

I think I'll stick with mocking the axios-cache-adapter module for now, as that seems to best fit our use cases, but these are all great suggestions if anyone else comes across the same issue.

ahayes91 avatar Feb 26 '21 11:02 ahayes91

@ahayes91 all totally fair responses, and I think your solution is probably the way to go for now. None of these are ideal solutions, but for now I guess it's just a matter of having to take a pragmatic approach. Mock heavy isn't ideal, but it's just the nature of the beast when it comes to caching. Caching is hard!

I'm not related to this project in any way btw, so perhaps the author might be able to come up with a better overall approach. Best of luck!

citypaul avatar Feb 26 '21 11:02 citypaul

Hello @ahayes91

Is there a way to do something similar in axios-cache-adapter?

If you passed your own store instance as you do with localForage you can use the clear() method on the local forage instance to empty the cache. You can also access the storage instance through cache.store property and thus call await cache.store.clear() on it.

If you use the setup method to instantiate axios including the axios-cache-adapter bound to it you get access to the storage instance through the api.cache property:

import { setup } from 'axios-cache-adapter'

// setup localforage..

// setup axios with cache adapter
const api = setup({
  // axios options
  baseURL: 'http://my-api',

  // axios-cache-adapter options
  cache: {
	store: forageStore
	// ... other options
  }
})

// Will get cached
const data = await api.get('/some-url')

// Clear the cache
await api.cache.clear()

I believe you should be able to clear your cache before/after each tests just like it is demonstrated for Mock Service Worker if you have can have access to the localforage instance.

Now what is the best way to go for tests and caching systems, I don't really know, as @citypaul says caching is hard 😅

Let me know.

Cheers

ghost avatar Feb 26 '21 17:02 ghost