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 (
   "fmt"
   "net"
   "net/smtp"
   "strings"
)

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

API authorization through middlewares

When dealing with API authorization based on access tokens, permissions (user can see a list, can create an item, can delete one etc) and/or account types (administrator, moderator, normal user etc), I’ve seen the approach of checking requirements inside the HTTP handlers functions.

This post and attached source code do not handle security, API design, data storage patterns or any other best practices that do not aim directly at the main subject: Authorization through middlewares. All other code is just for illustrating the idea as a whole.

A classical way of dealing various authorization checks is to verify everything inside the handler function.

type User struct {
   Username    string
   Type        string
   Permissions uint8
}

var CanDoAction uint8 = 1

func tokenIsValid(token string) bool {
   // ...
   return true
}

func getUserHandler(c echo.Context) error {
   // Check authorization token
   token := c.Get("token").(string)
   if !tokenIsValid(token) {
      return c.NoContent(http.StatusUnauthorized)
   }

   user := c.Get("user").(User)

   // Check account type
   if user.Type != "admin" {
      return c.NoContent(http.StatusForbidden)
   }

   // Check permission for handler
   if user.Permissions&CanDoAction == 0 {
      return c.NoContent(http.StatusForbidden)
   }

   // Get data and send it as response
   data := struct {
      Username string `json:"username"`
   }{
      Username: user.Username,
   }

   return c.JSON(http.StatusOK, data)
}

The handler is doing more than its purpose. Continue reading API authorization through middlewares

Good behavior with bad consequences

Most companies emphasize on good behavior, being friendly to everybody in order to maintain respect and a calm working environment. Which is really great until they don’t speak up and avoid pointing out flaws.

We all have flaws. Not only things that we should improve, but pure flaws, things we don’t do the proper way. Things that we do wrong! And when you’re not told you’re wrong, you’re not going to improve. If you don’t improve, projects suffer.

Now imagine big companies with a lot of people who are not told what to improve, who are overly protected and bad things are hidden from them. Year after year. They have many directions they can’t level up in.

Being told you’re wrong shouldn’t be offensive to you, instead should raise a flag for yourself and make you ask questions, find answers, learn.

“Obsessing About Best Practices”

Today I read a very good article about some mistakes that one can do as a programmer, and the only idea that made me a little bit uncomfortable was the one about best practices. The author was talking about “Obsessing About Best Practices”. While I agree obsessing is not healthy, I consider his statement “There are no best practices.” is too strong. I think it’s something that one could hear and get too confident about their decisions.

Thinking the things you know today are the best there can be is wrong. Trying to apply everything you’re hyped about is wrong. Giving the same 2 or 3 methods you know as solutions to any problem is wrong. Not being curios to find out what else exists beyond any best practice and beyond your knowledge is fatal.

Still, there are some general best practices that you should at least think of before writing code. There are those SOLID principles that some have no idea about, some guide themselves through them, and for sure some are not very happy about. And many design patterns which were tested for years and years. Again, I agree about not being obsessed with them, but try to understand where they can come in need. And stay up to date. The Singleton was a rock star some years ago, now it smells like a dead body in many misused cases.

Because “There are no best practices.”, projects can take on a really, really unwanted highway. I say they do exist. Just open your mind to see which are the proper ones for your case.