Estratégias para Mock um serviço Web
-
03-07-2019 - |
Pergunta
Estou implementando um cliente consumir um webservice. Quero reduzir dependências e decidiu zombar do webservice.
Eu uso Mockito , ele tem a vantagem vs. EasyMock para ser capaz de aulas simuladas, não interfaces de apenas. Mas isso não é o ponto.
No meu teste, eu tenho este código:
// Mock the required objects
Document mDocument = mock(Document.class);
Element mRootElement = mock(Element.class);
Element mGeonameElement = mock(Element.class);
Element mLatElement = mock(Element.class);
Element mLonElement = mock(Element.class);
// record their behavior
when(mDocument.getRootElement()).thenReturn(mRootElement);
when(mRootElement.getChild("geoname")).thenReturn(mGeonameElement);
when(mGeonameElement.getChild("lat")).thenReturn(mLatElement);
when(mGeonameElement.getChild("lon")).thenReturn(mLonElement);
// A_LOCATION_BEAN is a simple pojo for lat & lon, don't care about it!
when(mLatElement.getText()).thenReturn(
Float.toString(A_LOCATION_BEAN.getLat()));
when(mLonElement.getText()).thenReturn(
Float.toString(A_LOCATION_BEAN.getLon()));
// let it work!
GeoLocationFetcher geoLocationFetcher = GeoLocationFetcher
.getInstance();
LocationBean locationBean = geoLocationFetcher
.extractGeoLocationFromXml(mDocument);
// verify their behavior
verify(mDocument).getRootElement();
verify(mRootElement).getChild("geoname");
verify(mGeonameElement).getChild("lat");
verify(mGeonameElement).getChild("lon");
verify(mLatElement).getText();
verify(mLonElement).getText();
assertEquals(A_LOCATION_BEAN, locationBean);
O que meus programas de código é que eu "micro-test" o objeto de consumo. É como se eu iria implementar meu código produtivo no meu teste. Um exemplo para o xml resultado é Londres em GeoNames . Na minha opinião, é muito granular.
Mas como posso zombar um webservice sem dar everystep? Devo deixar o objeto fictício apenas retornar um arquivo XML?
Não é sobre o código, mas a abordagem .
Eu estou usando JUnit 4.x e Mockito 1.7
Solução
Você realmente quer ser zombando os resultados retornados a partir do webservice para o código que estará usando o resultado. Em seu código exemplo acima você parece estar zombando mDocument mas você realmente quer passar em uma instância do mDocument que foi retornado de uma instância zombou de seu webservice e afirmar que o locationBean retornado do geoLocationFetcher corresponde ao valor de A_LOCATION_BEAN.
Outras dicas
Eu acho que o verdadeiro problema aqui é que você tem um singleton que as chamadas e cria o serviço web por isso é difícil para inserir um simulada.
Você pode ter que adicionar (possivelmente nível pacote) acesso à classe Singleton. Por exemplo, se o construtor é algo como
private GeoLocationFactory(WebService service) {
...
}
Você pode fazer a nível de pacote construtor e apenas criar um com um serviço web zombou.
Como alternativa, você pode definir o webservice, adicionando um método setter, embora eu não gosto Singletons mutáveis. Também nesse caso, você tem que lembrar para remover o webservice depois.
Se o webservice é criado em um método que você pode ter que fazer o extensível GeoLocationFactory para substituir o serviço de simulação.
Você também pode olhar para remover o próprio singleton. Há artigos on-line e provavelmente aqui sobre como fazer isso.
A opção mais fácil seria zombar do cliente WebService,
when(geoLocationFetcher.extractGeoLocationFromXml(anyString()))
.thenReturn("<location/>");
Você pode modificar o código para ler o XML de resposta do sistema de arquivos.
O código de exemplo pode ser encontrado aqui: Mocking .NET WebServices com Mockito