Scrivere il pacchetto principale del programma per la banca

Completato

Ora che il progetto di base è in esecuzione insieme al file di test, è possibile iniziare a scrivere il codice che implementa le funzionalità e i requisiti dell'unità precedente. Qui rivisitiamo alcuni argomenti illustrati in precedenza, ad esempio errori, strutture e metodi.

Aprire il file $GOPATH/src/bankcore/bank.go, rimuovere la funzione Hello() e iniziare a scrivere la logica di base del sistema di online banking.

Creare le strutture per clienti e conti

Iniziamo creando una Customer struttura in cui abbiamo il nome, l'indirizzo e il numero di telefono di una persona che vuole diventare un cliente bancario. Occorre anche una struttura per i dati dei conti (Account). Poiché un cliente può avere più di un conto, si incorporeranno le informazioni sul cliente nell'oggetto account. In pratica, si creerà ciò che è stato definito nel test TestAccount.

Le strutture necessarie potrebbero avere un aspetto simile all'esempio di codice seguente:

package bank

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

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

Quando si esegue il comando go test -v nel terminale, si dovrebbe vedere che il test viene superato:

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

Questo test viene superato perché sono state implementate le strutture per Customer e Account. Ora che le strutture sono al loro posto, si procederà con la scrittura dei metodi per aggiungere le funzionalità necessarie nella versione iniziale del sistema di online banking. Queste funzionalità includono il versamento, il prelievo e il trasferimento di denaro.

Implementare il metodo di versamento

Il primo metodo da implementare è quello che consente di aggiungere denaro al conto. Prima di procedere, però, occorre creare la funzione TestDeposit nel file bank_test.go:

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")
    }
}

Quando si esegue go test -v, nell'output dovrebbe essere visualizzato un test non superato:

# 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]

Per soddisfare il test riportato sopra, si creerà un metodo Deposit per la struttura Account che restituisca un errore se l'importo ricevuto è uguale o minore di zero. In caso contrario, l'importo ricevuto verrà semplicemente aggiunto al saldo del conto.

Usare il codice seguente per il metodo Deposit:

// 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
}

Quando si esegue go test -v, si dovrebbe vedere che il test viene superato:

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

È anche possibile scrivere un test che verifichi che si riceva un messaggio di errore se si cerca di versare un importo negativo, come nel codice seguente:

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")
    }
}

Quando si esegue il comando go test -v, si dovrebbe vedere che il test viene superato:

=== 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

Da questo punto in poi, si scriverà un test case per ogni metodo. È tuttavia consigliabile scrivere il maggior numero possibile di test per i propri programmi, in modo da coprire sia gli scenari previsti che quelli imprevisti. In questo caso, ad esempio, viene testata la logica di gestione degli errori.

Implementare il metodo di prelievo

Prima di scrivere la Withdraw funzionalità, scrivere il test:

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 si esegue il comando go test -v, nell'output dovrebbe essere visualizzato un test non superato:

# 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]

Implementare la logica per il Withdraw metodo, in cui si riduce il saldo del conto in base all'importo ricevuto come parametro. Come è già stato fatto, è necessario verificare che il numero ricevuto sia maggiore di zero e che il saldo del conto sia sufficiente.

Usare il codice seguente per il metodo Withdraw:

// 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
}

Quando si esegue il comando go test -v, si dovrebbe vedere che il test viene superato:

=== 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

Implementare il metodo per la stampa dell'estratto conto

Si scriverà ora un metodo per stampare l'estratto conto che include il nome, il numero e il saldo del conto. Ma prima occorre creare la funzione TestStatement:

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")
    }
}

Quando si esegue go test -v, nell'output dovrebbe essere visualizzato un test non superato:

# 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]

Scrivere ora il metodo Statement, che dovrebbe restituire una stringa. È necessario sovrascrivere questo metodo in un secondo momento come sfida. Usare il codice seguente:

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

Quando si esegue go test -v, si dovrebbe vedere che il test viene superato:

=== 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

Si passerà alla sezione successiva e si scriverà l'API Web che espone il Statement metodo .