简体   繁体   中英

How manually commit transaction with Junit and Spring

I know it's not something new but I tried several approachs found and they didn't work. I've a simple Junit test:

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class LicensePlateTests {

@Autowired
private LicensePlateRepository licensePlateRepository;

@Autowired
private CountryRepository countryRepository;

@Before
public void setup() {
    TestTransaction.start();
    Country country = new Country();
    country.setName("Italy");
    country.setIsoCode("IT");
    countryRepository.save(country);

    assertEquals(1, countryRepository.count());
}

@Test
public void saveLicensePlateAndChangeCheckAudited() {
        assertEquals(1, countryRepository.count());
        LicensePlate plate = new LicensePlate();
        plate.setLicensePlate("AA123BB");
        plate.setEngineEuroLevel(3);
        plate.setCoutry(countryRepository.findFirstByOrderByIdAsc());
        plate = licensePlateRepository.save(plate);             

        assertEquals(1, licensePlateRepository.count());

        plate.setEngineEuroLevel(5);
        plate = licensePlateRepository.save(plate);     

        //always 1 plate
        assertEquals(1, licensePlateRepository.count());

        //different version number
        assertEquals(2, plate.getVersion().intValue());

    }
}

This test case fails because the version number remains 1.

What I'm doing could seem a bit strange but this test is just partial and is done because I'm using @Audited annotation on several properties inside my bean to track changes. What I was trying to do here is checking if the version number is incremented after a change. I know that @Transactional commit the transaction just at the end of the method and so this explains why my method fails.

What I was looking for is a way to make several commits in the same test method but at the same time preserving the visibilily of the transaction from the @Before method to the end of the test case.

Some best practice to follow?

===============================================================

