I am trying to write a test for my service that sets up a connection with another service that returns me items from the database. My problem is that I set the connection properties in the test and start the service. How can this be mock or something similar?
My start service method:
public void doStartService() {
super.doStartService();
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
manager.setDefaultMaxPerRoute(maxConnectionsPerRoute);
manager.setMaxTotal(maxConnections);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(connectTimeout)
.setSocketTimeout(socketTimeout)
.setRedirectsEnabled(false).build();
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setDefaultRequestConfig(requestConfig);
builder.setConnectionManager(manager);
client = builder.build();
}
My setup test method and one test method:
private ProductCatalogIntegrationService service;
#Before
public void setup() {
service = new Service();
service.setConnectTimeout(10000);
service.setSocketTimeout(10000);
service.setMaxConnections(10);
service.setMaxConnectionsPerRoute(10);
service.setUrl("http://localhost:8888/products");
service.doStartService();
}
#Test
public void testReturnProductById() {
service.setProductById(GET_PRODUCT_BY_ID); // set url from get product by id, by this url my other service goes to the database
jsonItem = service.getProductById("1"); //get product by id 1
assertEquals(jsonItem.getId(), FIRST_PRODUCT_ID); // I compare the id on which I made the request to the database, so that I came and was wrapped in a class wrapper
}
How to do it correctly, so as not to configure the connection in the tests?
Javalin would be an excellent tool for mocking the real service as it allows for state assertions in your tests.
Wiremock can be used as well. But it leads to hard to maintain behavioral tests (verify).
Related
I am using Spring Boot 1.5.x (Spring 4.2.x), and I created a RestClientSdk spring component class as shown here:
#Component
public class RestClientSdkImpl implements RestClientSdk {
private RestTemplate restTemplate;
#Autowired
public RestClientSdkImpl(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
...
//other methods kept out for brevity
}
I have also defined a DefaultRestTemplateCustomizer spring component as shown here:
#Component
public class DefaultRestTemplateCustomizer implements RestTemplateCustomizer {
private LogClientHttpRequestInterceptor logClientHttpRequestInterceptor;
#Autowired
public DefaultRestTemplateCustomizer(LogClientHttpRequestInterceptor logClientHttpRequestInterceptor) {
this.logClientHttpRequestInterceptor = logClientHttpRequestInterceptor;
}
#Override
public void customize(RestTemplate restTemplate) {
restTemplate.getInterceptors().add(logClientHttpRequestInterceptor);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
}
}
With that, I've defined a test class as shown below that uses the #RestClientTest annotation as shown below.
#RunWith(SpringRunner.class)
#RestClientTest(RestClientSdk.class)
#ActiveProfiles("test")
/*
* The RestClientTest only includes the most minimal configuration to include a rest template builder,
* so we include the rest sdk auto config within the scope of the test
*/
#ImportAutoConfiguration(RestSdkAutoConfiguration.class)
public class RestClientApplicationBehaviourTest{
#Autowired
private RestClientSdk restClientSdk;
#Autowired
private MockRestServiceServer mockRestServiceServer;
/**
* A simple Http Get that retrieves a JSON document from a rest server and
* produces a plain old java object as a response.
*/
#Test
public void testPlainHttpGet() throws IOException{
//ARRANGE
RestClientDto<?> simpleGetRequest = simpleHttpGet();
mockRestServiceServer.expect(once(), requestTo("http://localhost:8080/account/1234567890"))
.andRespond(withSuccess(IOUtils.getInputAsString("/stubs/account.json"),MediaType.APPLICATION_JSON));
//ACT
Account account = restClientSdk.send(simpleGetRequest, Account.class);
//ASSERT
mockRestServiceServer.verify();
Assert.assertNotNull(account);
Assert.assertNotNull(account.getAccountId());
Assert.assertNotNull(account.getFirstName());
Assert.assertNotNull(account.getLastName());
}
...
//not including other methods for brevity
}
PROBLEM
Because the MockRestServiceServer builder overrides the BufferingClientHttpRequestFactory in my rest template with a MockClientHttpRequestFactory, I am getting a null response from my body. This is because the logging interceptor is reading the input stream coming from the response and as such the stream no longer has content to read. The BufferingClientHttpRequestFactory would prevent that from happening. Now, I know that as of Spring 5.0.5, there is an extra option in the MockRestServiceServer builder called bufferContent, but I don't have the option of moving to Spring 5.x (Spring Boot 2.x), so I was wondering if there is a way to get this configured using Spring Boot 1.5.x / Spring 4.2.x.
I thank you in advance!
Juan
In order to workaround the issue, I needed to define a test configuration, that would allow me to override the client request factory. Please see the code below. It is a bit hacky, but I suppose the real solution here would be to upgrade to Spring 5.x / Spring Boot 2.x.
#Configuration
#Profile("test")
public class MockRestServiceServerConfiguration {
/**
* Wrap the Mock Rest client factory in the buffered one.
* #param restClientSdk The rest client SDK containing the rest template to use.
* #return The mock rest service server to use.
*/
#Bean
public MockRestServiceServer mockRestServiceServer(RestClientSdkImpl restClientSdkImpl) {
RestTemplate restTemplate = restClientSdkImpl.getRestTemplate();
MockRestServiceServer server = MockRestServiceServer.createServer(restTemplate);
//need to do this because getRequestFactory returns InterceptingHttpRequestFactory wraping the mock rest service server request factory
List<ClientHttpRequestInterceptor> templateInterceptors = restTemplate.getInterceptors();
restTemplate.setInterceptors(null);
//now we wrap the delegate, which should be the mock rest service server request factory
BufferingClientHttpRequestFactory bufferingFact = new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory());
restTemplate.setRequestFactory(bufferingFact);
restTemplate.setInterceptors(templateInterceptors);
return server;
}
}
The protobuf definition is as follows:
syntax = "proto3";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
I need to use Mockito along with JUnit testing.
The encouraged way to test a service is to use the in-process transport and a normal stub. Then you can communicate with the service like normal, without lots of mocking. Overused mocking produces brittle tests that don't instill confidence in the code being tested.
GrpcServerRule uses in-process transport behind-the-scenes. We now I suggest taking a look at the examples' tests, starting with hello world.
Edit: We now recommend GrpcCleanupRule over GrpcServerRule. You can still reference the hello world example.
The idea is to stub the response and stream observer.
#Test
public void shouldTestGreeterService() throws Exception {
Greeter service = new Greeter();
HelloRequest req = HelloRequest.newBuilder()
.setName("hello")
.build();
StreamObserver<HelloRequest> observer = mock(StreamObserver.class);
service.sayHello(req, observer);
verify(observer, times(1)).onCompleted();
ArgumentCaptor<HelloReply> captor = ArgumentCaptor.forClass(HelloReply.class);
verify(observer, times(1)).onNext(captor.capture());
HelloReply response = captor.getValue();
assertThat(response.getStatus(), is(true));
}
I am using an injected OkHttpClient object in my class. I am using method injection to set some interceptors on my client object like following:
#Inject
private OkHttpClient httpClient;
#Inject
void onPostInject() {
httpClient
.newBuilder()
.addInterceptor(loggingInterceptor)
.addInterceptor(httpClientInterceptor);
}
Now if I put a breakpoint in onPostInject method and reach the last statement, I see the size of interceptors collection within the httpClient object is 0. My Integration test is also failing because of the same reason.
I have removed injection from the httpClient field and use the build a bit different to get it worked:
private OkHttpClient httpClient;
#Inject
void onPostInject() {
httpClient =
new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(httpClientInterceptor)
.build();
}
I am using RestTemplate along with its factory HttpComponentsClientHttpRequestFactory in one of my projects. In this project, I need to make a Http url call to my server which is running a restful service which returns back the response as a JSON String.
Below is my code -
public class GetUserClientData {
public String getData(KeyHolder keys) {
return new HTTPRequestAccess(keys).makeHttpRequest();
}
}
Below is my class which wraps the HttpClient part -
public class HTTPRequestAccess {
// should this be static?
private RestTemplate restTemplate;
private KeyHolder keys;
private int timeout;
public HTTPRequestAccess(KeyHolder keys){
this(keys.getTimeoutValue()); // setting timeout to RestTemplate
this.keys = keys;
}
public HTTPRequestAccess(int timeout) {
this.timeout = timeout;
restTemplate = new RestTemplate(clientHttpRequestFactory());
}
private ClientHttpRequestFactory clientHttpRequestFactory() {
// is this not expensive that every time we are creating this new object?
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(timeout);
factory.setConnectTimeout(timeout);
return factory;
}
public String makeHttpRequest() {
String response = null;
try {
// some logic here
String url = generateURL();
response = restTemplate.getForObject(url, String.class);
// some logic here
} catch (RestClientException ex) {
// log exception and do some stuff
} catch (Exception ex) {
// log exception
}
return response;
}
}
Should RestTemplate and HttpComponentsClientHttpRequestFactory be static here in my HTTPRequestAccess class as if I see it correctly, I am recreating the whole connection pool for each request in RestTemplate which is not the right way I guess because each factory has connection and thread pool and they are pretty heavy object I guess.
In general what is the best way to use RestTemplate along with its factory HttpComponentsClientHttpRequestFactory in a multithreading environment? I guess RestTemplate is thread safe but I don't think HttpComponentsClientHttpRequestFactory is thread safe. Correct me if I am wrong? I will be running this library under heavy load.
I am using spring-web-3.2.8.RELEASE version.
In one of my projects, I had created a static instance of HttpComponentsClientHttpRequestFactory and passed it to every RestTemplate.
Though, in here, it is suggested to also have a global instance of RestTemplate.
Maybe irrelevant, but one important point is to pass HttpClients.createDefault() to your HttpComponentsClientHttpRequestFactory while constructing it since by default, this factory uses system properties to create HttpClient for your factory and that may cause a lot of pain in production environment.
You may also pass your custom HttpClient.
How to unit test the below method with the EasyMock. I tried to mock WebResource but it is returning me a NullPointerException.
public void connect()
{
Client client = setUpClient();
WebResource jobBuilder = client.resource("URL");
String jobXml = jobBuilder.accept(MediaType.APPLICATION_XML)
.type(MediaType.APPLICATION_JSON)
.entity(request)
.post(String.class);
}
public Client setUpClient()
{
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(JacksonJsonProvider.class);
Client client = Client.create(cc);
return client;
}
You clearly have to read up on the Inversion of Control Principle (http://en.wikipedia.org/wiki/Inversion_of_control), if not for the sake of the better designed code, then for the sake of unit testing. The client object in the method above is created inside the method itself, using the static factory method Client.create(). There is no good way to inject a mock collaborator with that approach. You should either allow injection of the client via a setter or a constructor, or delegate its creation to some sort of a factory.
If you use the 1st approach, you can inject the mock client directly via the setter or constructor during the unit test setup.
If you use the 2nd approach, you can provide a factory that would return a mock client when called.
EDIT 5/03:
Here's example of making your code testable by providing an object factory for a 3rd party library object:
public class ClassToTest {
private ClientFactory factory;
public ClassTotest() {
this(new ClientFactory());
}
public ClassToTest(ClientFactory factory) {
this.factory = factory;
}
public void connect() {
Client client = factory.getClient();
WebResource jobBuilder = client.resource("URL");
String jobXml = jobBuilder.accept(MediaType.APPLICATION_XML)
.type(MediaType.APPLICATION_JSON)
.entity(request)
.post(String.class);
}
}
public class ClientFactory() {
public Client getClient() {
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(JacksonJsonProvider.class);
Client client = Client.create(cc);
return client;
}
}
Now, in your application code you can instantiate your class using the no-argument constructor. In the unit test you would use the other constructor. This way you would be able to inject a mock Client that you would script for the purpose of testing WebResource.
Hope this helps.