Integration tests

Integration tests are checking if your microservices are communicating properly and they are doing their job fine. That means during this type of testing your code is communicating with microservices running on JLupin Platform. It is including using built-in load balancers and all other JLupin Platform features. This is making integration tests similar as possible to system running on production. Test class will call service running on JLupin Platform as an outer client.

Figure 1. Test client calling microservice.

Example integration test with implementation is shown below:

example-b

Service interface:

package com.example.service.interfaces;

public interface BService {
    String callB(String input);
}

Service implementation:

package com.example.service.impl;

import com.example.service.interfaces.BService;
import org.springframework.stereotype.Service;

@Service(value = "bService")
public class BServiceImpl implements BService {
    @Override
    public String callB(String input) {
        return "[called B]" + input;
    }
}

Spring configuration file:

package com.example.configuration;

import com.jlupin.impl.client.util.JLupinClientUtil;
import com.jlupin.interfaces.client.delegator.JLupinDelegator;
import com.jlupin.interfaces.common.enums.PortType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;

@Configuration
@ComponentScan("com.example")
public class ExampleBSpringConfiguration {
    @Bean
    public JLupinDelegator getJLupinDelegator() {
        return JLupinClientUtil.generateInnerMicroserviceLoadBalancerDelegator(PortType.JLRMC);
    }


    @Bean(name = "jLupinRegularExpressionToRemotelyEnabled")
    public List getRemotelyBeanList() {
        List<String> list = new ArrayList<>();
        list.add("bService");
        // list.add("<REMOTE_SERVICE_NAME>");
        return list;
    }
}

JLupin Platform configuration file:

package com.example.configuration;

import com.jlupin.impl.container.application.spring.JLupinAbstractSpringApplicationContainer;
import com.jlupin.interfaces.configuration.microservice.container.application.JLupinAbstractApplicationContainerProducer;
import com.jlupin.interfaces.container.application.JLupinApplicationContainer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

public class ExampleBJLupinConfiguration extends JLupinAbstractApplicationContainerProducer {
    @Override
    public JLupinApplicationContainer produceJLupinApplicationContainer() {
        return new JLupinAbstractSpringApplicationContainer() {
            @Override
            public AbstractApplicationContext getAbstractApplicationContext() {
                return new AnnotationConfigApplicationContext(ExampleBSpringConfiguration.class);
            }
        };
    }
}

Compile all above classes and pack them into jar archive. Then create example-b directory somewhere. This will be the name of the microservice. Put inside newly created jar archive and microservice configuration file. Example configuration is shown below. If you want to know more about configuration microservices please folow here.

Microservice configuration.yml file:

SERVERS:
  JLRMC: #JLupin Remote Method Calls Fast Protocol:
    readTimeout: 480000
    isWaitForFinishExecuteAllRequests: true
    waitToShutdownThreadsOnStop: 60000
    backlog: 0
    receiveBufferSize: 0
    isReuseAddress: false
    threadPoolSize: 128
    isLogPeriodicOnDebug: true
    isDestroyThreadOnTimeout: false
    threadExecutingTimeOut: 240000
    isStartOnMainServerInitialize: true
  TRANSMISSION:
    readTimeout: 480000
    isWaitForFinishExecuteAllRequests: false
    waitToShutdownThreadsOnStop: 60000
    backlog: 0
    receiveBufferSize: 0
    isReuseAddress: false
    threadPoolSize: 8
    isLogPeriodicOnDebug: true
    isDestroyThreadOnTimeout: false
    threadExecutingTimeOut: 3600000
    isStartOnMainServerInitialize: true
  QUEUE:
    readTimeout: 480000
    isWaitForFinishExecuteAllRequests: true
    waitToShutdownThreadsOnStop: 60000
    backlog: 1024
    receiveBufferSize: 1024
    isReuseAddress: false
    threadPoolSize: 128
    isLogPeriodicOnDebug: true
    isDestroyThreadOnTimeout: false
    threadExecutingTimeOut: 240000
    isStartOnMainServerInitialize: true
ENTRY_POINTS:
  QUEUE:
    threadAmount: 512
    howOftenCheckingServerInMillis: 5000
    repeatsAmount: 4
    timeToWaitBetweenRepeatProbeInMillis: 1000
TRANSMISSION:
  MICROSERVICES_GRANT_ACCESS:
    MICROSERVICES_LIST:
      #- microserviceName: 'sampleMicroservice'
      #  serviceName: 'sampleServiceName'
      #  methodName: 'sampleMethodName'
      #- microserviceName: 'sampleMicroservice2'
      #  serviceName: 'sampleServiceName2'
      #  methodName: 'sampleMethodName2'
PROPERTIES:
  #jvmOptions1: '-Xms128M -Xmx256M -agentlib:jdwp=transport=dt_socket,address=12998,server=y,suspend=n'
  jvmOptions1: '-Xms64M -Xmx128M' #jvmOptions_2 - default the same as jvmOptions_1
  #jvmOptions2: '-Xms128M -Xmx256M'
  externalPort: '8000'
  version: '1.5.0.0'
  switchDelayTime: 0
  connectionSocketTimeoutInMillis: 1000
  readTimeoutInMillis: 30000
  isKeepAlive: false
  isOOBInline: false
  isTcpNoDelay: false
  isReuseAddress: false
  sendBufferSize: 0
  receiveBufferSize: 0
  soLinger: 0
  trafficClass: 0
  #javaExecutablePath: 'c:\\jvm\\bin\\java.exe'
  #additionalClassPath: 'c:\\temp\\*'
  isStartOnMainServerInitialize: true
  priorityStartOnMainServerInitialize: 4
  waitForProcessInitResponseTimeInMillis: 90000
  waitForProcessStartResponseTimeInMillis: 90000
  waitForProcessDestroyResponseTimeInMillis: 30000
  isAllFilesToJVMAppClassLoader: false
  isArchiveOnStart: false
  startLogMode: INFO
  isInitErrorCauseWithNetworkInformation: true
  isJmxEnabled: true
  jmxOptions: '-Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false'
  jmxPrimaryPort: -1
  jmxSecondaryPort: -1
  checkAvailableScript: 'function isAvailable(checkResponseTimeInMillis, jrmcActiveThreads, jrmcMaxThreads,
                                              queueActiveThreads, queueMaxThreads, servletActiveThreads, servletMaxThreads,
                                              jvmMaxMemoryInBytes, jvmTotalMemoryInBytes, jvmFreeMemoryInBytes,
                                              jvmProcessCpuLoadInPercentage, userAvailableFlag) {
                          var isAvailableByUser = Boolean(userAvailableFlag);
                            if(checkResponseTimeInMillis > 20000 || !isAvailableByUser) {
                               return false;
                            }
                            return true;
                         }'
APPLICATION:
  applicationContainerProducerClassName: 'com.example.configuration.ExampleBJLupinConfiguration'
INITIALIZING_LOGGER:
  #directoryPath: '/logs/server'
  #fileName: 'file_name'
  fileExtension: 'log'
  fileSizeInMB: 20
  maxFiles: 10
MEMORY_ERRORS:
  isRestartOnError: true
  howManyTimes: 4
  percentageGrowth: 15
  isHeapDump: true
THREAD_POOLS:
  THREAD_POOL_1:
    size: 8
    waitingTimeForTasksCompletionInMillis: 10000
  #THREAD_POOL_2:
  #  size: 8
  #  waitingTimeForTasksCompletionInMillis: 10000

You can also include here configuration file for Log4j2 (log4j2.xml):

<?xml version="1.0" encoding="UTF-8"?>

<!-- ===================================================================== -->
<!--                                                                       -->
<!--  Log4j2 Configuration                                                  -->
<!--                                                                       -->
<!-- ===================================================================== -->

<!--
   | For more configuration information and examples see the Apache Log4j2
   | website: https://logging.apache.org/log4j/2.x/index.html