I think I've solved my problem. I'm not sure my solution is a best practice and it's good, so I don't post it as reply. I think it could be useful so this is the complete class (I took inspiration from https://github.com/spring-projects/spring-data-envers/blob/master/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java )

     @RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
@TestPropertySource(locations = "classpath:test.properties")
public class LicensePlateAuditableTests {
    private Logger log = LogManager.getLogger();

    @Autowired
    private LicensePlateRepository licensePlateRepository;

    @Autowired
    private CountryRepository countryRepository;

    @Before
    public void setup() {
        licensePlateRepository.deleteAll();
        countryRepository.deleteAll();

        Country country = new Country();
        country.setName("Italy");
        country.setIsoCode("IT");
        countryRepository.save(country);        
    }



    @Test
    public void saveLicensePlateAndChangeCheckAudited() {
        assertEquals(1, countryRepository.count());

        LicensePlate plate = new LicensePlate();
        plate.setLicensePlate("AA123BB");
        plate.setEngineEuroLevel(3);
        plate.setCoutry(countryRepository.findFirstByOrderByIdAsc());
        plate = licensePlateRepository.save(plate);

        // First version number is 1
        assertEquals(1, plate.getVersion());

        // Changing some auditable data
        plate.setEngineEuroLevel(5);
        plate.setLicensePlate("AA956BB");
        plate = licensePlateRepository.save(plate);

        // different version number
        assertEquals(2, plate.getVersion());

        // I should have 2 revisions
        assertEquals(2, licensePlateRepository.findRevisions(plate.getId()).getContent().size());

        Revisions<Integer, LicensePlate> revisions = licensePlateRepository.findRevisions(plate.getId());

        int i = 1;
        for (Revision<Integer, LicensePlate> rev : revisions.getContent()) {
            if (i == 1) {
                // At the beginning the engine level was 3
                assertEquals(3, rev.getEntity().getEngineEuroLevel());
                // At the beginning the place number was AA123BB
                assertEquals("AA123BB", rev.getEntity().getLicensePlate());
            }

            if (i == 2) {
                // Then the engine level became 5
                assertEquals(5, rev.getEntity().getEngineEuroLevel());
                // Then the license plate number became AA956BB
                assertEquals("AA956BB", rev.getEntity().getLicensePlate());
            }
            i++;
        }

        // Check if the entry in the database has updated data
        assertEquals(5, licensePlateRepository.findOne(plate.getId()).getEngineEuroLevel());
        assertEquals("AA956BB", licensePlateRepository.findOne(plate.getId()).getLicensePlate());
    }

your comments in test method give you hints how many tests method should be. Just advice : you should follow on one name convention for tests method that is appropriate for you and your team. In my projects I get used to start test method's name with 'check' , 'verify'... and explaine use case that I check in method. something like this (just idea as you didn't show real classes) also I tried to show how test's method name might looks like :

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
@TestPropertySource(locations = "classpath:test.properties")
public class LicensePlateAuditableTests {
private Logger log = LogManager.getLogger();

@Autowired
private LicensePlateRepository licensePlateRepository;

@Autowired
private CountryRepository countryRepository;

private LicensePlate plate;
private final String PLATE_NUMBER_FIRST = "AA123BB";
private final String PLATE_NUMBER_SECOND = "AA956BB";
private int ENGINE_EURO_LEVEL_FIRST = 3;
private int ENGINE_EURO_LEVEL_SECOND = 3;

@Before
public void setup() {
    licensePlateRepository.deleteAll();
    countryRepository.deleteAll();

    Country country = new Country();
    country.setName("Italy");
    country.setIsoCode("IT");
    countryRepository.save(country);
}

@Test
public void checkThatVersionIsOneAfterCreateLicensePlate() {
    //given
    plate = createLicensePlateWithDefaultCountry(PLATE_NUMBER_FIRST, ENGINE_EURO_LEVEL_FIRST);
    //when
    plate = licensePlateRepository.save(plate);
    //then
    // First version number is  -- comment : if you want to comment something , include it into assert fail message
    assertEquals("version should be 1 for just created LicensePlate ", 1, plate.getVersion());
}

@Test
public void check_that_version_is_increased_when_changing_some_auditable_data() {
    //given
    plate = createLicensePlateWithDefaultCountry(PLATE_NUMBER_FIRST, ENGINE_EURO_LEVEL_FIRST);
    plate = licensePlateRepository.save(plate);
    //when
    setPlateEngineEuroLevelAndLicensePlate(PLATE_NUMBER_SECOND,ENGINE_EURO_LEVEL_SECOND);
    plate = licensePlateRepository.save(plate);
    //then
    assertEquals("version should be increased when Changing some auditable data",2, plate.getVersion());

    // Check if the entry in the database has updated data
    LicensePlate plateInDB = licensePlateRepository.findOne(plate.getId());
    assertNotNull(".. explanation..." , plateInDB);
    assertPlateNumberAndEngineEuroLevelAsInPlate(PLATE_NUMBER_SECOND , ENGINE_EURO_LEVEL_SECOND, plateInDB);
}

@Test
public void verify_revisions_after_two_changes() {
    //given
    plate = createLicensePlateWithDefaultCountry(PLATE_NUMBER_FIRST, ENGINE_EURO_LEVEL_FIRST);
    plate = licensePlateRepository.save(plate);
    //when
    setPlateEngineEuroLevelAndLicensePlate(PLATE_NUMBER_SECOND,ENGINE_EURO_LEVEL_SECOND);
    plate = licensePlateRepository.save(plate);
    //then
    Revisions<Integer, LicensePlate> revisions = licensePlateRepository.findRevisions(plate.getId());

    assertNotNull(".....something like : revisions should be found...." ,revisions);
    assertNotNull(".....something like : revisions.getContent should be found...." ,revisions.getContent());
    assertFalse(" should be present content for revisions " ,  revisions.getContent().isEmpty());
    assertEquals("I should have 2 revisions" ,2, revisions.getContent().size());

    //!!!! if you sure about order !!!!
    Revision<Integer, LicensePlate> revFirst = revisions.getContent().get(0);
    Revision<Integer, LicensePlate> revSecond = revisions.getContent().get(1);

    assertPlateNumberAndEngineEuroLevelAsInPlate(PLATE_NUMBER_FIRST, ENGINE_EURO_LEVEL_FIRST, revFirst.getEntity());
    assertPlateNumberAndEngineEuroLevelAsInPlate(PLATE_NUMBER_SECOND , ENGINE_EURO_LEVEL_SECOND, revSecond.getEntity());

}

private void assertPlateNumberAndEngineEuroLevelAsInPlate(String plateNumber , int engineEuroLevel , LicensePlate plate){
    assertEquals(engineEuroLevel, plate.getEngineEuroLevel());
    assertEquals(plateNumber , plate .getLicensePlate());
}

private LicensePlate createLicensePlateWithDefaultCountry(String plateNumber , int engineEuroLevel){
    LicensePlate plate = new LicensePlate();
    setPlateEngineEuroLevelAndLicensePlate(plateNumber , engineEuroLevel);
    plate.setCoutry(countryRepository.findFirstByOrderByIdAsc());
    return plate;
}

private void setPlateEngineEuroLevelAndLicensePlate(String plateNumber , int engineEuroLevel){
    plate.setEngineEuroLevel(engineEuroLevel);
    plate.setLicensePlate(plateNumber);
}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM