Escreva o pacote principal do banco

Concluído

Agora que temos o projeto base em execução junto com nosso arquivo de teste, vamos começar a escrever o código que implementa os recursos e requisitos da unidade anterior. Aqui, revisitamos alguns assuntos que discutimos anteriormente, como erros, estruturas e métodos.

Abra o $GOPATH/src/bankcore/bank.go arquivo, remova a Hello() função e vamos começar a escrever a lógica central do nosso sistema bancário online.

Criar estruturas para clientes e contas

Vamos começar por criar uma Customer estrutura onde temos o nome, morada e número de telefone de uma pessoa que quer tornar-se cliente bancário. Além disso, precisamos de uma estrutura para os Account dados. Como um cliente pode ter mais de uma conta, vamos incorporar as informações do cliente no objeto da conta. Basicamente, vamos criar o que definimos no TestAccount teste.

As estruturas de que precisamos podem se parecer com o exemplo de código a seguir:

package bank

// Customer ...
type Customer struct {
    Name    string
    Address string
    Phone   string
}

// Account ...
type Account struct {
    Customer
    Number  int32
    Balance float64
}

Quando você executar o go test -v comando em seu terminal agora, você deve ver que o teste está passando:

=== RUN   TestAccount
--- PASS: TestAccount (0.00s)
PASS
ok      github.com/msft/bank    0.094s

Este teste está a passar porque implementámos as estruturas para Customer e Account. Agora que temos as estruturas, vamos escrever os métodos para adicionar os recursos que precisamos na versão inicial do nosso banco. Estas características incluem depósito, levantamento e transferência de dinheiro.

Implementar o método de depósito

Precisamos começar com um método para permitir adicionar dinheiro à nossa conta. Mas antes de fazermos isso, vamos criar a TestDeposit bank_test.go função no arquivo:

func TestDeposit(t *testing.T) {
    account := Account{
        Customer: Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number:  1001,
        Balance: 0,
    }

    account.Deposit(10)

    if account.Balance != 10 {
        t.Error("balance is not being updated after a deposit")
    }
}

Ao executar go test -vo , você verá um teste com falha na saída:

# github.com/msft/bank [github.com/msft/bank.test]
./bank_test.go:32:9: account.Deposit undefined (type Account has no field or method Deposit)
FAIL    github.com/msft/bank [build failed]

Para satisfazer o teste anterior, vamos criar um Deposit método para nossa Account estrutura que retorna um erro se o valor recebido for igual ou inferior a zero. Caso contrário, basta adicionar o valor recebido ao saldo da conta.

Use o seguinte código para o Deposit método:

// Deposit ...
func (a *Account) Deposit(amount float64) error {
    if amount <= 0 {
        return errors.New("the amount to deposit should be greater than zero")
    }

    a.Balance += amount
    return nil
}

Ao executar go test -vo , você verá que o teste está passando:

=== RUN   TestAccount
--- PASS: TestAccount (0.00s)
=== RUN   TestDeposit
--- PASS: TestDeposit (0.00s)
PASS
ok      github.com/msft/bank    0.193s

Você também pode escrever um teste que confirma que você recebe um erro quando tenta depositar um valor negativo, como este:

func TestDepositInvalid(t *testing.T) {
    account := Account{
        Customer: Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number:  1001,
        Balance: 0,
    }

    if err := account.Deposit(-10); err == nil {
        t.Error("only positive numbers should be allowed to deposit")
    }
}

Ao executar o go test -v comando, você verá que o teste está passando:

=== RUN   TestAccount
--- PASS: TestAccount (0.00s)
=== RUN   TestDeposit
--- PASS: TestDeposit (0.00s)
=== RUN   TestDepositInvalid
--- PASS: TestDepositInvalid (0.00s)
PASS
ok      github.com/msft/bank    0.197s

Nota

A partir daqui, escreveremos um caso de teste para cada método. Mas você deve escrever tantos testes em seus programas quanto se sentir confortável, para que possa cobrir cenários esperados e inesperados. Por exemplo, neste caso, a lógica de tratamento de erros é testada.

Implementar o método de retirada

Antes de escrevermos a Withdraw funcionalidade, vamos escrever o teste para ela:

func TestWithdraw(t *testing.T) {
    account := Account{
        Customer: Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number:  1001,
        Balance: 0,
    }

    account.Deposit(10)
    account.Withdraw(10)

    if account.Balance != 0 {
        t.Error("balance is not being updated after withdraw")
    }
}

Quando você executa o go test -v comando, você deve ver um teste com falha na saída:

# github.com/msft/bank [github.com/msft/bank.test]
./bank_test.go:67:9: account.Withdraw undefined (type Account has no field or method Withdraw)
FAIL    github.com/msft/bank [build failed]

Vamos implementar a lógica para o Withdraw método, onde reduzimos o saldo da conta pelo valor que recebemos como parâmetro. Como fizemos antes, precisamos validar que o número que recebemos é maior que zero e que o saldo na conta é suficiente.

Use o seguinte código para o Withdraw método:

// Withdraw ...
func (a *Account) Withdraw(amount float64) error {
    if amount <= 0 {
        return errors.New("the amount to withdraw should be greater than zero")
    }

    if a.Balance < amount {
        return errors.New("the amount to withdraw should be less than the account's balance")
    }

    a.Balance -= amount
    return nil
}

Ao executar o go test -v comando, você verá que o teste está passando:

=== RUN   TestAccount
--- PASS: TestAccount (0.00s)
=== RUN   TestDeposit
--- PASS: TestDeposit (0.00s)
=== RUN   TestDepositInvalid
--- PASS: TestDepositInvalid (0.00s)
=== RUN   TestWithdraw
--- PASS: TestWithdraw (0.00s)
PASS
ok      github.com/msft/bank    0.250s

Implementar o método de instrução

Vamos escrever um método para imprimir o extrato que inclua o nome, o número e o saldo da conta. Mas primeiro, vamos criar a TestStatement função:

func TestStatement(t *testing.T) {
    account := Account{
        Customer: Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number:  1001,
        Balance: 0,
    }

    account.Deposit(100)
    statement := account.Statement()
    if statement != "1001 - John - 100" {
        t.Error("statement doesn't have the proper format")
    }
}

Ao executar go test -vo , você verá um teste com falha na saída:

# github.com/msft/bank [github.com/msft/bank.test]
./bank_test.go:86:22: account.Statement undefined (type Account has no field or method Statement)
FAIL    github.com/msft/bank [build failed]

Vamos escrever o Statement método, que deve retornar uma cadeia de caracteres. (Você tem que substituir esse método mais tarde como um desafio.) Use o seguinte código:

// Statement ...
func (a *Account) Statement() string {
    return fmt.Sprintf("%v - %v - %v", a.Number, a.Name, a.Balance)
}

Ao executar go test -vo , você verá que o teste está passando:

=== RUN   TestAccount
--- PASS: TestAccount (0.00s)
=== RUN   TestDeposit
--- PASS: TestDeposit (0.00s)
=== RUN   TestDepositInvalid
--- PASS: TestDepositInvalid (0.00s)
=== RUN   TestWithdraw
--- PASS: TestWithdraw (0.00s)
=== RUN   TestStatement
--- PASS: TestStatement (0.00s)
PASS
ok      github.com/msft/bank    0.328s

Vamos passar para a próxima seção e escrever a API da Web que expõe o Statement método.