Wiremock

wiremock

Wiremock é uma ferramenta que auxilia a criação de mocks.

Site oficial: http://wiremock.org/

Exemplo simples utilizando o jar do wiremock.

Faça download o jar standalone em http://repo1.maven.org/maven2/com/github/tomakehurst/wiremock-standalone/2.8.0/wiremock-standalone-2.8.0.jar

Para rodar o wiremock, executar o comando java no terminal no mesmo path onde encontra-se o jar:

 
java -jar wiremock-standalone-2.8.0.jar

Por default, a porta é 8080. Para rodar em uma porta específica, basta configurar:

java -jar wiremock-standalone-2.8.0.jar --port 8085

O wiremock será inicializado.

img1

Para criar um mapeamento simples, vamos supor uma requisição GET que retorne code 200 (sucesso) e retorne um json.
para isso, dentro da pasta onde está o jar do mock, dentro de uma pasta mappings, no mesmo path de onde encontra-se o jar do wiremock, devemos criar um arquivo .json mapeando a request, qual o method, a url e ainda qual a response, com o status code e um body.

 
{
  "request": {
  "method": "GET",
  "url": "/alguma/url"
},
"response": {
  "status": 200,
  "body": "Alguma url foi chamada com sucesso!!!",
  "headers": {
    "Content-Type": "text/plain"
  }
}

Nota: para que a alteração faça efeito, é necessário restartar o wiremock, apenas finalizando a execução atual e executando o java -jar novamente.

Fazendo o restart, ao entrar num browser no endereço http://localhost:8085/alguma/url teremos o seguinte resultado:

img2

Uma outra forma de trabalhar com wiremock é mapear num response um outro json como resposta.
Para isso, criamos um novo arquivo .json dentro da pasta mappings, com o seguinte mapeamento:

{
  "request": {
    "method": "GET",
    "url": "/alguma/url2"
  },
  "response": {
    "status": 200,
    "bodyFileName": "result1.json",
    "headers": {
      "Content-Type": "application/json"
    }
  }
}

Note que o response aponta para um arquivo result1.json. Esse arquivo deve ficar numa pasta __files, na mesma altura de onde encontra-se o jar do wiremock.
Esse arquivo possui o seguinte conteúdo:

{
  "campo1": "valor1",
  "campo2": 123,
  "campo3": {
    "campo3.1":"tst",
    "campo3.2":2342342342343434
  }
}

Ao executar então a nova url mapeada http://localhost:8085/alguma/url2 teremos o seguinte resultado:

img3

Exemplo de mock retornando erro:

{
  "request": {
    "method": "GET",
    "url": "/algum/erro"
  },
  "response": {
    "status": 404
  }
}

img4

Alguns status comuns de serem mapeados:
200 ok
201 created
404 not found
403 forbidden
405 method not allowed
500 internal server error
502 bad gateway
503 Service Unavailable

Lista com todos os status: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes

Para mapear o wiremock para que parte do que está na request seja usado como resposta, é preciso configurar o response templating. Para isso, ao executar o comando para inicializar o wiremock, é necessário passar o parâmetro –global-response-templating

java -jar wiremock-standalone-2.8.0.jar --port 8085 --global-response-templating

Exemplo simples de uma request em que o último parâmetro da url será usado como resposta.
Mapeamento da request:

{
  "request": {
    "method": "GET",
    "urlPattern": "/alguma/url3/.*"
  },
  "response": {
    "status": 200,
    "bodyFileName": "result2.json",
    "headers": {
      "Content-Type": "application/json"
    }
  }
}

Nesse caso foi mapeado um pattern de url, onde qualquer chamada a /alguma/url3/ mais algum parâmetro, deverá responder o que está mapeado nesse response.

Mapeamento do response, pegando o valor que está na url utilizando o request path:

{
  "campo1": {{request.path.[2]}},
  "campo2": 123,
  "campo3": {
    "campo3.1":"tst",
    "campo3.2":2342342342343434
  }
}

Note que o parâmetro utilizado é o segundo [2]. O primeiro item da url é mapeado como 0.
Dessa forma temos para 0 a palavra “alguma”, para 1 a palavra “url3” e para 2 o que for digitado na url.
Caso seja solicitada a seguinte url http://localhost:8085/alguma/url3/123456, teremos o seguinte resultado:

img5

Ou ainda, caso seja solicitada a seguinte url http://localhost:8085/alguma/url3/unicorn, teremos o seguinte resultado:

unicorn

#SQN só que não 😛 rs

Errou

Segue o resultado correto:
img6

Além de GET, também é possível mapear POST, DELETE, etc, sempre mantendo a idéia de qual a url ou url pattern da request, qual o method, e qual o response e status code de retorno.
Para mais informações e também outras formas de usar o wiremcock: http://wiremock.org/docs/getting-started/

Exemplo utilizado nesse post pode ser encontrado em https://github.com/reyoko/wiremock-simple-example

Branches no Git vs Branches no Mercurial

git-hg

 

Listar branch corrente

Mercurial Git

hg branch


git branch

Criando branch

Mercurial Git

hg branch -f nome_da_branch


git checkout -b nome_da_branch

Mudando de branch

Mercurial Git

hg update -C nome_da_branch


git checkout nome_da_branch

Commit de alterações na branch

Mercurial Git

hg add

hg commit -m "mensagem do commit"

hg push -b nome_da_branch


git add .

git commit -m "mensagem do commit"

git push

Primeiro push na nova branch

Mercurial

hg push -b nome_da_branch --new_branch

Git

git push --set-upstream origin nome_da_branch

Atualizando branch

Mercurial Git

hg pull

hg update


git pull

Merge alterações do default no branch corrente

Mercurial Git

hg merge default


git pull origin master

Java 8 – Stream e Lambda

java-lambda-expression1

 

Concatenando Streams

 
 
 Stream<String> stringStream1 = 
                Stream.of("AAA", "BBB", "CCC", "ABC");
 Stream<String> stringStream2 = 
                Stream.of("AAA", "BBB", "CCC", "DDD");

 Stream<String> concatStream = 
                Stream.concat(stringStream1, stringStream2);
 concatStream.forEach(s -> System.out.println(s));
 

Stream.concat permite concatenar dois streams. Um vez que temos um stream, podemos executar um forEach no mesmo para iterá-lo. O código acima concatema os dois streams e ainda imprime o valor dos itens após a concatenação.

Outra formas para fazer o forEach com o println:

 
  concatStream.forEach(System.out::println);
 

Outra forma, mais simplificada de fazer o concat e o println.

 
  Stream.concat(stringStream1, stringStream2)
      .forEach(System.out::println)
 

Concatenando apenas ítens não repetidos

 
 stringStream1 =
     Stream.of("AAA", "BBB", "CCC", "ABC");
 stringStream2 =
     Stream.of("AAA", "BBB", "CCC", "DDD");

 // Concat only non repeated values
 Stream.concat(stringStream1, stringStream2)
       .distinct().forEach(System.out::println);
 

Convertendo lista de String em Stream e contando ítens não repetidos

 
 List<String> list =
     Arrays.asList("AAA", "BBB", "CCC", "BBB", "CCC", "AAA", "AAA");
 System.out.println(list.stream().distinct().count());
 

Para os próximos exemplos, vamos utilizar um objeto Person.

 
public class Person {

 private String firstName;
 private int age;
 
 public Person(){
 
 }
  
 public Person(String firstName, int age){
 this.firstName = firstName;
 this.age = age;
 }

 //getters and setters ...
 
}
 

Trabalhando com Listas e Streams

  
 Person hebert = new Person("Hebert", 24);
 Person john = new Person("John", 25);
 Person rafael = new Person("Rafael", 30);
 Person paulo = new Person("Paulo", 35);
 Person alfredo = new Person("Alfredo", 30);

 List<Person> people1 = Arrays.asList(hebert, john, alfredo);
 List<Person> people2 = Arrays.asList(rafael, paulo);
 

Concatenando listas e printando lista final

 
 // concat to a list
 List<Person> peopleList = 
              Stream.concat(people1.stream(), people2.stream())
                    .collect(Collectors.toList());
 peopleList.forEach(p -> System.out.println(p.toString()));
 

Apenas fazendo um paralelo com Java 7, o seguinte código em Java 8 usando forEach

 
 peopleList.forEach(p -> System.out.println(p.toString()));
 

Seria similar ao código abaixo no Java 7:

 
 for (People p : peopleList){
    System.out.println(p.toString());
 }
 

Concatenando como Set

 
 // concat to set 
 people1 = Arrays.asList(hebert, john);
 people2 = Arrays.asList(hebert, john, rafael, paulo);
 Set<Person> set = Stream
                        .concat(people1.stream(), people2.stream())
                        .collect(Collectors.toSet());

Filtrando lista por nome e populando em nova lista (collect to list).

 List<Person> johnList =
          peopleList.stream()
                    .filter(p -> p.getFirstName().equals("John"))
                    .map(p -> new Person(p.getFirstName(), p.getAge()))
                    .collect(Collectors.toList());
 

Soma das idades de uma lista de pessoas, usando stream, mapToInt e sum;

 
 int sum = peopleList.stream().mapToInt(p -> p.getAge()).sum();
 

Calculando média de idade de Pessoas da lista utilizando stream e mapToInt com método average.

 
 OptionalDouble average =
          peopleList.stream()
                    .mapToInt(p -> p.getAge()).average();
 if (average.isPresent()) {
    System.out.println(average.getAsDouble());
 }
 

Contando quantas pessoas da lista possuem nomes terminados em “o”, utilizando o filter, endsWith e collect counting.

 
 long count = peopleList.stream()
                   .filter(p -> p.getFirstName()
                   .endsWith("o"))
                   .collect(Collectors.counting());
 

Retornar primeira pessoa da lista que tenha o nome terminado em “a” utilizando filter, endsWith, findFirst. Caso não encontrado, retorna null.

 
 Person resultPerson = peopleList.stream()
                          .filter(p -> p.getFirstName().endsWith("a"))
                          .findFirst().orElse(null);
 

Filtra lista de pessoas com pessoas com nome terminado em “o” e printa o resultado.

 
peopleList.stream().filter(p -> p.getFirstName()
                   .endsWith("o"))
                   .forEach(System.out::println);
 

Ordena lista de pessoas por primeiro nome

 
 peopleList.stream()
           .sorted(Comparator.comparing(Person::getFirstName));
 

Ordena lista de pessoas por idade na ordem decrescente

 
 peopleList.stream()
           .sorted(Comparator.comparing(Person::getAge)
           .reversed());
 

Trabalhando com branches no Git

maxresdefault

 

Talvez um dos maiores desafios para quem começa a usar o Git é saber como trabalhar com branches.

Até então, enquanto trabalhei com SVN, o conceito de branch era bem diferente. No Git, podemos usar branches para trabalhar isoladamente numa feature ou num bug fixing. Sempre criamos essa branch a partir de alguma origem. Por exemplo, por default, no Git, temos um branch master. Seria como um branch default. Em relação ao SVN seria como o trunk.

É possível trabalhar em muitas formas com o Git. Desde uma forma simples, usando sempre apenas o default, como também usando o Git Flow. Consiste em trabalhar em várias branches paralelas. Podemos ter, por exemplo, uma branch para desenvolvimento, uma para release, uma para hotfix, etc. Além disso, em cima de cada branch dessa, podemos criar branches de “desenvolvimento”. Essas branches sempre serão mergeadas para alguma branch final como master, release, hotfix, etc.

Por exemplo, vamos supor que você vá iniciar uma feature nova, desenvolvendo do zero. Então, a partir da branch master, podemos criar uma nova branch “branch_new_feature”.

Você pode desenvolver nessa branch, e commitar ou dar push normalmente nela.

Umas vez que você deu push e disponibilizou essa branch remotamente, outros usuários também conseguem pegar as alterações dessa branch, mergear em outra branch e continuar o trabalho.

Essa estratégia acaba funcionando bem quando temos features a serem desenvolvidas tanto quando temos mais de um dev na mesma feature quanto quando temos features que são dependentes entre si.

E além disso, garante que seu master fique intacto, sem poluir com features “parciais” que podem conter bugs.

Há quem ache isso ruim, não ter a features disponibilizadas mesmo que parcialmente. Porém, acaba sendo uma boa prática, quando se tem entregas parciais.

De novo, existem n formas de trabalhar com essas ferramentas de versionamento.

Vou citar uma que funcionou bem, num time de 7 desenvolvedores.

Usarei como exemplo um repositório github.

Ao criar repositório, é disponibilizado automaticamente um repositório com branch master.

Diagram1-crop

Criei o repositório, criando por default um arquivo readme. Para um primeiro teste, fiz o clone do repositório:

git clone <url>

Ao executar o comando:

gitk -all

É mostrado um commit inicial com a mensagem mostrada acima.

Modifiquei o readme file, e inseri no repositório (por padrão, estou trabalhando no branch master, sem criar nenhuma nova branch).

Alterei o arquivo e salvei.

Para verificar arquivos alterados, executei o comando:

git status

Para adicionar o arquivo alterado, executei o comando:

git add .

Para comitar o arquivo com mensagem customizada:

git commit -m "changing readme file in master branch"

E dei um push:

git push

Ao verificar no repositório via gitk -all, é possível ver que houve a alteração, com a mensagem de commit que configurei (cada bolinha do diagrama representa uma mudança).

Diagram2-crop

Resolvi então criar uma branch paralela a master, a qual nomiei como release.

git checkout -b release

Modifiquei arquivo, salvei e comitei:

git add .

git commit -m "changing readme file in release branch"

Ao tentar executar push obtive um erro. Como é a primeira vez que tento dar push nessa branch, o comando é um pouco diferente.

git push --set-upstream origin release

Diagram3-crop

Ao visualizar o diagrama do gitk –all, uma nova bolinha foi criada, não mais na reta de master. É uma nova branch, com nome release. E no diagrama acima é possível ver a representação dessa mudança. Na branch release (azul) é possível ver o novo commit, com a mesma mensagem que foi passada no comando.

Vamos supor que agora, eu queria voltar para a branch master. E mais, além de voltar, quero que o que foi feito em release, seja mergeado no master, ou seja, as duas branches terão as mesmas implementações.

Primeiro, preciso mudar de branch, voltar para master:

git checkout master

Para mergear release em master, é necessário fazer um pull:

git pull origin release

Como nas duas branches eu mexi no mesmo arquivo, ocorreu um merge. Resolvi os conflitos e para que esse merge seja finalizado com sucesso, preciso comitar as alterações.

Após salvar arquivo mergeado, executar a seguinte sequência de comandos:

git add .

git commit -m "solving merge"

git push

Diagram4-crop

Ao visualizar como ficou na árvore de alterações, voltamos para master, agora com as alterações de release, conforme o diagrama mostra a bolinha azul voltando para a vermelha.

Agora vamos exemplificar uma alteração mais simples, sem merge.

Para voltar para a branch release, executo o comando de checkout

git checkout release

Vou adicionar um novo arquivo e comitá-lo ainda em release

Crio o arquivo, salvo.

git add .

git commit -m "adding new file in release branch"

git push

Diagram5-crop

Novamente, uma bolinha azul representando release, ainda não vinculada à branch master. Release está à frente de master, ou seja, minha branch de release contém alterações que ainda não existem em master.

Para jogar essas alterações agora em master, devemos dar checkout para master.

git checkout master

Para pegar as alterações de release para master, executar o pull:

git pull origin release

E para finalizar e aplicar essas alterações, executar o push

git push

Veja como fica a representação gráfica no final de toda essa iteração:

FullDiagram

Todo o conteúdo novo de release foi para master.

Essas imagens foram meramente ilustrativas.

Ao executar o comando gitk –all no windows, foi mostrada uma imagem como a que segue abaixo:

gitk

Esse foi apenas um exemplo simples.

Geralmente, em tempo de desenvolvimento, temos vários devs trabalhando em cima de master, por exemplo. Cada um cria sua branch relacionada à sua feature. Conforme vão finalizando as features, as mesmas são integradas em master. Claro que isso pode dar merges, mas funciona bem. Caso você precise gerar entregas intermediárias, você total controle que tudo que está em master, teve seu desenvolvimento finalizado.

Uma outra forma de trabalhar que funciona muito bem, seria ter 3 branches.

Uma de develop, outra de release e uma de hotfix.

Iniciando o Sprint, o Time A começa a trabalhar em cima do develop. Uma vez que todos terminaram as features, começa o processo de validação, migrando então tudo o que estava em develop para release. Todo o Time A agora cria branches em cima da branch release. Fazem correções de bugs em cima dessa branch. Terminada toda a validação e estabilização, é gerada uma entrega e o cliente executa testes ou coloca em produção. Em paralelo a isso o time pode voltar a trabalhar no develop no próximo Sprint. Ou então podemos ter Time B, Time C, etc trabalhando em paralelo (aí está a importância de não se “contaminar” a branch principal, nesse caso, develop). O cliente pode dar feedback de que existe um bug pontual que precisa ser corrigido. Todo o release vai agora para hotfix, o dev vai e corrige em hotfix, libera nova versão, etc. Toda vez que um “processo” desse tipo é finalizado, seja mudar para nova branch ou entrega finalizada, sprint aceito, tudo que está em release e hotfix devem voltar para develop. Todos precisam ficar atentos a sempre estarem no branch certo (isso exige uma comunicação muito boa entre o(s) time(s)).

Com esse tipo de separação de branches também fica mais claro para o time de QA o que validar, quando validar. O que entrou ou não para uma determinada release. Esse modo de trabalhar em branches é conhecido como git flow.

Mas de novo, tudo depende do time. Para alguns times isso é muito difícil de entender, preferem ter um branch só e funciona. Para outros, essa estratégia de ter muitos branches paralelos também funciona super bem. Mas sempre fica uma pessoa encarregada em ajudar o time, comunicar qual a branch que deve ser usada, a release em qual branch vai ser gerada, e além disso, alguém que fique encarregado, ou até mesmo um rodízio no time, de quem vai mergear cada branch onde for preciso a cada etapa do processo, para que nada seja perdido. Lógico que além disso também precisamos ter uma boa integração entre as ferramentas como Jira, Jenkins, etc.

 

Material interessante sobre git flow: https://www.atlassian.com/git/tutorials/comparing-workflows

Git vs SVN vs Mercurial

download (1)

Comparativo comandos básicos

Comando Git SVN Tortoise Mercurial
Clonar git clone <url_repositorio> checkout hg clone <url_repositorio>
Adicionar arquivo git add <arquivo> ou git add . add hg add
Comitar git commit -m “comentário relativo ao commit” commit hg commit -m “comentário relativo ao commit”
Versionar arquivo git push origin master O commit do svn já funciona como commit + push do git ou do mercurial hg push
Atualizar repositório git pull update hg pull e hg update