https://goo.gl/YY3h4g

Quem sou eu?

Ricardo Longa

Graduado em Sistemas de Informação

Pós-graduado em Engenharia de Software

Professor de cursos técnicos, graduação e pós-graduação

Effective Software Craftsman at Neoway Business Solutions

Palestrante desde 2013

Artigo publicado na Devmedia sobre Design Patterns

Quem é você?

  • Nome?

  • Profissão atual? Aonde?

  • Linguagens que já trabalhou?

  • Como conheceu Golang?

  • Algum hobby ou curiosidade?

Cronograma

Dúvidas?

Introdução

Em 2007 a Google deu início ao Go, como um projeto interno, desenvolvido por Rob Pike, Ken Thompson e Robert Griesemer.

Em novembro de 2009 a Google abriu o código fonte da linguagem.

Go permite a escrita de programas procedurais, orientados a objetos ou funcionais.

Uma linguagem de tipagem forte e estática, com inferência de tipos e duck typing*.

* - Se faz "quack" e anda como um pato, então é um pato.

A concorrência em Go é um dos maiores diferenciais da linguagem, veremos em detalhes.

Preparação do ambiente (parte 1)

Execute o comando:

sudo tar -C /usr/local -xzf go1.5.1.linux-amd64.tar.gz

Adicione à variável de ambiente PATH:

/usr/local/go/bin

Exemplo em ~/.bashrc:

export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin

Baixe:

go1.5.1.windows-amd64.msi

O instalador para Windows configurará as variáveis de ambiente automaticamente.

Preparação do ambiente (parte 2)

Precisamos de um diretório de trabalho (workspace).

Este diretório deve possuir outros três sub-diretórios:

  • /$HOME/ambiente/workspace-go/src
  • /$HOME/ambiente/workspace-go/bin​
  • /$HOME/ambiente/workspace-go/pkg

Adicione seu workspace à variável de ambiente GOPATH.

export GOPATH=$HOME/ambiente/workspace-go

Adicione GOPATH/bin à variável de ambiente PATH.

export PATH=$PATH:$GOPATH/bin

IDE

HelloGo

Crie o diretório:

/$GOPATH/src/hellogo

Crie o arquivo:

/$GOPATH/src/hellogo/hello.go

Cole este conteúdo:

Vá para $GOPATH/src e execute:

go run hellogo/hello.go

Sintaxe básica

Não precisamos finalizar nosso código com ponto e vírgula.

func main() {
     fmt.Println("Hello!")
}

Parênteses são opcionais nos if's.

func main() {
     if crazy {     
          fmt.Println("Hello!")
     }
}

Mas todo if deve abrir e fechar chaves.

func main() {
     if crazy {     
          fmt.Println("Hello!")
     }
}

A expressão lógica em um if deve ser sempre verdadeiro ou falso.

Definimos variáveis assim, a tipagem a direita:

func main() {
     var roberta Pessoa
}

Go possui inferência de tipos:

func main() {
     roberta := Pessoa{}
}

Go possui apenas uma estrutura de repetição:

func main() {
     for i, v := range valores {
          // ...
     }
}

Range sobre uma coleção devolve o índice e a cópia do valor:

func main() {
     for i, v := range valores {
          // ...
     }
}

loop infinito e break:

for {
     break
}

Go também permite nomearmos blocos for:

teste:
for {
     break teste
}

O retorno de uma função ou método a direita:

func soma(a int, b int) int {
     return a + b
}

Argumentos do mesmo tipo abreviados:

func soma(a, b int) int {
     return a + b
}

Erro de compilação com variáveis não utilizadas.

func soma(a, b int) int {
     var x int
     return a + b
}

Blank identifier, ignorando um valor...

func main() {
     for _, v := range valores {
          // ...
     }
}

Múltiplos retornos não nomeados...

func getName(uf string) (string, error) {
    name, exists := estados[uf]
    if exists {
        return name, nil
    }
    
    return "", errors.New("Not found.")
}

Múltiplos retornos nomeados...

func getName(uf string) (name string, err error) {
    var exists bool

    name, exists = estados[uf]
    if exists {
        return name, nil
    }
    
    return "", errors.New("Not found.")
}

Go não possui classes, definimos structs.

type Pessoa struct {
     Nome string
}

Os métodos não possuem um receptor implícito, como self ou this em outras linguagens.

Definimos um método privado para Pessoa assim:

type Pessoa struct {
     Nome string
}

 

func (me Pessoa) getNome() string {
    return me.Nome
}

Definimos um método público para Pessoa assim:

type Pessoa struct {
     Nome string
}

 