-->

<Configuration status="WARN">
    <!-- Extract log directory and file name into variables -->
    <Properties>
        <Property name="logDirectory">../logs/microservice/example-b</Property>
        <Property name="logFileName">microservice</Property>
    </Properties>

    <Appenders>
        <!-- RollingFileAppender configured to role every day -->
        <RollingFile name="FILE">
            <FileName>${logDirectory}/${logFileName}.log</FileName>
            <FilePattern>${logDirectory}/${logFileName}.%d{yyyy-MM-dd}.log</FilePattern>

            <!-- Compress log files to gzip -->
            <!-- More configuration https://logging.apache.org/log4j/2.x/manual/appenders.html#DefaultRolloverStrategy -->
            <!-- <FilePattern>${logDirectory}/${logFileName}.%d{yyyy-MM-dd}.log.gz</FilePattern> -->

            <!-- Do not truncate file -->
            <Append>true</Append>

            <!-- The default pattern: Date Priority [Category] (Thread) Message\n -->
            <PatternLayout pattern="%d %-5p [%c] (%t) %m%n" />

            <Policies>
                <!-- Rollover every microservice start - very useful for debugging -->
                <!-- <OnStartupTriggeringPolicy /> -->

                <!-- Rollover at the top of each day -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />

                <!-- Rollover if file size is greater than 200 MB -->
                <!-- <SizeBasedTriggeringPolicy size="200 MB"/> -->
            </Policies>

            <!-- Keep last 10 log files -->
            <!-- More configuration https://logging.apache.org/log4j/2.x/manual/appenders.html#DefaultRolloverStrategy -->
            <!-- <DefaultRolloverStrategy max="10" /> -->
        </RollingFile>

        <!-- AsyncAppender for high performance -->
        <Async name="ASYNC_FILE">
            <BufferSize>1000</BufferSize>
            <AppenderRef ref="FILE" />
        </Async>
    </Appenders>

    <Loggers>
        <!-- Setup for root logger with AsyncAppender -->
        <Root level="info">
            <AppenderRef ref="ASYNC_FILE" />
        </Root>
    </Loggers>
</Configuration>

Now pack your example-b directory into zip file example-b.zip (inside zip at top level there should be visible example-b directory), start JLupin Platform (start.sh/start.cmd) and run control (control.sh/control.cmd). Copy created zip file example-b.zip into upload directory. Then from control window run command microservice upload example-b.zip. Your microservice will be uploaded to server and started. You can significantly speed up this process with our JCS JLupin Platform Maven Plugin and also with our JCS JLupin Platform IntelliJ Plugin.

example-a

Service interface:

package com.example.service.interfaces;


public interface AService {
    String callA(String input);
}

Service implementation:

package com.example.service.impl;

import com.example.service.interfaces.AService;
import com.example.service.interfaces.BService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service(value = "aService")
public class AServiceImpl implements AService {
    @Autowired
    private BService bService;

    @Override
    public String callA(String input) {
        return "[called A]" + bService.callB(input);
    }
}

Spring configuration file:

package com.example.configuration;

import com.example.service.interfaces.BService;
import com.jlupin.impl.client.util.JLupinClientUtil;
import com.jlupin.interfaces.client.delegator.JLupinDelegator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;

@Configuration
@ComponentScan("com.example")
public class ExampleASpringConfiguration {

    @Bean
    public JLupinDelegator getJLupinDelegator() {
        return JLupinClientUtil.generateInnerMicroserviceLoadBalancerDelegator(PortType.JLRMC);
    }
    @Bean(name = "bService")
    public BService getExampleService() {
        return JLupinClientUtil.generateRemote(getJLupinDelegator(), "example-b", "bService", BService.class);
    }

    @Bean(name = "jLupinRegularExpressionToRemotelyEnabled")
    public List getRemotelyBeanList() {
        List<String> list = new ArrayList<>();
        list.add("aService");
        // list.add("<REMOTE_SERVICE_NAME>");
        return list;
    }
}

