Mockito e como cozinhá-lo

Sobre o artigo


Aqui está outro guia para Mockito. Nele, por um lado, tentei descrever a funcionalidade dessa biblioteca para que um leitor não familiarizado com ela tivesse a oportunidade de usá-la imediatamente, e não apenas uma idéia geral. Por outro lado, eu queria torná-lo compacto o suficiente e estruturado para que pudesse lê-lo rapidamente na íntegra e rapidamente encontrar nele algo uma vez lido, mas esquecido. Em geral, este artigo, que seria útil para mim, quando me deparei com esta biblioteca e realmente não entendi como ela funciona.


Suponho que possa me ser útil agora - às vezes esqueço um pouco disso, e é mais conveniente recordar o material não de acordo com a documentação oficial ou com os artigos de outras pessoas, mas com a minha própria, digamos, sinopse. Ao mesmo tempo, tentei criar o texto para que fosse conveniente principalmente conhecer Mockito do zero e, em alguns lugares, analiso em detalhes coisas aparentemente óbvias - nem todas as quais eram óbvias para mim desde o início.


Conteúdo:


  1. Mockito: o que é e por que é necessário
  2. Ambiente, versões e animal experimental
  3. zombar e espionar
  4. Gerenciamento de comportamento
    1. Definir condições de chamada
    2. Definir resultados da chamada
  5. Chamadas de método de rastreamento
  6. Objetos simulados como valores de campo e anotações do Mockito
  7. Comportamento de reversão para sessões padrão e Mockito
  8. O que mais?

Mockito: o que é e por que é necessário


Em resumo, o Mockito é uma estrutura de stub.


Como você sabe, ao testar o código (principalmente o teste de unidade, mas não apenas), o elemento sob teste geralmente precisa fornecer instâncias de classes que ele deve usar ao trabalhar. No entanto, muitas vezes eles não precisam ser totalmente funcionais - pelo contrário, são obrigados a se comportar de maneira estritamente definida, para que seu comportamento seja simples e completamente previsível. Eles são chamados de stubs. Para obtê-los, você pode criar implementações de teste alternativas de interfaces, herdar as classes necessárias com redefinição de funcionalidade e assim por diante, mas tudo isso é bastante inconveniente, redundante e cheio de erros. Uma solução mais conveniente em todos os sentidos são estruturas especializadas para a criação de stubs. Um deles (e talvez o mais famoso por Java) é o Mockito.


O Mockito permite que você crie com uma única linha de código o chamado mock (algo como a base para o esboço desejado) de qualquer classe. Para tal simulação, imediatamente após a criação, um determinado comportamento padrão é característico (todos os métodos retornam valores conhecidos anteriormente - geralmente isso é null ou 0 ). Você pode redefinir esse comportamento da maneira que desejar, controlá-lo com o grau certo de detalhes e assim por diante. Como resultado, o mock se torna um esboço com as propriedades necessárias. Abaixo, discutirei em detalhes como fazer isso.


Observo que o mock também pode ser criado para essas classes, cuja nova instância não é possível apenas criar, em particular, classes com construtores exclusivamente privados, como classes singleton e utilitário, e com uma configuração mínima da estrutura e enumerações.


Ambiente, versões e animal experimental


Ao escrever este artigo, usei:


  • Mockito: 'org.mockito: mockito-core: 2.24.0' (última versão estável no momento da redação)
  • TestNG: 'org.testng: testng: 6.14.3' como uma estrutura de teste
  • AssertJ: 'org.assertj: assertj-core: 3.11.1' como uma ferramenta de validação
  • Lombok: 'org.projectlombok: lombok: 1.18.6' (apenas por conveniência)
  • Java 8

Para minhas experiências desumanas, escrevi essa interface de um serviço que fornece acesso a determinados dados.


 public interface DataService { void saveData(List<String> dataToSave); String getDataById(String id); String getDataById(String id, Supplier<String> calculateIfAbsent); List<String> getData(); List<String> getDataListByIds(List<String> idList); List<String> getDataByRequest(DataSearchRequest request); } 

E este código (que seja por uma questão de ordem) da classe de solicitação passada para o último dos métodos de interface.


 @AllArgsConstructor @Getter class DataSearchRequest { String id; Date updatedBefore; int length; } 

