Learning TDD together with Stickee

Aug. 4, 2024

Yari and me recently started doing a project to learn both Spring Boot and test-driven development (TDD). The project, named Stickee, is intended to be a Pastebin clone, but we plan to add more features in the future, like uploading other kind of files.

The project follows a typical Spring Boot architecture with controllers, models and services.

Today we did the two basic operations of the NoteService: get(), which returns a note given its resource locator, and create() which creates a new note. Since we used TDD, we created tests for each method before implementing them.

Doing these tests made me realize that, at the moment we were writing them, we were basically imposing restrictions on all different implementation posibilities. And not only on the output, but on the internal behaviour, since we were mocking the NoteRepository, and this forces the NoteService to use the NoteRepository methods that we mocked, to pass the tests.

The test for the create() method is very simple:

@Test
void shouldSave() {
    var note = NoteStubBuilder.create().build();
    
    noteService.create(note);

    verify(noteRepository).save(note);
}

We are simply checking that NoteService calls the save() method with the Note given as a parameter on its create() method.

For the get() method, we made two tests:

@Test
void shouldGetWhenExisting() {
    var note = NoteStubBuilder.create().withText("I should be get").build();

    given(noteRepository.findByResourceLocator(note.getResourceLocator())).willReturn(Optional.of(note));
    var maybeNote = noteService.get(note.getResourceLocator());

    assertTrue(maybeNote.isPresent());
    assertEquals(note, maybeNote.get());        
}
@Test
void shouldGetEmptyWhenAbsent() {
    String resourceLocator = "f0f0f0";

    given(noteRepository.findByResourceLocator(resourceLocator)).willReturn(Optional.empty());
    var maybeNote = noteService.get(resourceLocator);

    assertTrue(maybeNote.isEmpty());
}

The first one mocks the repository call so it will return a note when given its resource locator. The method can’t create a note by its own, because it needs the Note’s text, only provided by the repository. So we force the get() method to actually return the Note provided by the repository.

In the second test, we are mocking the repository call to behave like it doesn’t find a note with the given resource locator. This test is necesary because if a Note doesn’t exist, the NoteService could still creates a Note by its own with arbitrary text. But with this test, we are forcing it to return an Optional.empty() if the repository doesn’t find any note.

If you have any question or improvement, contact me at @Sugui@awoo.fai.st.