func (me Pessoa) GetNome() string {
    return me.Nome
}

Argumentos são sempre cópias, exceto slices, maps e channels.

type Pessoa struct {
     Nome string
}

 

func (me *Pessoa) SetNome(nome string) {
     me.Nome = nome
}

Arrays

Arrays são coleções do mesmo tipo e de tamanho fixo e invariável.

var a [3]int
numeros := [5]int{1, 2, 3, 4, 5}
nomes := [...]string{"Ricardo", "Longa"}

Primeiro elemento na posição 0.

numeros := [5]int{1, 2, 3, 4, 5}
primeiro := numeros[0]

Último elemento na posição len(array) - 1.

numeros := [5]int{1, 2, 3, 4, 5}
ultimo := numeros[len(numeros) - 1]

Slices

Um abstração em cima de Arrays, onde não definimos o tamanho da coleção.

var a []int
numeros := []int{1, 2, 3, 4, 5}
nomes := []string{"Ricardo", "Longa"}

A grande vantagem: quando utilizados como argumento ou retorno, são passados por referência.

Para iterar utilizamos o operador range.

numeros := []int{1, 2, 3}

 

for i, v := range numeros {
     numeros[i] = v * 2
}

Podemos omitir a cópia do valor...

numeros := []int{1, 2, 3}

 

for i := range numeros {
     numeros[i] *= 2
}

Podemos omitir a posição e a cópia do valor...

numeros := []int{1, 2, 3}

 

for range numeros {
     // ...
}

Maps

Coleção de pares chave-valor sem ordem definida. As chaves devem ser do mesmo tipo e únicas.

Podemos criar um map da forma literal ou com make().

map1 := map[int]string{}
map2 := make(map[int]string)

Se possível, declare sempre o tamanho do map.

somenteComMake := make(map[int]string, 40)

Verificando se a chave existe no map:

_, encontrado := estados["sc"]
if encontrado {
     // ...
}

Para remover uma chave existente no map:

delete(estados, "sc")

Podemos iterar as chaves e os valores com range:

for sigla, estado := range estados {
     // ...
}

Structs

Go tem suporte à limitado a Orientação a Objetos. Não há suporte a herança mas sim a composição.

Criação de novos tipos:

type Itens []string

func (me *Itens) Categorizar() {
     // ...
}

Criamos um novo tipo estendendo um []string, porém Itens não pode ser passado como []string ou vice-versa. Na prática, são tipos diferentes.

Type conversion - T(x)

func main() {
     []string(Itens{"Pneu", "Freio"})
     Itens([]string{"Pneu", "Freio"})
}

Uma Struct é um conjunto de variáveis que definem um novo tipo.

type Pessoa struct {
     Nome string
     Idade int
}

Podemos instanciar Pessoa destas formas:

pessoa := Pessoa{"Ricardo", 29}
pessoa := Pessoa{Nome: "Ricardo", Idade: 29}

Podemos obter o ponteiro da instância com &:

pessoa := &Pessoa{"Ricardo", 29}
pessoa := &Pessoa{Nome: "Ricardo", Idade: 29}

Podemos definir métodos para Pessoa:

type Pessoa struct {}

func (me *Pessoa) Andar() {}
func (me *Pessoa) Correr() {}

Interfaces

Um contrato de métodos comuns a um ou mais tipos, similar ao Java, por exemplo.

Contudo, não precisamos da palavra implements. Basta que o tipo defina todos os métodos da Interface.

Uma interface:

type Pessoa interface {
     Andar()
     Correr()
     Cumprimentar() string
}

Uma implementação:

type Homem struct {}

func (me *Homem) Andar() {}
func (me *Homem) Correr() {}
func (me *Homem) Cumprimentar() string {
     return "Olá"
}

Duck typing - se faz "quack" e anda como um pato, provavelmente é um pato.

Json

"Quase qualquer tipo válido em Go pode ser serializado, exceto Channels, Functions e Complex." (Caio Filipini, p. 153)

Serializando em JSon:

type Pessoa struct {
     Nome string 
     Idade int
}

pessoa := Pessoa{"Ricardo", 29}

 

json, err := json.Marshal(pessoa)
fmt.Println(string(json))

O resultado em JSon:

{
     "Nome": "Ricardo",
     "Idade": 29
}

E esses atributos?

{
     "Nome": "Ricardo",
     "Idade": 29
}

Struct tags

type Pessoa struct {
     Nome string `json:"nome"` 
     Idade int `json:"idade"`
}

pessoa := Pessoa{"Ricardo", 29}

 