As unidades de dados são identificadas por ID e possuem mais algumas características, mas diretamente na forma em que são retornadas pelo serviço, são cadeias de caracteres e não objetos mais complexos. Não perco nada importante, e os exemplos são mais simples e claros.


Observarei imediatamente: nos exemplos abaixo, chamo diretamente os métodos substituídos dos meus objetos simulados para maior clareza, mas com testes reais a idéia não é de todo! Neste teste, eu consistentemente faria o seguinte:


  • configurei a simulação do meu serviço, conforme necessário;
  • passei (provavelmente, via construtor) para uma instância de outra classe que o utiliza (suponha que contenha algum tipo de lógica de negócios usando os dados fornecidos pelo DataService ), que eu realmente testaria;
  • habilitou a funcionalidade da classe testada e controlou os resultados;
  • se necessário, controlaria o número e a ordem das chamadas para o (s) método (s) do meu mock, que deveria ter sido chamado pela classe testada como resultado da ação anterior.

zombar e espionar


A classe central do Mockito, através da qual é suposto acessar a maior parte da funcionalidade, é, de fato, uma classe chamada Mockito (há também a classe BDDMockito que fornece os mesmos recursos de uma forma mais adequada para o BDD , mas aqui não vou me debruçar sobre isso) . O acesso à funcionalidade é implementado através de seus métodos estáticos.


Para criar uma simulação da classe DataService , basta fazer o seguinte:


 DataService dataServiceMock = Mockito.mock(DataService.class); 

Concluído - recebi uma instância da classe de que preciso. Será aceito por qualquer método ou construtor que exija um parâmetro desse tipo (por exemplo, o construtor da classe que eu quero testar). Mesmo que uma verificação de dependência aguarde mais tarde, ela será aprovada: não apenas a instanceof DataService retornará true , mas também o dataServiceMock.getClass() - ou seja, DataService.class . De alguma maneira formal, distinguir programaticamente um objeto falso de um objeto comum acaba sendo uma tarefa bastante difícil, que é lógica: afinal, o primeiro se destina apenas a ser indistinguível do segundo. No entanto, o Mockito possui uma ferramenta para isso - o método Mockito.mockingDetails . Ao passar para ele um objeto arbitrário, recebo um objeto da classe MockingDetails . Ele contém informações sobre o que esse objeto representa do ponto de vista do Mockito: se é falso, espião (veja abaixo), como foi usado, como foi criado e assim por diante.


É digna de nota a situação em que tento criar uma simulação para a classe final ou uma instância simulada de enum ou substituir o comportamento do método final. Nesse caso, com o comportamento padrão do Mockito, o código acima se recusa a funcionar, citando precisamente essa circunstância. No entanto, isso pode ser alterado - basta criar no projeto (com o dispositivo padrão da árvore de diretórios do projeto) o arquivo test/resources/mockito-extensions/org.mockito.plugins.MockMaker e digite a linha:


 mock-maker-inline 

Depois disso, você pode imitar as classes finais e enumerações da maneira usual, bem como substituir os métodos finais.


O escárnio que eu entrei em ação é o menos característico possível: nem um único método terá efeito sobre nada, e o valor retornado será null para tipos de objetos e 0 para tipos primitivos. Observe: se o método retornar uma coleção, a simulação padrão não retornará null , mas instâncias de coleção vazias. Por exemplo, para List isso acabará sendo um LinkedList vazio LinkedList independentemente do que o método real deveria ter retornado. Mas como os valores de matrizes, primitivas ou objetos, fico null . O comportamento padrão (e não apenas) pode ser alterado usando a funcionalidade da classe MockSettings , mas isso raramente é necessário.


De uma maneira ou de outra, na maioria dos casos, não precisarei do comportamento padrão e, na próxima seção, analisarei em detalhes como definir o que é necessário.


No entanto, e se eu quiser usar um objeto de classe real com a funcionalidade disponível como um stub, redefinindo a operação de apenas parte de seus métodos? Se estamos falando de teste de unidade, essa necessidade geralmente (mas nem sempre) indica que o projeto não está bem com o design e, em princípio, isso não é recomendado. No entanto, há situações em que isso, por algum motivo, não pode ser evitado. Para este caso, Mockito tem o chamado espião, "espiões". Diferentemente das simulações, elas podem ser criadas com base na classe e no objeto final:


 DataService dataServiceSpy = Mockito.spy(DataService.class); // or DataService dataService = new DataService(); dataServiceSpy = Mockito.spy(dataService); 

