15

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?

homersimpson
  • 4,124
  • 4
  • 29
  • 39
Ankit Bansal
  • 2,162
  • 8
  • 42
  • 79
  • 1
    Create a class implementing the interface, or use Mockito to create mock instances of the interface. – JB Nizet Nov 13 '17 at 07:08

5 Answers5

25

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

homersimpson
  • 4,124
  • 4
  • 29
  • 39
Nis
  • 560
  • 5
  • 18
13

This is the simplest way to mock projections in TEST

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

    String getUrl() {
        return "url";
    }
};
homersimpson
  • 4,124
  • 4
  • 29
  • 39
IZI_SL
  • 135
  • 1
  • 5
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);
homersimpson
  • 4,124
  • 4
  • 29
  • 39
Fabian
  • 466
  • 5
  • 6
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);
homersimpson
  • 4,124
  • 4
  • 29
  • 39
Milan Thummar
  • 261
  • 1
  • 2
  • 11
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));

    }
Arturo Volpe
  • 3,442
  • 3
  • 25
  • 40
dg100500
  • 11
  • 1
  • 1
    You don't need to pollute the projection interface with setter methods. Instead, define them in your implementation class. – Mark Norman Apr 02 '19 at 01:35