Mock Projection Result Spring Data JPA
Asked Answered
L

5

16

I am using spring data jpa in my spring boot project.

I am firing an JPQL query and using an projection to store the result of query.

My Projection :

public interface VeryBasicProjection {
    String getTitle();
    String getUrl();
}

My service calling this projection :

public List<VeryBasicDTO> getLatestData(int limit){

    // Pageable for Limit
    Pageable pageable = new PageRequest(0, limit);

    // Get Data from DB
    List<VeryBasicProjection> latestData = tableRepository.getLatestData("live", 2,pageable);
    List<VeryBasicDTO> responseDTO = new ArrayList<>();

    // Map Projection to DTO
    for(VeryBasicProjection veryBasicProjection : latestData){
        VeryBasicDTO veryBasicDTO = new VeryBasicDTO();
        veryBasicDTO.buildDTO(veryBasicProjection);
        responseDTO.add(veryBasicDTO);
    }

    return responseDTO;
}

Now I want to test this service using Mockito(Unit Test Case) I am mocking the call to repository using when and thenReturn.

My question is how do I mock the result of repository? What should be in thenReturn? I mean how do I create instance of projection and setData to it?

London answered 13/11, 2017 at 6:13 Comment(1)
Create a class implementing the interface, or use Mockito to create mock instances of the interface.Husted
C
28

If you want to create an instance of your projection without creating a class implementing the interface, you can use SpelAwareProxyProjectionFactory.

import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
    
// ...
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
VeryBasicProjection projection = factory.createProjection(VeryBasicProjection.class);
projection.setTitle("theTitle");
projection.setUrl("theUrl");

You also need to add setters in your projection:

public interface VeryBasicProjection {
    String getTitle();
    String getUrl();
    void setTitle(String title);
    void setUrl(String url);
}

Source: https://github.com/spring-projects/spring-data-examples/blob/master/rest/projections/src/test/java/example/springdata/rest/projections/SimpleProjectionTests.java

Cimbura answered 6/9, 2018 at 13:18 Comment(2)
you missed this part ProjectionFactory factory = new SpelAwareProxyProjectionFactory();Visage
Thank you, I have added it.Cimbura
B
16

This is the simplest way to mock projections in TEST

VeryBasicProjection VeryBasicProjection = new VeryBasicProjection() {
    String getTitle() {
        return "Title";
    }

    String getUrl() {
        return "url";
    }
};
Billiebilling answered 19/9, 2019 at 8:45 Comment(0)
P
8

As an addition to Nis' answer:

If the interface doesn't have setters, the projection can be initialized with a map:

ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
Map<String, String> map = Map.of(
        "title", "theTitle",
        "url", "theUrl"
);
VeryBasicProjection projection = factory.createProjection(VeryBasicProjection.class, map);
Petry answered 23/6, 2021 at 20:21 Comment(1)
Doesn't compileNidify
I
2

We have implemented the same stuff in below way

First mocked the two type of objects:

@Mock
private EntityManager em;
    
@Mock
private DemoProjectRepo demoProjectRepo;

My demoProjectRepo.findByAll returns List<DemoProjectDevices>

DemoProjectDevices device1 = new DemoProjectDevices();
device1.setAcctNbr("2365897412236589");
device1.setdeviceSeq(new BigDecimal(1));
device1.setCrteTms("2017-07-29 01:21:44.910807");

List<DemoProjectDevices> demoProjectDevices = new ArrayList<DemoProjectDevices>();
demoProjectDevices.add(device1);

For the mock when and thenReturn:

Mockito.when(demoProjectRepo.findByAll("2365897412236589", em))
        .thenReturn(demoProjectDevices);
Impressive answered 13/11, 2017 at 8:24 Comment(0)
A
1

In your projection interface you need to add setters for the values you have getters for.

So when you have a concrete class implementing the projection interface you can add the values to that class so you might have something along these lines:

    public interface VeryBasicProjection {
        String getTitle();
        String getUrl();
        void setTitle(String title);
        void setUrl(String url);
    }

    public class VeryBasicProjectionImpl implements VeryBasicProjection{
        //add implementing methods
    }

    ////

    @Mock
    Repository tableRepo;

    @InjectMocks
    ClassUnderTest c;

    @Test
    public void test(){

        // Pageable for Limit
        Pageable pageable = new PageRequest(0, limit);

        VeryBasicProjection vbp = new VeryBasicProjectionImpl();
        // add data to object here using the setters

        List<VeryBasicProjection> projList = new ArrayList<>()
        //add objects created

        when(tableRepo.getLatestData("live", 2, pageable)).thenReturn(projList));

    }
Aldarcy answered 11/5, 2018 at 9:2 Comment(1)
You don't need to pollute the projection interface with setter methods. Instead, define them in your implementation class.Invar

© 2022 - 2024 — McMap. All rights reserved.