Ao criar espião com base em uma classe, se seu tipo for uma interface, um objeto simulado regular será criado e, se o tipo for uma classe, o Mockito tentará criar uma instância usando o construtor padrão (sem parâmetros). E somente se não houver esse construtor, ocorrerá um erro e o teste não funcionará.


O comportamento de objetos espiões, por padrão, é idêntico ao comportamento de uma instância de classe regular, no entanto, eles me oferecem as mesmas possibilidades que objetos simulados: permitem redefinir seu comportamento e monitorar seu uso (consulte as seções a seguir). Um ponto importante: o espião não é um invólucro em torno da instância em que foi criado! Portanto, chamar o método espião não afetará o estado da instância original.


Gerenciamento de comportamento


Então, sobre como conseguir zombaria ou espionagem para fazer o que eu preciso. Além disso, eu sempre escreverei simplesmente "zombaria" em todos os lugares - isso significa "zombaria ou espião", a menos que expressamente indicado de outra forma.


Em geral, controlar o comportamento de um objeto simulado se resume a um conceito óbvio: quando um simulado foi afetado de tal maneira (isto é, tal e tal método foi chamado com tais e tais argumentos), deve responder de tal e tal maneira. Esse conceito possui duas implementações na classe Mockito - a principal, recomendada pelos desenvolvedores para uso sempre que possível, e a alternativa, usada onde a principal não é adequada.


A principal implementação é baseada no método Mockito.when . Esse método usa como "parâmetro" uma chamada para o método redefinido do objeto de simulação (dessa maneira, a ação detectada é corrigida) e retorna um objeto do tipo OngoingStubbing , que permite chamar um dos métodos da família Mockito.then... (é assim que a reação a esse efeito é definida). Todos juntos, no caso mais simples, é algo parecido com isto:


 List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.when(dataService.getAllData()).thenReturn(data); 

Após esta operação, chamando o método getAllData() no objeto getAllData() , obtenho o objeto especificado na primeira linha da listagem.


Aqui, a intuição familiar "orientada a objetos" pode causar algum tipo de mau funcionamento, portanto vale a pena insistir em mais detalhes. Do ponto de vista da sintaxe Java, o valor passado para o método when como parâmetro é, obviamente, o valor retornado pelo método substituído. Para simulação, esse é um valor vazio; para espião, esse é o valor retornado pelo método do objeto real. Mas, graças à ação mágica "sob o capô" do Mockito, o método when funcionará normalmente (e não trava quando iniciado com um erro) somente se a chamada do método de objeto simulado estiver dentro dos colchetes depois de when .


Uma ideologia semelhante geralmente funciona ao definir o comportamento de uma simulação em um Mockito: chamando um método (de um objeto ou classe simulado do Mockito ), tento não obter o valor retornado por ele, mas de alguma forma influenciar a possível chamada do método do objeto simulado com o qual trabalho: especificar suas fronteiras, definir o resultado, estabelecer a observação de seus desafios e assim por diante. Parece um pouco nebuloso, admito, e na primeira colisão parece estranho, mas, depois de descobrir, você logo começa a sentir essa abordagem como completamente natural no contexto de trabalhar com stubs.


Uma implementação alternativa de vincular a condição e o resultado da chamada são os métodos da família Mockito.do... Esses métodos permitem definir o comportamento começando com o resultado da chamada e retornar um objeto da classe Stubber , com o qual você já pode definir a condição. A mesma ligação acima feita desta maneira é assim:


 List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.doReturn(data).when(dataService).getData() 

Qual é a diferença, por que vincular através do Mockito.when é considerado preferível e quando você ainda precisa usar os métodos do Mockito.do... ? Observe: na primeira implementação, ao definir o comportamento do método (neste caso, getAllData() ), a chamada para a versão que ainda não foi redefinida é executada primeiro e somente então, nas entranhas do Mockito, ocorre uma substituição. No segundo, essa chamada não ocorre - o método Stubber.when passado diretamente para o método Stubber.when , e um objeto do mesmo tipo retornado por esse método, mas de natureza diferente, é chamado com um método substituível. Essa diferença determina tudo. A ligação via Mockito.do... não controla, no estágio de compilação, qual método redefinível chamarei e se é compatível no tipo com o valor de retorno fornecido. Portanto, geralmente Mockito.when preferível - não pode haver erro com isso. Mas pode haver casos em que eu quero evitar chamar um método substituído - para uma simulação criada recentemente, tal chamada é bastante aceitável, mas se eu já redefinir esse método ou lidar com o espião, pode ser indesejável, e lançar uma exceção não permitirá a redefinição necessária. . E aqui a ligação através do Mockito.do... vem em Mockito.do...