JLupin Platform configuration file:

package com.example.configuration;

import com.jlupin.impl.container.application.spring.JLupinAbstractSpringApplicationContainer;
import com.jlupin.interfaces.configuration.microservice.container.application.JLupinAbstractApplicationContainerProducer;
import com.jlupin.interfaces.container.application.JLupinApplicationContainer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

public class ExampleAJLupinConfiguration extends JLupinAbstractApplicationContainerProducer {
    @Override
    public JLupinApplicationContainer produceJLupinApplicationContainer() {
        return new JLupinAbstractSpringApplicationContainer() {
            @Override
            public AbstractApplicationContext getAbstractApplicationContext() {
                return new AnnotationConfigApplicationContext(ExampleASpringConfiguration.class);
            }
        };
    }
}

Compile all above classes and pack them into jar archive. Then create example-a directory somewhere. This will be the name of the microservice. Put inside newly created jar archive and microservice configuration file. Example configuration is shown below. If you want to know more about configuration microservices please folow here.

Microservice configuration.yml file:

SERVERS:
  JLRMC: #JLupin Remote Method Calls Fast Protocol
    readTimeout: 480000
    isWaitForFinishExecuteAllRequests: true
    waitToShutdownThreadsOnStop: 60000
    backlog: 0
    receiveBufferSize: 0
    isReuseAddress: false
    threadPoolSize: 128
    isLogPeriodicOnDebug: true
    isDestroyThreadOnTimeout: false
    threadExecutingTimeOut: 240000
  TRANSMISSION:
    readTimeout: 480000
    isWaitForFinishExecuteAllRequests: false
    waitToShutdownThreadsOnStop: 60000
    backlog: 0
    receiveBufferSize: 0
    isReuseAddress: false
    threadPoolSize: 8
    isLogPeriodicOnDebug: true
    isDestroyThreadOnTimeout: false
    threadExecutingTimeOut: 3600000
  QUEUE:
     readTimeout: 480000
     isWaitForFinishExecuteAllRequests: true
     waitToShutdownThreadsOnStop: 60000
     backlog: 0
     receiveBufferSize: 0
     isReuseAddress: false
     threadPoolSize: 128
     isLogPeriodicOnDebug: true
     isDestroyThreadOnTimeout: false
     threadExecutingTimeOut: 240000
PROPERTIES:
  #jvmOptions1: '-Xms128M -Xmx256M -agentlib:jdwp=transport=dt_socket,address=12998,server=y,suspend=n'
  jvmOptions1: '-Xms128M -Xmx256M' #jvmOptions_2 - default the same as jvmOptions_1
  #jvmOptions2: '-Xms128M -Xmx256M'
  switchDelayTime: 0
  connectionSocketTimeoutInMillis: 1000
  readTimeoutInMillis: 30000
  expectedCheckResponseTimeInMillis: 2000
  isKeepAlive: false
  isOOBInline: false
  isTcpNoDelay: false
  isReuseAddress: false
  sendBufferSize: 0
  receiveBufferSize: 0
  soLinger: 0
  trafficClass: 0
  #javaExecutablePath: 'c:\\jvm\\bin\\java.exe'
  #additionalClassPath: 'c:\\temp\\*'
  isStartOnMainServerInitialize: true
  priorityStartOnMainServerInitialize: 4
  waitForProcessInitResponseTimeInMillis: 90000
  waitForProcessStartResponseTimeInMillis: 90000
  waitForProcessDestroyResponseTimeInMillis: 30000
  isAllFilesToJVMAppClassLoader: false
  isArchiveOnStart: false
  startLogMode: INFO
  isInitErrorCauseWithNetworkInformation: true
  checkAvailableScript: 'function isAvailable(checkResponseTimeInMillis, jrmcActiveThreads, jrmcMaxThreads,
                                              queueActiveThreads, queueMaxThreads, servletActiveThreads, servletMaxThreads,
                                              jvmMaxMemoryInBytes, jvmTotalMemoryInBytes, jvmFreeMemoryInBytes,
                                              jvmProcessCpuLoadInPercentage, userAvailableFlag) {
                          var isAvailableByUser = Boolean(userAvailableFlag);
                            if(checkResponseTimeInMillis > 20000 || !isAvailableByUser) {
                               return false;
                            }
                            return true;
                         }'