json, err := json.Marshal(pessoa)
fmt.Println(string(json))

Resultado

{
     "nome": "Ricardo",
     "idade": 29
}

Desserializando um JSon:

j := []byte(`{"nome":"Ricardo","idade":29}`)

var pessoa Pessoa
err := json.Unmarshal(j, &pessoa)

Web server c/

https://github.com/gin-gonic/gin

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and server on 0.0.0.0:8080
}

Testing

Os arquivos de testes devem ser nomeados com o sufixo _test.go.

/$GOPATH/src/hellogo/main.go
/$GOPATH/src/hellogo/util.go
/$GOPATH/src/hellogo/util_test.go

Arquivos com o sufixo _test.go serão ignorados na fase de build.

As funções de testes devem conter o prefixo Test e receber um ponteiro de testing.T.

func TestExport(t *testing.T) {
     t.Fail()
}

Para rodar os testes utilizamos o comando go test.

$GOPATH/src/hellogo go test

Podemos importar apenas o pacote assert do projeto Testify. Easy assertions!

 

github.com/stretchr/testify/assert
import "github.com/stretchr/testify/assert"
func TestSomething(t *testing.T) {

     assert.Equal(t, 123, 123, "they should be equal")
     assert.NotEqual(t, 123, 456, "they should not be equal")
     assert.Nil(t, object)
     if assert.NotNil(t, object) {
          assert.Equal(t, "Something", object.Value)
     }

}

Rest API Testing

Podemos facilmente testar APIs Rest utilizando os pacotes net/http e net/http/httptest.

Criamos um http.NewRequest(), um httptest.NewRecorder() e passamos para ServeHTTP(res, req) do Gin.

func TestPost(t *testing.T) {     
     router := gin.New()
     router.POST("/export", controller.Post)


     response := httptest.NewRecorder()

     body, _ := json.Marshal([]byte(`{"nome":"Ricardo"}`))
     request, _ = http.NewRequest("POST", "/export", bytes.NewReader(body))
     router.ServeHTTP(response, request)
     
     assert.Equal(t, response.Code, 400)
}

Ginkgo

Instalando o Ginkgo.

go get github.com/onsi/ginkgo/ginkgo
go get github.com/onsi/gomega

Gerando a suite de testes.

ginkgo bootstrap

Executando a suite de testes.

ginkgo
go test

Adicionando especificações a suíte.

ginkgo generate books

package books_test

import (
    . "/path/to/books"
    . "github.com/onsi/ginkgo"
    . "github.com/onsi/gomega"
)

var _ = Describe("Book", func() {

})

Vamos ver na prática:

  1. Context
  2. It
  3. Expect
  4. BeforeEach
  5. AfterEach
  6. By
  7. Prefix F
  8. Prefix P
  9. Watch -r

Gomega

go get github.com/onsi/gomega

Instalando o Gomega.

gomega.RegisterFailHandler(ginkgo.Fail)

O ginkgo bootstrap já configura o Gomego automaticamente.

Expect(ACTUAL).To(BeNil())
Expect(ACTUAL).To(BeEmpty())
Expect(ACTUAL).NotTo(Equal(EXPECTED))
Ω(result).Should(Equal("foo"))
Ω(result).To(BeTrue())

err := DoSomethingSimple()
Ω(err).ShouldNot(HaveOccurred()) 
#or...
Ω(DoSomethingSimple()).Should(Succeed())

A DSL do Gomego.

func DoSomethingHard() (string, error) {
    // ...
}

result, err := DoSomethingHard()
Ω(err).ShouldNot(HaveOccurred())
Ω(result).Should(Equal("foo"))

 

Ω(DoSomethingHard()).Should(Equal("foo"))
This will only pass if the return value of DoSomethingHard() is ("foo", nil).

Dica: Um atalho...

Agouti

Primeiro vamos iniciar o Selenium Standalone Server.

docker run -d --name selenium --net=host -P selenium/standalone-chrome
#or...
java -jar selenium-server-standalone-2.53.0.jar

Instalando o Agouti.

go get github.com/sclevine/agouti

Uma navegação com Agouti:

page, _ := agouti.NewPage("http://localhost:4444/wd/hub", agouti.Browser("chrome"))
page.Navigate("https://www.google.com.br/")
page.Find("input[name=\"txtSenha\"]").Fill(password)
page.Find(".botaoVermelho").Click()
page.FindByID("txtCPF").Fill(document)
page.Find("input.loginOk:nth-child(4)").Click()

Dúvidas?

Golang

By Ricardo Longa

Loading comments...

More from Ricardo Longa