Creating a Mock Class for Unit Testing with a Spring Repository
I don’t know about you, but when I do unit testing, I often don’t want to have to deal with all of the details about accessing the data store. In fact, all of those details can make your unit tests far more brittle than they need to be. Sometimes, you just want it to be a little more black box and deal with the results instead of all of the interim data accesses that got you there. That’s when you need to create a Mock Class for your repository.
I was recently trying to test a batch program that rebuilt an index in my Mongo DB from existing tables. I thought, “Wouldn’t it be great to have a Mock class that would implement my repository and just work like a database?”
Then, I thought about how a Mongo DB is like a giant HashMap and that it should be really easy to create a Mock Repository and back it with a Hash Map. So I did. What follows is an example I hope you benefit from.
Creating a Mock Class for Unit Testing with a Spring Repository
Let’s start with a Widget that gets stored in the repo. I have left off the getters/setters for the sake of simplicity.
public class Widget { @Id @GeneratedValue private Long id; private String name; private String description; }
Now let’s look at the repository we created to store these.
public interface WidgetRepository extends MongoRepository<Widget, Long> { }
And now for the interesting part…here’s my implementation of a MockWidgetRepository that uses a HashMap to back it up.
public class MockWidgetRepository implements WidgetRepository{ Map<Long, Widget> backingMap = new HashMap<>(); @Override public <S extends Widget> S save(S entity) { backingMap.put(entity.getId(), entity); return entity; } @Override public <S extends Widget> List<S> save(Iterable<S> entites) { return null; } @Override public Widget findOne(Long id) { Widget result = backingMap.get(id); return result; } @Override public boolean exists(Long aLong) { return false; } @Override public List<Widget> findAll() { List<Widget> result = new ArrayList<>(); result.addAll(backingMap.values()); return result; } @Override public Iterable<Widget> findAll(Iterable<Long> longs) { return null; } @Override public long count() { return 0; } @Override public void delete(Long aLong) { } @Override public void delete(Widget entity) { } @Override public void delete(Iterable<? extends Widget> entities) { } @Override public void deleteAll() { backingMap.clear(); } @Override public List<Widget> findAll(Sort sort) { return null; } @Override public Page<Widget> findAll(Pageable pageable) { return null; } @Override public <S extends Widget> S insert(S entity) { return null; } @Override public <S extends Widget> List<S> insert(Iterable<S> entities) { return null; } @Override public <S extends Widget> S findOne(Example<S> example) { return null; } @Override public <S extends Widget> List<S> findAll(Example<S> example) { return null; } @Override public <S extends Widget> List<S> findAll(Example<S> example, Sort sort) { return null; } @Override public <S extends Widget> Page<S> findAll(Example<S> example, Pageable pageable) { return null; } @Override public <S extends Widget> long count(Example<S> example) { return 0; } @Override public <S extends Widget> boolean exists(Example<S> example) { return false; } }
A couple of notes:
- I only implemented save, findOne, findAll, and deleteAll because that’s all I needed for now. The rest of the methods are left as an exercise for the reader.
- I’ve left the backingMap public because this is only used for testing and it makes it easier and faster.
- I’ve stubbed the other methods so that it will compile cleanly.
And that’s it. Pretty simple, huh?
Happy coding!