Search code examples
javaspringspring-bootjunitassertj

Is it a good idea to use ID for comparison with AssertJ?


I got the following class with which I want to test my repository:

package de.gabriel.vertretungsplan.repositories;

import de.gabriel.vertretungsplan.models.Fach;
import de.gabriel.vertretungsplan.models.Klasse;
import de.gabriel.vertretungsplan.models.StundenplanEintrag;
import de.gabriel.vertretungsplan.models.Tag;
import de.gabriel.vertretungsplan.models.enums.Anwesenheit;
import de.gabriel.vertretungsplan.models.enums.Rolle;
import de.gabriel.vertretungsplan.models.enums.Tage;
import de.gabriel.vertretungsplan.models.users.Lehrer;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ActiveProfiles;

import java.util.List;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

@DataJpaTest
@ActiveProfiles("test")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class StundenplanEintragRepositoryTest {

    @Autowired
    private StundenplanEintragRepository stundenplanEintragRepository;
    @Autowired
    private KlasseRepository klasseRepository;
    @Autowired
    private FachRepository fachRepository;
    @Autowired
    private LehrerRepository lehrerRepository;
    @Autowired
    private TagRepository tagRepository;

    private Lehrer deutschLehrer;
    private Tag montag;
    private StundenplanEintrag stundenplanEintragReference;

    @BeforeAll
    void init() {
        Klasse klasse6a = new Klasse("6a");

        klasseRepository.saveAll(List.of(
                klasse6a
        ));

        Fach deutsch = new Fach("Deutsch", "DE");

        fachRepository.saveAll(List.of(
                deutsch
        ));

        deutschLehrer = new Lehrer("deutschLehrer", "deutschLehrer", Rolle.getPrefixedRolle(Rolle.LEHRER), Anwesenheit.ANWESEND);
        deutschLehrer.setFaecher(List.of(deutsch));

        lehrerRepository.saveAll(List.of(
                deutschLehrer
        ));

        montag = new Tag(Tage.MONTAG);

        tagRepository.saveAll(List.of(
                montag
        ));

        stundenplanEintragReference = new StundenplanEintrag(1, klasse6a, deutsch, deutschLehrer, montag);

        stundenplanEintragRepository.saveAll(List.of(
                stundenplanEintragReference
        ));
    }

    @AfterAll
    void tearDown() {
        stundenplanEintragRepository.deleteAll();
    }

    @Test
    @DisplayName("Check if correct Stundenplan Eintrag is returned given Lehrer [deutschLehrer] and Tag [montag]")
    void findByLehrerAndTag() {
        List<StundenplanEintrag> stundenplanEintrag = stundenplanEintragRepository.findByLehrerAndTag(deutschLehrer, montag);
        assertThat(stundenplanEintrag.get(0).getId()).isEqualTo(stundenplanEintragReference.getId());
    }

    @Test
    @DisplayName("Check if correct Stundenplan Eintrag is returned given Lehrer [deutschLehrer]")
    void findByLehrer() {
        List<StundenplanEintrag> stundenplanEintrag = stundenplanEintragRepository.findByLehrer(deutschLehrer);
        assertThat(stundenplanEintrag.get(0).getLehrer().getUsername()).isEqualTo(deutschLehrer.getUsername());
    }

}

As you can see currently I am comparing the result from the repository by a single field with the reference object (like the id or username), because if I compare them like this:

    @Test
    @DisplayName("Check if correct Stundenplan Eintrag is returned given Lehrer [deutschLehrer] and Tag [montag]")
    void findByLehrerAndTag() {
        List<StundenplanEintrag> stundenplanEintrag = stundenplanEintragRepository.findByLehrerAndTag(deutschLehrer, montag);
        assertThat(stundenplanEintrag.get(0)).isEqualTo(stundenplanEintragReference);
    }

    @Test
    @DisplayName("Check if correct Stundenplan Eintrag is returned given Lehrer [deutschLehrer]")
    void findByLehrer() {
        List<StundenplanEintrag> stundenplanEintrag = stundenplanEintragRepository.findByLehrer(deutschLehrer);
        assertThat(stundenplanEintrag.get(0).getLehrer()).isEqualTo(deutschLehrer);
    }

the tests fail because the adress of the actual objects differ from the "expected" objects adress even if they are "the same" objects.

Expected :de.gabriel.vertretungsplan.models.StundenplanEintrag@38d895e8
Actual   :de.gabriel.vertretungsplan.models.StundenplanEintrag@1b897ffb

I understand that it gets a new adress in memory (wrong as pointed out in the comments, it's the hash code). I want to know if it is a good practice to compare by something like the id or how else you would solve this or if there is a better practice to compare the object returned by a repository with the originally saved object? (And I still want to be able to save the entity only once in the @BeforeAll annotated method)


Solution

  • Generally I see nothing bad in comparison IDs in the test. It's pretty obvious that you operate with records and assert if it's the same record (the same record has the same ID).

    As an option, of course, you may consider to make your solution more complex and override equals()(and hashCode()) methods of your class. It will make the solution more elegant, so you encapsulate in the class the definition of "being the same". You can define field(s) that needs to be equal in order to consider the objects as equal.

    I do not think there is a "right" answer to your question. It depends... Check the discussions on the Internet and get the idea in which situations you want to override equals and in which situations you don't. Here are the couple of such a discussions:

    1. https://softwareengineering.stackexchange.com/questions/399265/do-we-always-need-to-override-equals-hashcode-when-crreating-a-new-class

    2. Why do I need to override the equals and hashCode methods in Java?

    As I said, for the start, I'd probably compare IDs and not go the path of overriding, especially if you're not feeling confident with that.