Outra situação em que você não pode ficar sem os métodos Mockito.do... está substituindo o método que retorna void : o parâmetro Mockito.when pendente não pode funcionar com esse método. Mockito.doReturn aqui, mas há Mockito.doThrow , Mockito.doAnswer e, raramente, é suficiente o Mockito.doNothing .


A seguir, analisarei um pouco mais detalhadamente como definir condições e resultados das chamadas. Considerarei apenas a ligação através do Mockito.when - uma maneira alternativa é quase completamente semelhante no manuseio.


Definir condições de chamada


O exemplo acima refere-se a um método sem parâmetros, e a condição de chamada associada é possível, uma coisa - o fato da chamada. Assim que os parâmetros aparecem, a situação se torna mais complicada. No mínimo, para chamar um método cujo comportamento estou definindo, preciso passar algo para ele. Mas outra coisa é mais importante: pode ser que eu nem sempre queira obter a reação dada, mas apenas quando a chamo com parâmetros que atendem a certos requisitos. DataService possui este método:


 String getDataItemById(String id) { // some code... } 

Se eu precisar definir uma resposta para qualquer chamada para esse método, independentemente dos argumentos, devo usar o método Mockito.any :


 Mockito.when(dataService.getDataItemById(any())) .thenReturn("dataItem"); 

Se eu precisar do mock para reagir apenas a um determinado valor do argumento, você pode usar esse valor diretamente ou os métodos de Mockito.eq (se for uma equivalência) ou Mockito.same (se for necessária a comparação de links):


 Mockito.when(dataService.getDataItemById("idValue")) .thenReturn("dataItem"); // or Mockito.when(dataService.getDataItemById(Mockito.eq("idValue"))) .thenReturn("dataItem"); 

E se eu quiser que o argumento atenda a alguns requisitos, existem vários métodos estáticos especializados convenientes da mesma classe Mockito (por exemplo, as strings podem ser verificadas quanto ao conteúdo no início ou no final de uma determinada sequência de caracteres, correspondência de padrões etc.). Há também um método geral Mockito.argThat (e seus análogos para tipos primitivos) que aceita a implementação da interface funcional ArgumentMatcher:


 Mockito.when(dataService.getDataById( Mockito.argThat(arg -> arg == null || arg.length() > 5))) .thenReturn("dataItem"); 

AdditionalMatchers classes ArgumentMatchers e AdditionalMatchers permitem que você trabalhe com algumas implementações úteis prontas para uso dessa interface. Por exemplo, AdditionalMatchers.or e AdditionalMatchers.and permitem combinar outros correspondentes (nota: os métodos estáticos dessas classes não retornam instâncias dos correspondentes, mas apenas os acessam!)


Para o mesmo método, você pode definir o comportamento várias vezes com requisitos diferentes para os argumentos, e todos os modelos de comportamento definidos dessa maneira agirão simultaneamente. Obviamente, em alguns casos, eles podem se cruzar - digamos, exigirei retornar um resultado quando o valor int do parâmetro for menor que 5 e o outro quando o valor par for recebido. Nessa situação, o comportamento especificado posteriormente tem precedência. Portanto, ao definir padrões complexos de comportamento, você deve começar com os requisitos mais fracos (no limite - any() ) e depois passar para os mais específicos.


Ao trabalhar com métodos com mais de um argumento, os requisitos especificados são combinados de acordo com o AND lógico, ou seja, para obter o resultado especificado, TODOS os argumentos devem atender ao requisito declarado. Não encontrei uma maneira de definir uma maneira arbitrária de combiná-los, embora talvez exista.


Além disso, ao especificar o comportamento de um método desse tipo, não é possível combinar os métodos estáticos do Mockito e a transferência direta de valores. Use Mockito.eq ou Mockito.same .


Definir resultados da chamada


Depois que o método do objeto simulado é chamado, o objeto deve responder à chamada. As principais conseqüências possíveis são retornar o resultado e lançar uma exceção, e é justamente nessas opções que o kit de ferramentas Mockito é projetado principalmente.


No caso mais simples já mostrado acima, a resposta à chamada é retornar um valor. Vou dar o código dele novamente:


 List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.when(dataService.getAllData()).thenReturn(data); 

Observe: você só pode retornar um objeto; não há métodos separados para primitivos. Portanto, se o método retornar um valor primitivo, em tal situação, ocorrerá desmarcação. Na maioria dos casos, isso não interfere, mas se o compilador pensa de outra forma, você terá que concordar de alguma forma com ele ... ou tolerar seus avisos.


Lançar exceções não é mais difícil:


 Mockito.when(dataService.getDataById("invalidId")) .thenThrow(new IllegalArgumentException()); 

Existe outra maneira: você pode criar um objeto de exceção e jogá-lo diretamente, ou pode fornecer ao Mockito apenas uma classe de exceção para que ele seja criado automaticamente:


 Mockito.when(dataService.getDataById("invalidId")) .thenThrow(IllegalArgumentException.class); 

Nos dois casos, a sintaxe permite que você use e verifique as exceções, mas o Mockito não permitirá que você execute esse teste se o tipo de exceção não corresponder ao método que desejo forçar ao lançar essa exceção.


Ao usar uma classe como parâmetro, construtores (mesmo sem parâmetros), bem como a inicialização direta do campo, são ignorados - o objeto é criado ignorando-os (afinal, isso é Mockito!), Para que todos os campos da exceção lançada sejam null . Portanto, se o conteúdo da exceção for importante para você (por exemplo, algum campo de type que tenha um valor padrão), será necessário abandonar esse método e criar exceções manualmente.


Essas opções de reação são adequadas se, em resposta a uma chamada com determinadas condições, você sempre precisar retornar um certo valor, sempre o mesmo valor do resultado ou sempre lançar a mesma exceção, e na maioria dos casos esses recursos serão suficientes. Mas e se for necessária mais flexibilidade? Suponha que meu método aceite uma coleção de valores e retorne outra coleção de valores associados ao primeiro para um (por exemplo, obtendo uma coleção de objetos de dados pelo conjunto de seus IDs) e eu quero usar esse objeto simulado repetidamente com diferentes conjuntos de entradas no teste dados, obtendo cada vez o resultado correspondente. Obviamente, é possível descrever separadamente a reação a cada conjunto específico de parâmetros, mas existe uma solução mais conveniente - o método Mockito.thenAnswer , também Mockito.then como Mockito.then . Ele aceita uma implementação da interface funcional Answer , cujo único método é receber um objeto da classe InvocationOnMock . Deste último, posso solicitar os parâmetros da chamada do método (um por número ou todos de uma vez na forma de uma matriz) e agir com eles como eu quiser. Por exemplo, você pode obter um valor correspondente a ele para cada um dos elementos da minha coleção, formar uma nova coleção a partir deles e retorná-lo (nota: o resultado desejado é simplesmente retornado e não gravado em algum campo do objeto de parâmetro, como seria de esperar):


 Mockito.when(dataService.getDataByIds(Mockito.any())) .thenAnswer(invocation -> invocation .<List<String>>getArgument(0).stream() .map(id -> { switch (id) { case "a": return "dataItemA"; case "b": return "dataItemB"; default: return null; } }) .collect(Collectors.toList())); 

Ideologicamente, isso é algo como escrever um modelo de um método real: obter parâmetros, processar, retornar um resultado. - , - , , , mock- .


Answer , , — , AnswersWithDelay , ReturnsElementsOf . .


: InvocationOnMockObject[] , generic-.


thenCallRealMethod . . mock-, spy-. mock , , - null . spy thenCallRealMethod spy ; , - .


thenAnswer : InvocationOnMock callRealMethod() — , "" - .


OngoingStubbing OngoingStubbing , , , . , . thenReturn thenThrow , varargs. .


 Mockito.when(dataService.getDataById("a")) .thenReturn("valueA1", "valueA2") .thenThrow(IllegalArgumentException.class); 

"valueA1 , — "valueA2 ( ), ( ) IllegalArgumentException .



: (mock' ), . , : , , . verify .


, , :


 Mockito.verify(dataService).getDataById(Mockito.any()); 

, getDataById , , . , Mockito, when , , , mock-. , , , when , — mock', (. ).


:


 Mockito.verify(dataService, Mockito.times(1)) .getDataById(Mockito.any()); 

Mockito.times ; Mockito.never . Mockito.atLeast ( Mockito.atLeastOnce 1) Mockito.atMost , , Mockito.only , , mock- (. . ).


, Mockito , VerificationAfterDelay VerificationWithTimeout , Mockito.after Mockito.timeout . Por exemplo:


 Mockito.verify(dataService, Mockito.after(1000).times(1)) .getDataById(Mockito.any()); 

, mock , , , . . after timeout , , , — , . , timeout — . VerificationWithTimeout never atMost : .


, Mockito.any() . , , — Mockito , , . Mock- , , , , :


 dataService.getDataById("a"); dataService.getDataById("b"); Mockito.verify(dataService, Mockito.times(2)).getDataById(Mockito.any()); Mockito.verify(dataService, Mockito.times(1)).getDataById("a"); Mockito.verify(dataService, Mockito.never()).getDataById("c"); dataService.getDataById("c"); Mockito.verify(dataService, Mockito.times(1)).getDataById("c"); Mockito.verifyNoMoreInteractions(dataService); 

verifyNoMoreInteractions ( verifyZeroInteractions ) — - ( verify ) mock- — . : varargs, , , !


, , , . , InOrder :


 InOrder inOrder = Mockito.inOrder(dataService); 

varargs; — mock- , InOrder . verify , Mockito.verify :


 inOrder.verify(dataService, times(2)).saveData(any()); inOrder.verify(dataService).getData(); 

, saveData , getData . , InOrder , — .


, , — , . - , , — , , . ArgumentCaptor capture() . Por exemplo:


 DataSearchRequest request = new DataSearchRequest("idValue", new Date(System.currentTimeMillis()), 50); dataService.getDataByRequest(request); ArgumentCaptor<DataSearchRequest> requestCaptor = ArgumentCaptor.forClass(DataSearchRequest.class); Mockito.verify(dataService, times(1)).getDataByRequest(requestCaptor.capture()); assertThat(requestCaptor.getAllValues()).hasSize(1); DataSearchRequest capturedArgument = requestCaptor.getValue(); assertThat(capturedArgument.getId()).isNotNull(); assertThat(capturedArgument.getId()).isEqualTo("idValue"); assertThat(capturedArgument.getUpdatedBefore()).isAfterYear(1970); assertThat(capturedArgument.getLength()).isBetween(0, 100); 

ArgumentCaptor , , ArgumentCaptor . getValue() , getAllValues() — . , , .


Mock- Mockito


, mock- , — @Mock - :


 MockitoAnnotations.initMocks(this); 

( , mock', )


spy @Spy@Mock … spy , , ? , — spy .


@Captor ArgumentCaptor — , , .


@InjectMocks . - Mockito, . mock- , . , . - , null , - . ( ) dependency injection.


Mockito


, : mock (spy, argument captor...), , , . , mock' — , . JUnit , , TestNG — . , , mock' , , , . . , , — , .


, mock- . TestNG @BeforeMethod ( @AfterMethod ). mock' , , ( JUnit — @Before ).


, , — Mockito.reset Mockito.clearInvocations . varargs, mock'. , . : (, ) , / mock' , — . , mock' . . , , .


(, ) — MockitoAnnotations.initMocks(this); . "" , Mockito.


— Mockito. . mock- , ( mock' ). , MockitoSession , . TestNG:


 @Mock DataService dataService; MockitoSession session; @BeforeMethod public void beforeMethod() { session = Mockito.mockitoSession() .initMocks(this) .startMocking(); } @Test public void testMethod() { // some code using the dataService field } @AfterMethod public void afterMethod() { session.finishMocking(); } 

, — , "" (, ) , .


?


Mockito: mock spy-, . , . , , :


  • Mockito mock- MockSettings ( — , mock' - );
  • mock-, MockingDetails ;
  • BDDMockito Mockito ;
  • ( JUnit Mockito, ).

Mockito . javadoc' Mockito .


, , .

Source: https://habr.com/ru/post/pt444982/


All Articles