package io.github.azagniotov.stubby4j.stubs;
import com.google.api.client.http.HttpMethods;
import io.github.azagniotov.stubby4j.client.StubbyResponse;
import io.github.azagniotov.stubby4j.common.Common;
import io.github.azagniotov.stubby4j.http.StubbyHttpTransport;
import io.github.azagniotov.stubby4j.yaml.YAMLParser;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.internal.util.reflection.FieldSetter;
import org.mockito.junit.MockitoJUnitRunner;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class StubRepositoryTest {
private static final File CONFIG_FILE = new File("parentPath", "childPath");
private static final Future<List<StubHttpLifecycle>> COMPLETED_FUTURE =
CompletableFuture.completedFuture(new LinkedList<StubHttpLifecycle>());
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Mock
private StubbyHttpTransport mockStubbyHttpTransport;
@Mock
private HttpServletRequest mockHttpServletRequest;
@Mock
private YAMLParser mockYAMLParser;
@Captor
private ArgumentCaptor<String> stringCaptor;
@Captor
private ArgumentCaptor<File> fileCaptor;
@Captor
private ArgumentCaptor<List<StubHttpLifecycle>> stubsCaptor;
private StubRequest.Builder requestBuilder;
private StubResponse.Builder responseBuilder;
private StubRepository spyStubRepository;
@Before
public void beforeEach() throws Exception {
requestBuilder = new StubRequest.Builder();
responseBuilder = new StubResponse.Builder();
final StubRepository stubRepository = new StubRepository(CONFIG_FILE, COMPLETED_FUTURE);
final Field stubbyHttpTransportField = stubRepository.getClass().getDeclaredField("stubbyHttpTransport");
FieldSetter.setField(stubRepository, stubbyHttpTransportField, mockStubbyHttpTransport);
spyStubRepository = spy(stubRepository);
}
@Test
public void shouldExpungeOriginalHttpCycleList_WhenNewHttpCyclesGiven() throws Exception {
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithDefaultResponse("/resource/item/1");
final boolean resetResult = spyStubRepository.resetStubsCache(stubs);
assertThat(resetResult).isTrue();
assertThat(spyStubRepository.getStubs().size()).isGreaterThan(0);
}
@Test
public void shouldMatchHttplifecycle_WhenValidIndexGiven() throws Exception {
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithDefaultResponse("/resource/item/1");
final boolean resetResult = spyStubRepository.resetStubsCache(stubs);
assertThat(resetResult).isTrue();
assertThat(spyStubRepository.getStubs().size()).isGreaterThan(0);
final Optional<StubHttpLifecycle> matchedStubOptional = spyStubRepository.matchStubByIndex(0);
assertThat(matchedStubOptional.isPresent()).isTrue();
}
@Test
public void shouldNotMatchHttplifecycle_WhenInvalidIndexGiven() throws Exception {
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithDefaultResponse("/resource/item/1");
final boolean resetResult = spyStubRepository.resetStubsCache(stubs);
assertThat(resetResult).isTrue();
assertThat(spyStubRepository.getStubs().size()).isGreaterThan(0);
final Optional<StubHttpLifecycle> matchedStubOptional = spyStubRepository.matchStubByIndex(9999);
assertThat(matchedStubOptional.isPresent()).isFalse();
}
@Test
public void shouldDeleteOriginalHttpCycleList_WhenValidIndexGiven() throws Exception {
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithDefaultResponse("/resource/item/1");
final boolean resetResult = spyStubRepository.resetStubsCache(stubs);
assertThat(resetResult).isTrue();
assertThat(spyStubRepository.getStubs().size()).isGreaterThan(0);
final StubHttpLifecycle deletedHttpLifecycle = spyStubRepository.deleteStubByIndex(0);
assertThat(deletedHttpLifecycle).isNotNull();
assertThat(spyStubRepository.getStubs()).isEmpty();
}
@Test
public void shouldDeleteOriginalHttpCycleList_WhenInvalidIndexGiven() throws Exception {
expectedException.expect(IndexOutOfBoundsException.class);
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithDefaultResponse("/resource/item/1");
final boolean resetResult = spyStubRepository.resetStubsCache(stubs);
assertThat(resetResult).isTrue();
assertThat(spyStubRepository.getStubs().size()).isGreaterThan(0);
spyStubRepository.deleteStubByIndex(9999);
}
@Test
@SuppressWarnings("unchecked")
public void shouldVerifyExpectedHttpLifeCycles_WhenRefreshingStubbedData() throws Exception {
final List<StubHttpLifecycle> expectedStubs = buildHttpLifeCyclesWithDefaultResponse("/resource/item/1");
when(mockYAMLParser.parse(anyString(), any(File.class))).thenReturn(expectedStubs);
spyStubRepository.refreshStubsFromYAMLConfig(mockYAMLParser);
verify(mockYAMLParser, times(1)).parse(stringCaptor.capture(), fileCaptor.capture());
verify(spyStubRepository, times(1)).resetStubsCache(stubsCaptor.capture());
assertThat(stubsCaptor.getValue()).isEqualTo(expectedStubs);
assertThat(stringCaptor.getValue()).isEqualTo(CONFIG_FILE.getParent());
assertThat(fileCaptor.getValue()).isEqualTo(CONFIG_FILE);
}
@Test
public void shouldGetMarshalledYamlByIndex_WhenValidHttpCycleListIndexGiven() throws Exception {
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithDefaultResponse("/resource/item/1");
spyStubRepository.resetStubsCache(stubs);
final String actualMarshalledYaml = spyStubRepository.getStubYAMLByIndex(0);
assertThat(actualMarshalledYaml).isEqualTo("This is marshalled yaml snippet");
}
@Test
public void shouldFailToGetMarshalledYamlByIndex_WhenInvalidHttpCycleListIndexGiven() throws Exception {
expectedException.expect(IndexOutOfBoundsException.class);
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithDefaultResponse("/resource/item/1");
spyStubRepository.resetStubsCache(stubs);
spyStubRepository.getStubYAMLByIndex(10);
}
@Test
public void shouldUpdateStubHttpLifecycleByIndex_WhenValidHttpCycleListIndexGiven() throws Exception {
final String expectedOriginalUrl = "/resource/item/1";
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithDefaultResponse(expectedOriginalUrl);
spyStubRepository.resetStubsCache(stubs);
final StubRequest stubbedRequest = spyStubRepository.getStubs().get(0).getRequest();
assertThat(stubbedRequest.getUrl()).isEqualTo(expectedOriginalUrl);
final String expectedNewUrl = "/resource/completely/new";
final List<StubHttpLifecycle> newHttpLifecycles = buildHttpLifeCyclesWithDefaultResponse(expectedNewUrl);
final StubHttpLifecycle newStubHttpLifecycle = newHttpLifecycles.get(0);
spyStubRepository.updateStubByIndex(0, newStubHttpLifecycle);
final StubRequest stubbedNewRequest = spyStubRepository.getStubs().get(0).getRequest();
assertThat(stubbedNewRequest.getUrl()).isEqualTo(expectedNewUrl);
}
@Test
public void shouldUpdateStubHttpLifecycleByIndex_WhenInvalidHttpCycleListIndexGiven() throws Exception {
expectedException.expect(IndexOutOfBoundsException.class);
final String expectedOriginalUrl = "/resource/item/1";
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithDefaultResponse(expectedOriginalUrl);
spyStubRepository.resetStubsCache(stubs);
final StubRequest stubbedRequest = spyStubRepository.getStubs().get(0).getRequest();
assertThat(stubbedRequest.getUrl()).isEqualTo(expectedOriginalUrl);
final String expectedNewUrl = "/resource/completely/new";
final List<StubHttpLifecycle> newHttpLifecycles = buildHttpLifeCyclesWithDefaultResponse(expectedNewUrl);
final StubHttpLifecycle newStubHttpLifecycle = newHttpLifecycles.get(0);
spyStubRepository.updateStubByIndex(10, newStubHttpLifecycle);
}
@Test
public void shouldUpdateStubResponseBody_WhenResponseIsRecordable() throws Exception {
final String sourceToRecord = "http://google.com";
final String expectedOriginalUrl = "/resource/item/1";
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithCustomResponse(expectedOriginalUrl, responseBuilder.emptyWithBody(sourceToRecord).build());
spyStubRepository.resetStubsCache(stubs);
final StubResponse stubbedResponse = spyStubRepository.getStubs().get(0).getResponse(true);
assertThat(stubbedResponse.getBody()).isEqualTo(sourceToRecord);
assertThat(stubbedResponse.isRecordingRequired()).isTrue();
final String actualResponseText = "OK, this is recorded response text!";
final StubRequest stubbedRequest = spyStubRepository.getStubs().get(0).getRequest();
when(mockStubbyHttpTransport.fetchRecordableHTTPResponse(eq(stubbedRequest), anyString())).thenReturn(new StubbyResponse(200, actualResponseText));
for (int idx = 0; idx < 5; idx++) {
doReturn(stubs.get(0).getRequest()).when(spyStubRepository).toStubRequest(any(HttpServletRequest.class));
final StubSearchResult stubSearchResult = spyStubRepository.search(mockHttpServletRequest);
final StubResponse recordedResponse = stubSearchResult.getMatch();
assertThat(recordedResponse.getBody()).isEqualTo(actualResponseText);
assertThat(recordedResponse.isRecordingRequired()).isFalse();
assertThat(stubbedResponse.getBody()).isEqualTo(recordedResponse.getBody());
assertThat(stubbedResponse.isRecordingRequired()).isFalse();
}
verify(mockStubbyHttpTransport, times(1)).fetchRecordableHTTPResponse(eq(stubbedRequest), anyString());
}
@Test
public void shouldNotUpdateStubResponseBody_WhenResponseIsNotRecordable() throws Exception {
final String recordingSource = "htt://google.com"; //makes it non recordable
final String expectedOriginalUrl = "/resource/item/1";
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithCustomResponse(expectedOriginalUrl, responseBuilder.emptyWithBody(recordingSource).build());
spyStubRepository.resetStubsCache(stubs);
final StubResponse expectedResponse = spyStubRepository.getStubs().get(0).getResponse(true);
assertThat(expectedResponse.getBody()).isEqualTo(recordingSource);
doReturn(stubs.get(0).getRequest()).when(spyStubRepository).toStubRequest(any(HttpServletRequest.class));
final StubSearchResult stubSearchResult = spyStubRepository.search(mockHttpServletRequest);
final StubResponse failedToRecordResponse = stubSearchResult.getMatch();
assertThat(expectedResponse.getBody()).isEqualTo(recordingSource);
assertThat(failedToRecordResponse.getBody()).isEqualTo(recordingSource);
}
@Test
public void shouldRecordingUsingIncomingRequestQueryStringAndStubbedRecordableUrl() throws Exception {
final String sourceToRecord = "http://127.0.0.1:8888";
final StubRequest stubbedRequest =
requestBuilder
.withUrl("/search")
.withMethodGet()
.withHeader("content-type", Common.HEADER_APPLICATION_JSON)
.withQuery("queryOne", "([a-zA-Z]+)")
.withQuery("queryTwo", "([1-9]+)")
.build();
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithCustomResponse(stubbedRequest, responseBuilder.emptyWithBody(sourceToRecord).build());
spyStubRepository.resetStubsCache(stubs);
final String actualResponseText = "OK, this is recorded response text!";
when(mockStubbyHttpTransport.fetchRecordableHTTPResponse(eq(stubbedRequest), stringCaptor.capture())).thenReturn(new StubbyResponse(200, actualResponseText));
final StubRequest incomingRequest =
requestBuilder
.withUrl("/search")
.withMethodGet()
.withHeader("content-type", Common.HEADER_APPLICATION_JSON)
.withQuery("queryTwo", "12345")
.withQuery("queryOne", "arbitraryValue")
.build();
doReturn(incomingRequest).when(spyStubRepository).toStubRequest(any(HttpServletRequest.class));
final StubSearchResult stubSearchResult = spyStubRepository.search(mockHttpServletRequest);
final StubResponse recordedResponse = stubSearchResult.getMatch();
assertThat(recordedResponse.getBody()).isEqualTo(actualResponseText);
assertThat(stringCaptor.getValue()).isEqualTo(String.format("%s%s", sourceToRecord, incomingRequest.getUrl()));
}
@Test
public void shouldNotUpdateStubResponseBody_WhenResponseIsRecordableButExceptionThrown() throws Exception {
final String recordingSource = "http://google.com";
final String expectedOriginalUrl = "/resource/item/1";
final List<StubHttpLifecycle> stubs = buildHttpLifeCyclesWithCustomResponse(expectedOriginalUrl, responseBuilder.emptyWithBody(recordingSource).build());
spyStubRepository.resetStubsCache(stubs);
final StubResponse expectedResponse = spyStubRepository.getStubs().get(0).getResponse(true);
assertThat(expectedResponse.getBody()).isEqualTo(recordingSource);
final StubRequest matchedRequest = spyStubRepository.getStubs().get(0).getRequest();
when(mockStubbyHttpTransport.fetchRecordableHTTPResponse(eq(matchedRequest), anyString())).thenThrow(Exception.class);
doReturn(stubs.get(0).getRequest()).when(spyStubRepository).toStubRequest(any(HttpServletRequest.class));
final StubSearchResult stubSearchResult = spyStubRepository.search(mockHttpServletRequest);
final StubResponse actualResponse = stubSearchResult.getMatch();
assertThat(expectedResponse.getBody()).isEqualTo(recordingSource);
assertThat(actualResponse.getBody()).isEqualTo(recordingSource);
}
@Test
public void stubbedRequestEqualsAssertingRequest_WhenQueryParamArrayHasElementsWithinUrlEncodedQuotes() throws Exception {
final String paramOne = "names";
final String paramOneValue = "[\"alex\",\"tracy\"]";
final String url = "/invoice/789";
final StubRequest expectedRequest =
requestBuilder.withUrl(url)
.withMethodGet()
.withMethodHead()
.withQuery(paramOne, paramOneValue).build();
when(mockHttpServletRequest.getPathInfo()).thenReturn(url);
when(mockHttpServletRequest.getMethod()).thenReturn(HttpMethods.GET);
when(mockHttpServletRequest.getQueryString()).thenReturn("names=[%22alex%22,%22tracy%22]");
final StubRequest assertingRequest = spyStubRepository.toStubRequest(mockHttpServletRequest);
assertThat(assertingRequest).isEqualTo(expectedRequest);
}
@Test
public void stubbedRequestEqualsAssertingRequest_WhenQueryParamUrlEncodedArrayHasElementsWithinUrlEncodedQuotes() throws Exception {
final String paramOne = "names";
final String paramOneValue = "[\"alex\",\"tracy\"]";
final String url = "/invoice/789";
final StubRequest expectedRequest =
requestBuilder.withUrl(url)
.withMethodGet()
.withMethodHead()
.withQuery(paramOne, paramOneValue).build();
when(mockHttpServletRequest.getPathInfo()).thenReturn(url);
when(mockHttpServletRequest.getMethod()).thenReturn(HttpMethods.GET);
when(mockHttpServletRequest.getQueryString()).thenReturn("names=%5B%22alex%22,%22tracy%22%5D");
final StubRequest assertingRequest = spyStubRepository.toStubRequest(mockHttpServletRequest);
assertThat(assertingRequest).isEqualTo(expectedRequest);
}
@Test
public void stubbedRequestEqualsAssertingRequest_WhenQueryParamUrlEncodedArrayHasElementsWithinUrlEncodedSingleQuotes() throws Exception {
final String paramOne = "names";
final String paramOneValue = "['alex','tracy']";
final String url = "/invoice/789";
final StubRequest expectedRequest =
requestBuilder.withUrl(url)
.withMethodGet()
.withMethodHead()
.withQuery(paramOne, paramOneValue).build();
when(mockHttpServletRequest.getPathInfo()).thenReturn(url);
when(mockHttpServletRequest.getMethod()).thenReturn(HttpMethods.GET);
when(mockHttpServletRequest.getQueryString()).thenReturn("names=%5B%27alex%27,%27tracy%27%5D");
final StubRequest assertingRequest = spyStubRepository.toStubRequest(mockHttpServletRequest);
assertThat(assertingRequest).isEqualTo(expectedRequest);
}
@Test
public void stubbedRequestEqualsAssertingRequest_WhenQueryValuesHaveEncodedSinglePlus() throws Exception {
final String paramOne = "names";
final String paramOneValue = "stalin lenin truman";
final String encodedRawQuery = paramOneValue.replaceAll("\\s+", "%2B");
final String url = "/invoice/789";
final StubRequest expectedRequest =
requestBuilder.withUrl(url)
.withMethodGet()
.withQuery(paramOne, paramOneValue).build();
when(mockHttpServletRequest.getPathInfo()).thenReturn(url);
when(mockHttpServletRequest.getMethod()).thenReturn(HttpMethods.GET);
when(mockHttpServletRequest.getQueryString()).thenReturn("names=" + encodedRawQuery);
final StubRequest assertingRequest = spyStubRepository.toStubRequest(mockHttpServletRequest);
assertThat(assertingRequest).isEqualTo(expectedRequest);
}
@Test
public void stubbedRequestEqualsAssertingRequest_WhenQueryValuesHaveMultipleRawPluses() throws Exception {
final String paramOne = "names";
final String paramOneValue = "stalin lenin truman";
final String encodedRawQuery = paramOneValue.replaceAll("\\s+", "+++");
final String url = "/invoice/789";
final StubRequest expectedRequest =
requestBuilder.withUrl(url)
.withMethodGet()
.withQuery(paramOne, paramOneValue).build();
when(mockHttpServletRequest.getPathInfo()).thenReturn(url);
when(mockHttpServletRequest.getMethod()).thenReturn(HttpMethods.GET);
when(mockHttpServletRequest.getQueryString()).thenReturn("names=" + encodedRawQuery);
final StubRequest assertingRequest = spyStubRepository.toStubRequest(mockHttpServletRequest);
assertThat(assertingRequest).isEqualTo(expectedRequest);
}
@Test
public void stubbedRequestEqualsAssertingRequest_WhenQueryValuesHaveEncodedMultiplePluses() throws Exception {
final String paramOne = "names";
final String paramOneValue = "stalin lenin truman";
final String encodedRawQuery = paramOneValue.replaceAll("\\s+", "%2B%2B%2B");
final String url = "/invoice/789";
final StubRequest expectedRequest =
requestBuilder.withUrl(url)
.withMethodGet()
.withQuery(paramOne, paramOneValue).build();
when(mockHttpServletRequest.getPathInfo()).thenReturn(url);
when(mockHttpServletRequest.getMethod()).thenReturn(HttpMethods.GET);
when(mockHttpServletRequest.getQueryString()).thenReturn("names=" + encodedRawQuery);
final StubRequest assertingRequest = spyStubRepository.toStubRequest(mockHttpServletRequest);
assertThat(assertingRequest).isEqualTo(expectedRequest);
}
@Test
public void stubbedRequestEqualsAssertingRequest_WhenQueryValues_HasArrayElementsWithEncodedSpacesWithinUrlEncodedSingleQuotes() throws Exception {
final String paramOne = "names";
final String paramOneValue = "['stalin and truman','are best friends']";
final String encodedRawQuery = paramOneValue
.replaceAll("\\s+", "%20%20")
.replaceAll(Pattern.quote("["), "%5B")
.replaceAll("\\]", "%5D")
.replaceAll("'", "%27");
final String url = "/invoice/789";
final StubRequest expectedRequest =
requestBuilder.withUrl(url)
.withMethodGet()
.withMethodHead()
.withQuery(paramOne, paramOneValue).build();
when(mockHttpServletRequest.getPathInfo()).thenReturn(url);
when(mockHttpServletRequest.getMethod()).thenReturn(HttpMethods.GET);
when(mockHttpServletRequest.getQueryString()).thenReturn("names=" + encodedRawQuery);
final StubRequest assertingRequest = spyStubRepository.toStubRequest(mockHttpServletRequest);
assertThat(assertingRequest).isEqualTo(expectedRequest);
}
@Test
public void stubbedRequestEqualsAssertingRequest_WhenQueryValues_HasArrayElementsWithEncodedPlusWithinUrlEncodedSingleQuotes() throws Exception {
final String paramOne = "names";
final String paramOneValue = "['stalin and truman','are best friends']";
final String encodedRawQuery = paramOneValue
.replaceAll("\\s+", "%2B%2B%2B")
.replaceAll(Pattern.quote("["), "%5B")
.replaceAll("\\]", "%5D")
.replaceAll("'", "%27");
final String url = "/invoice/789";
final StubRequest expectedRequest =
requestBuilder.withUrl(url)
.withMethodGet()
.withMethodHead()
.withQuery(paramOne, paramOneValue).build();
when(mockHttpServletRequest.getPathInfo()).thenReturn(url);
when(mockHttpServletRequest.getMethod()).thenReturn(HttpMethods.GET);
when(mockHttpServletRequest.getQueryString()).thenReturn("names=" + encodedRawQuery);
final StubRequest assertingRequest = spyStubRepository.toStubRequest(mockHttpServletRequest);
assertThat(assertingRequest).isEqualTo(expectedRequest);
}
@Test
public void stubbedRequestNotEqualsAssertingRequest_WhenQueryParamArrayElementsHaveDifferentSpacing() throws Exception {
final String paramOne = "names";
final String paramOneValue = "[\"alex\", \"tracy\"]";
final String url = "/invoice/789";
final StubRequest expectedRequest =
requestBuilder.withUrl(url)
.withMethodGet()
.withMethodHead()
.withQuery(paramOne, paramOneValue).build();
when(mockHttpServletRequest.getPathInfo()).thenReturn(url);
when(mockHttpServletRequest.getMethod()).thenReturn(HttpMethods.GET);
when(mockHttpServletRequest.getQueryString()).thenReturn("names=[%22alex%22,%22tracy%22]");
final StubRequest assertingRequest = spyStubRepository.toStubRequest(mockHttpServletRequest);
assertThat(assertingRequest).isNotEqualTo(expectedRequest);
}
private List<StubHttpLifecycle> buildHttpLifeCyclesWithDefaultResponse(final String url) throws Exception {
return buildHttpLifeCyclesWithCustomResponse(url, StubResponse.okResponse());
}
private List<StubHttpLifecycle> buildHttpLifeCyclesWithCustomResponse(final String url, final StubResponse stubResponse) throws Exception {
final StubRequest stubRequest =
requestBuilder
.withUrl(url)
.withMethodGet()
.withHeader("content-type", Common.HEADER_APPLICATION_JSON)
.build();
return buildHttpLifeCyclesWithCustomResponse(stubRequest, stubResponse);
}
private List<StubHttpLifecycle> buildHttpLifeCyclesWithCustomResponse(final StubRequest stubRequest, final StubResponse stubResponse) throws Exception {
final StubHttpLifecycle.Builder stubBuilder = new StubHttpLifecycle.Builder();
stubBuilder.withRequest(stubRequest)
.withResponse(stubResponse)
.withCompleteYAML("This is marshalled yaml snippet");
return new LinkedList<StubHttpLifecycle>() {{
add(stubBuilder.build());
}};
}
}