APPLICATION:
  applicationContainerProducerClassName: 'com.example.configuration.ExampleAJLupinConfiguration'
INITIALIZING_LOGGER:
  #directoryPath: '/logs/server'
  #fileName: 'file_name'
  fileExtension: 'log'
  fileSizeInMB: 20
  maxFiles: 10
MEMORY_ERRORS:
  isRestartOnError: true
  howManyTimes: 4
  percentageGrowth: 15
  isHeapDump: true
THREAD_POOLS:
  THREAD_POOL_1:
    size: 8
    waitingTimeForTasksCompletionInMillis: 10000
  #THREAD_POOL_2:
  #  size: 8
  #  waitingTimeForTasksCompletionInMillis: 10000

You can also include here configuration file for Log4j (log4j.xml):

<?xml version="1.0" encoding="UTF-8"?>

<!-- ===================================================================== -->
<!--                                                                       -->
<!--  Log4j2 Configuration                                                  -->
<!--                                                                       -->
<!-- ===================================================================== -->

<!--
   | For more configuration information and examples see the Apache Log4j2
   | website: https://logging.apache.org/log4j/2.x/index.html
-->

<Configuration status="WARN">
    <!-- Extract log directory and file name into variables -->
    <Properties>
        <Property name="logDirectory">../logs/microservice/example-a</Property>
        <Property name="logFileName">microservice</Property>
    </Properties>

    <Appenders>
        <!-- RollingFileAppender configured to role every day -->
        <RollingFile name="FILE">
            <FileName>${logDirectory}/${logFileName}.log</FileName>
            <FilePattern>${logDirectory}/${logFileName}.%d{yyyy-MM-dd}.log</FilePattern>

            <!-- Compress log files to gzip -->
            <!-- More configuration https://logging.apache.org/log4j/2.x/manual/appenders.html#DefaultRolloverStrategy -->
            <!-- <FilePattern>${logDirectory}/${logFileName}.%d{yyyy-MM-dd}.log.gz</FilePattern> -->

            <!-- Do not truncate file -->
            <Append>true</Append>

            <!-- The default pattern: Date Priority [Category] (Thread) Message\n -->
            <PatternLayout pattern="%d %-5p [%c] (%t) %m%n" />

            <Policies>
                <!-- Rollover every microservice start - very useful for debugging -->
                <!-- <OnStartupTriggeringPolicy /> -->

                <!-- Rollover at the top of each day -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />

                <!-- Rollover if file size is greater than 200 MB -->
                <!-- <SizeBasedTriggeringPolicy size="200 MB"/> -->
            </Policies>

            <!-- Keep last 10 log files -->
            <!-- More configuration https://logging.apache.org/log4j/2.x/manual/appenders.html#DefaultRolloverStrategy -->
            <!-- <DefaultRolloverStrategy max="10" /> -->
        </RollingFile>

        <!-- AsyncAppender for high performance -->
        <Async name="ASYNC_FILE">
            <BufferSize>1000</BufferSize>
            <AppenderRef ref="FILE" />
        </Async>
    </Appenders>

    <Loggers>
        <!-- Setup for root logger with AsyncAppender -->
        <Root level="info">
            <AppenderRef ref="ASYNC_FILE" />
        </Root>
    </Loggers>
</Configuration>

Now pack your example-a directory into zip file example-a.zip (inside zip at top level there should be visible example-a directory), start JLupin Platform (start.sh/start.cmd) and run control (control.sh/control.cmd). Copy created zip file example-a.zip into upload directory. Then from control window run command microservice upload example-a.zip. Your microservice will be uploaded to server and started. You can significantly speed up this process with our JCS JLupin Platform Maven Plugin and also with our JCS JLupin Platform IntelliJ Plugin.

