Showing posts with label Spring Boot. Show all posts
Showing posts with label Spring Boot. Show all posts

16 May 2020

Spring Boot Reactive Testing Part 1



I was worked in spring 4 in the last couple of years. Then I thought to learn reactive spring using spring 5. Then I thought to write an article that I have discovered new in reactive programming testing.

Here I will not explain how to write reactive rest endpoints. Here It will explain how to test different kinds of scenarios. In this thread I will explain how to test data repository & reactive API endpoint. Next tutorial I will explain how to test reactive client

It is like same in synchronous mongo client. First write repository & then using that repository we can write a simple JUnit test. Here I have used StepVerifier to validate the response.

import dev.innova.mockito.mockitoserver.domain.UserData;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.test.context.junit4.SpringRunner;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;

@DataMongoTest
@RunWith(SpringRunner.class)
public class UserDataRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void testUserQuery() {

        Flux<UserData> userDataFlux = this.userRepository.deleteAll()
                .thenMany(Flux.just("sajith", "sajith", "sajith2", "sajuth3")
                .flatMap(item -> this.userRepository.save(new UserData(null, item, item)))
                .thenMany(this.userRepository.findByName("sajith")));

        StepVerifier.create(userDataFlux)
                .expectNextCount(2)
                .verifyComplete();
    }
}

As the first step I have created a User Resource configuration. It will return the user list.

import dev.innova.mockito.mockitoserver.domain.UserData;
import dev.innova.mockito.mockitoserver.repository.UserRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.web.reactive.function.server.RequestPredicates.GET;

@Configuration
public class UserResourceConfiguration {

    @Bean
    RouterFunction<ServerResponse> routes(UserRepository userRepository) {
        return RouterFunctions.route(GET("/allUsers"), request -> ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON).body(userRepository.findAll(), UserData.class));
    }
}


Then using mockito we can mock the object & methods. Then using WebTestClient test case like below.


import dev.innova.mockito.mockitoserver.config.UserResourceConfiguration;
import dev.innova.mockito.mockitoserver.domain.UserData;
import dev.innova.mockito.mockitoserver.facade.ClassifiedService;
import dev.innova.mockito.mockitoserver.repository.UserRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Flux;

@WebFluxTest
@Import(UserResourceConfiguration.class)
@RunWith(SpringRunner.class)
public class AddsRestApiTest {

    @MockBean
    private UserRepository userRepository;

    @Autowired
    private WebTestClient webTestClient;

    @MockBean
    private ClassifiedService classifiedService;

    @Test
    public void getAllAddsTest() throws Exception {

        UserData add1 = new UserData();
        add1.setId("001");
        add1.setName("Sample1");

        UserData add2 = new UserData();
        add2.setId("002");
        add2.setName("Sample2");

        UserData add3 = new UserData();
        add3.setId("003");
        add3.setName("Sample3");

        Mockito.when(this.userRepository.findAll())
                .thenReturn(Flux.just(add1, add2, add3));

        this.webTestClient.get()
                .uri("http://localhost:8084/allUsers")
                .exchange()
                .expectStatus().isOk()
                .expectBody().jsonPath("@.[0].id").isEqualTo("001");

    }
}

Code base available in here.If you have any suggestions or questions put comments here.

27 May 2018

Spring JPA/Data with Postgres Array Types


Last couple of weeks I have to work with spring boot pet project. This solution for one of the difficulty faced with postgres string array with spring JPA. The problem vs It Always failed to map the domain class array list with postgress text array. This Solution is common for any type of Arrays in postgres database.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package com.example.mytastyserver.util;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;

import java.io.Serializable;
import java.sql.*;

public class GenericArrayUserType<T extends Serializable> implements UserType {
    protected static final int[] SQL_TYPES = {Types.ARRAY};
    private Class<T> typeParameterClass;

    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return this.deepCopy(cached);
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (T) this.deepCopy(value);
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {

        if (x == null) {
            return y == null;
        }
        return x.equals(y);
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }

    @Override
    public Object nullSafeGet(ResultSet resultSet, String[] names, SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException, SQLException {
        if (resultSet.wasNull()) {
            return null;
        }
        if (resultSet.getArray(names[0]) == null) {
            return new Integer[0];
        }

        Array array = resultSet.getArray(names[0]);
        @SuppressWarnings("unchecked")
        T javaArray = (T) array.getArray();
        return javaArray;
    }

    @Override
    public void nullSafeSet(PreparedStatement statement, Object value, int index, SharedSessionContractImplementor sharedSessionContractImplementor) throws HibernateException, SQLException {
        Connection connection = statement.getConnection();
        if (value == null) {
            statement.setNull(index, SQL_TYPES[0]);
        } else {
            @SuppressWarnings("unchecked")
            T castObject = (T) value;
            Array array = connection.createArrayOf("integer", (Object[]) castObject);
            statement.setArray(index, array);
        }
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }

    @Override
    public Class<T> returnedClass() {
        return typeParameterClass;
    }

    @Override
    public int[] sqlTypes() {
        return new int[]{Types.ARRAY};
    }
}

Then you can add the type of the entity class.


1
2
@Type(type = "com.example.mytastyserver.util.GenericArrayUserType")
private String[] tags;

Hope this will helps to resolve your issue. Thanks