Unit testing and interfaces

  • Good code needs tests
  • Tests require good design
  • Good design implies decoupling
  • Interfaces help decouple
  • Decoupling lets you write tests
  • Tests help having good code

Good code and unit testing come hand in hand, and sometimes the bridge between them are interfaces. When you have an interface, you can easily “hide” any implementation behind it, even a mock for a unit test.

An important subject of unit testing is managing external dependencies. The tests should directly cover the unit while using fake replacements (mocks) for the dependencies.

I was given the following code and asked to write tests for it:

package mail

import (

func ValidateHost(email string) (err error) {
   mx, err := net.LookupMX(host(email))
   if err != nil {
      return err

   client, err := smtp.Dial(fmt.Sprintf("%s:%d", mx[0].Host, 25))
   if err != nil {
      return err

   defer func() {
      if er := client.Close(); er != nil {
         err = er

   if err = client.Hello("checkmail.me"); err != nil {
      return err
   if err = client.Mail("testing-email-host@gmail.com"); err != nil {
      return err
   return client.Rcpt(email)

func host(email string) (host string) {
   i := strings.LastIndexByte(email, '@')
   return email[i+1:]

The first steps were to identify test cases and dependencies: Continue reading Unit testing and interfaces