integration-test

At first create base class for tests where you will provide servers where microservice is deployed for testing:

package com.example.base;

import com.jlupin.common.communication.common.various.JLupinMainServerInZoneConfiguration;
import com.jlupin.impl.client.util.JLupinClientUtil;
import com.jlupin.impl.logger.impl.log4j.JLupinLoggerOverLog4jImpl;
import com.jlupin.impl.serialize.JLupinFSTSerializerImpl;
import com.jlupin.interfaces.client.delegator.JLupinDelegator;
import com.jlupin.interfaces.common.enums.PortType;
import com.jlupin.interfaces.logger.JLupinLogger;
import com.jlupin.interfaces.serialize.JLupinSerializer;
import org.junit.Before;

public abstract class BaseTest {
    private final JLupinDelegator jLupinDelegator;

    public BaseTest() {
        jLupinDelegator = JLupinClientUtil.generateOuterMicroserviceLoadBalancerDelegator(
                PortType.JLRMC,
                new JLupinMainServerInZoneConfiguration[]{
                        new JLupinMainServerInZoneConfiguration("NODE_1", "127.0.0.1", 9090, 9095, 9096, 9097)
                }
        );
    }

    public JLupinDelegator getJLupinDelegator() {
        return jLupinDelegator;
    }
}

Now you can define your microservice test:

package com.example;

import com.example.base.BaseTest;
import com.example.service.interfaces.AService;
import com.jlupin.impl.client.util.JLupinClientUtil;
import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class ExampleATest extends BaseTest {
    private AService getAService() {
        return JLupinClientUtil.generateRemote(getJLupinDelegator(), "example-a", "aService", AService.class);
    }

    @Test
    public void callATest() {
        AService aService = getAService();
        assertEquals("[called A][called B]input", aService.callA("input"));
    }
}

And you can configure Log4j2 to log to standard out. Put this file (log4j2.xml) into resources:

<?xml version="1.0" encoding="UTF-8"?>

<!-- ===================================================================== -->
<!--                                                                       -->
<!--  Log4j2 Configuration                                                  -->
<!--                                                                       -->
<!-- ===================================================================== -->

<!--
   | For more configuration information and examples see the Apache Log4j2
   | website: https://logging.apache.org/log4j/2.x/index.html
-->

<Configuration status="WARN">
    <Appenders>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <!-- The default pattern: Date Priority [Category] (Thread) Message\n -->
            <PatternLayout pattern="%d %-5p [%c] (%t) %m%n"/>
        </Console>

        <!-- AsyncAppender for high performance -->
        <Async name="ASYNC_STDOUT">
            <BufferSize>1000</BufferSize>
            <AppenderRef ref="STDOUT"/>
        </Async>
    </Appenders>

    <Loggers>
        <!-- Setup for root logger with AsyncAppender -->
        <Root level="info">
            <AppenderRef ref="ASYNC_STDOUT"/>
        </Root>
    </Loggers>
</Configuration>

As said before microservice is called as from outer client (see base test constructor and line jLupinDelegator = JLupinClientUtil.generateOuterMicroserviceLoadBalancerDelegator().

Test execution

Before running test make sure that you microservices (example-a and example-b) are deployed on server pointed by your base test class. Then execute callATest and see result.

JUnit version 4.12
.2017-09-26 12:00:23,930 INFO  [com.jlupin.impl.balancer.ext.impl.roundrobin.JLupinRoundRobinLoadBalancerImpl$1] (pool-2-thread-1)  MESSAGE ID:52 METHOD:call LINE:83 LOG:compute current node thread started correctly
2017-09-26 12:00:23,930 INFO  [com.jlupin.impl.balancer.base.JLupinBaseLoadLoadBalancer$2] (pool-1-thread-1)  MESSAGE ID:142 METHOD:call LINE:509 LOG:microservice discovery control thread started correctly

Time: 0,731

OK (1 test)