Summary

What is lexical scoping?

What are closures?

Lexical scoping is a technique used in improv

Comedic callbacks and Rule of Three

What is lexical scoping?

Lexical scoping is a computer science concept used to manage and read variables. Lexical scoping is used by the parser (the interpreter of our code) to determine which variables belong to which scope. This is best illustrated with an example:

Some key words:

variable: a value that can change over time, not constant

function: a block of code that can be run by a computer by “invoking” it using parentheses, like putOnPants()

closure: we’ll get to that soon 😉

setup.js

JavaScript

1

2

3

4

5

6

7

8

functionsayMyName(){

let name='Bram';// name is a local variable

functiondisplayName(){// displayName() is the inner function, a closure

console.log(<code>Hi${name}!</code>);// use variable declared in the parent function

You can run this in your Console too! On Chrome you can hit ⌘-Option-J(Ctrl-Shift-J on Windows) and copy pasta the code above. Feel free to have it say your name instead!

What are closures?

Closures are similar to lexical scoping, but provide us different benefits. Above, we called displayName immediately. However, in Javascript, functions are considered as first class objects. All this means is that we can treat functions as objects (an object is a complex topic, but it’s easiest to think of as an entity, like a person or a car). Lets modify our example above a bit:

Key Terms:

return: a return statement is the final line of code in a function. It basically returns the computation done

p2.js

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

functionsayMyNameClosure(name){

// we don't need our local variable anymore since we're passing name as an argument

functiondisplayName(){// displayName() is the inner function, a closure

console.log(<code>Hi${name}!</code>);

}

returndisplayName;// return displayName

}

let sayBram=sayMyNameClosure('Bram');

let sayBradPitt=sayMyNameClosure('Brad Pitt');

sayBram();// call sayBram

sayBradPitt();// call sayBradPitt

What’s happening here is that we’re setting up two separate lexical scopes. These scopes are unique, and both have a different idea of what name evaluates to. In sayBram name = Bram, but in sayBradPitt name = Brad Pitt. Feel free to try these out in your console!

First rule of closures: don’t talk about closures

What does this have to do with improv??

I agree, that’s enough code, let’s focus on comedy here. In improv comedy, the structure usually goes as follows: the audience gives your team a word or a phrase, you spend 20-30 seconds coming up with scenes about it, and then you go wild. The great thing about improv is that it’s all based on Yes, and, which means that everything your team members say is “true” for the scene.

Okay, so let’s say the word is typewriter. I might come up with a scene about how I work at a 1950’s news company, Mad Men style. My partner and I would act the scene, and pay attention to which bits get laughs. We then would store those away with the character for later.

Example:

Scene 1: (Me and one other person) I’m a distraught house husband whose novels aren’t taking off because they’re really bad. However, my wife doesn’t want to shatter my confidence so she tries to stifle her laughter as she reads my book.

Scene 2: (Two other people) A completely different timeline where typewriters are falling from the sky, causing mass hysteria. Kind of like Fahrenheit 451 meets dinosaur asteroid extinction.

Scene 3: (Me and someone from scene 2) We both narrow in on what went well with the audience the last two scenes and really send those jokes to the moon (as in, we make them even bigger and crazier)

What we’re doing here is lexical scoping! We’re remembering who our characters were and what they did, and then were invoking them later, like tellFunnyJokeFromEarlier(). I won’t act as someone else’s character and use their jokes, and they won’t use mine!

Rule of Three

I want to close this out discussing the rule of three. The rule of three basically states that jokes are really funny if you use them three times. The rule of three also applies to photography in the Rule of Thirds (another post for another day), and slogans like Stop, Drop and Roll. Three is just a really memorable number. So using what we’ve learned above we can write improv comedy as code like this:

Environment Variables

If you’re using environment variables that need to be loaded in before your tests run, you can use the –config flag. This flag will allow you to pass in environment variables. Example:

setup.js

JavaScript

1

2

3

4

5

6

(async function(){

console.log('Running Jest Setup init')

// put all process env vars here

process.env.TESTING=true

}()

// this function will evoke immediately

jest.config.js

JavaScript

1

2

3

4

5

6

7

module.exports={

...

'setupFiles':[

'./jest-setup.js'

]

...

}

Mocking Modules vs Mocking Specific Functions

When referring to external libraries, use jest.mock. This is a very powerful function that will mock all exported methods from a module. You can think of it like a window in front of a store. You can see all of the items inside, but you don’t interact with them directly. For more info, check out this link from the Jest docs

Reseting Modules

One of the most annoying things about testing is always having to clean up state after your done. It’s like being a kid again and having to clean your room every day before your friends can come over, even though they were the ones who made it dirty yesterday. Luckily Jest makes this really easy thanks to the resetModules() function. More info can be found at this link. This comes in handy extremely often, don’t be afraid to use it a lot!

FIN

That’s it! Hopefully you took away on or two things from this post. If you’d like to learn more about testing, you can check out my other post on it here: Tests: 5 Tips to Make Coding Less Stressful Jest is a great framework, and if you have any questions about it, please leave a comment below!

Finding Motivation

Avoiding websites like Facebook, Instagram, Youtube and Reddit can feel like a full time job. The internet is a great tool for productivity and knowledge, but all too often it’s a time sink as well. Earlier this week, in a fit equally caused by frustration as much as inspiration, I decided to write a Python script that would allow me to automate the blocking process on my computer, as well as learn something new.

Aside on Willpower

Willpower may or may not be a finite resource. Anecdotally, decision fatigue feels real to me, but it may be just a mindset. I do know, however, that the allure of time sink websites like Facebook and Instagram keep me from important stuff, like y’know, my blog (¯(ツ)/¯). Therefore, for me, it makes sense to have some framework to disallow myself from wasting time, because I have a hard time making that decision by choice.

Summary

The Hosts file (stored at /etc/hosts) is a file that connects IP addresses with human readable names. An IP address is where a site is located, it can kind of be thought of as an actual address.

We can overwrite actual IP addresses with our own! This means that no matter what browser you use and type in someurl.com we can re-route it before the connection is completed

In python to read and write to a file, the mode is ‘r+’

Since the /etc/hosts file is owned by the root directory, we need to use ‘sudo’ to edit it

Editing /etc/hosts isn’t an ideal way to block traffic, since the connection with the server (like facebook.com) is still started. A more thorough way to block traffic is to use IP Table rules. Thanks to this Stack Overflow post for helping me understand this!

Websites like Reddit have a ton of DNS resolutions, so we need to block ALL of them

That There Python Code

Here’s the whole script, we’ll break it down below.

block.py

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

fromsubprocessimportcall

importsys

defblock(blocklist):

print'Blocking sites!'

blocks_added=False

withopen('/etc/hosts','r+')asf:

data=f.readlines()

# iterate over data and see if blocking logic already exists

foridx,line inenumerate(data):

ifline=='#Block These Sites!'orline=='#Block These Sites!\n':

blocks_added=True

ifnotblocks_added:

blocklist_new_lines=map(lambdas:s+'\n',blocklist)

f.writelines(blocklist_new_lines)

else:

print'File already has blocks added'

f.close()

defunblock():

print'Unblocking sites!'

blocks_added=False

block_start=None

withopen('/etc/hosts','r+')asf:

data=f.readlines()

# iterate over data and see if blocking logic already exists

foridx,line inenumerate(data):

ifline=='#Block These Sites!'orline=='#Block These Sites!\n':

print'Found block'

blocks_added=True

block_start=idx

ifblocks_added:

f.seek(0)

foridx,line inenumerate(data):

ifidx<block_start:

f.write(line)

f.truncate()

else:

print'File has no blocks'

f.close()

defmain():

blocklist=[

'#Block These Sites!',

'# -- Block all of Reddit --',

'127.0.0.1 reddit.com',

'127.0.0.1 www.reddit.com',

'127.0.0.1 np.reddit.com',

'127.0.0.1 ssl.reddit.com',

'127.0.0.1 blog.reddit.com',

'127.0.0.1 fr.reddit.com',

'127.0.0.1 pay.reddit.com',

'127.0.0.1 es.reddit.com',

'127.0.0.1 en-us.reddit.com',

'127.0.0.1 en.reddit.com',

'127.0.0.1 ru.reddit.com',

'127.0.0.1 us.reddit.com',

'127.0.0.1 de.reddit.com',

'127.0.0.1 dd.reddit.com',

'127.0.0.1 no.reddit.com',

'127.0.0.1 pt.reddit.com',

'127.0.0.1 ww.reddit.com',

'127.0.0.1 ss.reddit.com',

'127.0.0.1 4x.reddit.com',

'127.0.0.1 sv.reddit.com',

'127.0.0.1 nl.reddit.com',

'127.0.0.1 hw.reddit.com',

'127.0.0.1 hr.reddit.com',

'# -- Block Youtube --',

'127.0.0.1 youtube.com',

'127.0.0.1 www.youtube.com',

'200:2:253d:369e:: youtube.com',

'# -- Block Gmail --',

'127.0.0.1 mail.google.com',

'# -- Block Facebook --',

'127.0.0.1 facebook.com',

'# -- Block Instagram --',

'127.0.0.1 instagram.com'

]

iflen(sys.argv)>1:

ifsys.argv[1]=='True':

block(blocklist)

else:

unblock()

else:

unblock()

call(['dscacheutil','-flushcache'])

if__name__=='__main__':

main()

Python

1

2

fromsubprocessimportcall

importsys

With any script, it’s best to start reading by understanding the dependencies of the project. When you know what libraries a developer used can give you instant insight into the structure of a project. It’s kinda like a table of contents in a way.

Here, we are using sys and subprocess. From subprocess we’re importing call. This method allows us to call terminal commands like ls or nano! From sys, we’re using argv which keeps track of how many arguments we pass in at runtime.

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

withopen('/etc/hosts','r+')asf:

data=f.readlines()

# iterate over data and see if blocking logic already exists

foridx,line inenumerate(data):

ifline=='#Block These Sites!'orline=='#Block These Sites!\n':

blocks_added=True

ifnotblocks_added:

blocklist_new_lines=map(lambdas:s+'\n',blocklist)

f.writelines(blocklist_new_lines)

else:

print'File already has blocks added'

f.close()

Both block() and unblock() function in a similar manner. They open a file, check it for a certain line (# Block These Sites!), and if found either append or remove the following lines, in the cases of block() and unblock() respectively. Therefore, it’s important to append our changes to the bottom of our etc/hosts file to prevent overwriting important host info!

Python

1

2

3

4

5

6

7

8

iflen(sys.argv)>1:

ifsys.argv[1]=='True':

block(blocklist)

else:

unblock()

else:

unblock()

call(['dscacheutil','-flushcache'])

This code block does two things for us, it checks whether a user input true or false as an argument (block and unblock respectively) and then uses the call() method we discussed earlier to flush our cache, registering browsers like Chrome and Firefox to have to ask /etc/hosts for it’s updated state.

The topic of minimalism is pretty large, so this topic may span multiple posts.

Recently, I’ve been really caught up in the idea of minimalism, and of simplicity in general. I’m subscribed to a guy named Matt D’Avella on Youtube. His ideas on creativity and his simple, straightforward style got me really excited to dig deeper into the idea of minimalism. Then I found out he directed a Netflix documentary called The Minimalists. Their doc really got me thinking about not only the material items I keep in my life, but the mindset I have as well. In order to be deliberate, sometimes we must take a step back and examine our previous assumptions. A mindset of minimalism will allow us to do just that.

Hello World is misleading

The simplest programs usually consist of “Hello World”. Here’s that program in three separate languages (Javascript, Ruby, Java respectively):

Hello World in JS

JavaScript

1

console.log('Hello World!')

Hello World in Ruby

Ruby

1

puts'Hello World!'

Hello World in Java

Java

1

System.out.println('Hello World!')

Unfortunately, these programs are misleading. Often, you’ll be looking to solve a bigger problem, and Hello World ain’t gonna cut it. Depending on your goals, the problem can get out of hand VERY quickly.

When the problems become larger, so does their solution space. Often, you’ll need to integrate libraries from external developers, weigh the pros and cons of similar technologies, and write feature after feature while stomping bug after bug. Code becomes an entangled mess, with different parts of the code base inexplicably relying on other parts, etc.

In the production world, the solution to this problem is often microservices. Microservices give us a smaller, deployable solutions to subproblems. This allows for developers to focus on smaller parts of the pipeline. This design pattern works well for certain types of applications but it doesn’t work for all applications.

When we approach writing code in a minimalist mindset, we strive to have clean code. Let’s take a tip from literary minimalism.

Literary minimalism is defined as:

Literary minimalism is characterized by an economy with words and a focus on surface description. Minimalist writers eschew adverbs and prefer allowing context to dictate meaning. Readers are expected to take an active role in creating the story, to “choose sides” based on oblique hints and innuendo, rather than react to directions from the writer.

The important bit is an economy with words and a focus on surface description. Following similar rules in our code, we should strive to be as terse as possible. We should find solutions that are simple and thorough.

Occam’s razor

Occam’s razor is a principle that states that the simplest solution tends to be the correct one.

As put in this amazing video from 1979, computers are meant to eliminate tedium in life. If the problem you’re solving requires a computer, chances are that you’re looking to solve a tedious problem. We can use this to our advantage! Instead of over engineering solutions, we should all strive to solve problems with as little coding kung-fu as possible, favoring readable and understandable over elegant.

Here are a few tips that are language agnostic, to help you code in a more minimalistic fashion.

Avoid comments. Comments equate to clutter generally, as they become less useful over time

Avoid side effects. Easier said than done in the scope of web programming especially, but libraries like Redux can help us centralize our side effects.

Ask yourself if your solution is over engineered! A good way to tell if the solution is too complicated is by using rubber ducking

Arrow functions are fun!

Arrow functions were introduced in ES6, and look like this (=>). When I first saw the arrow, I was confused as to what value it provides. What was wrong with the old way? Am I a bad JS developer if I don’t use arrow functions? I thought that maybe it was just a style thing, to make code more concise. And this was partly true! Arrow functions also do a bunch of other things though too, as we’ll see below.

The power of pointing

When I see an arrow in real life, my first instinct is to turn my head towards the direction its pointing. Generally, this has a reward of more information. Sometimes an arrow points to a fork in the road, or a good restaurant. In JS, the arrow isn’t too far off as an analogy. We look at the sign (arguments in this case), and “turn our heads” to get rewarded with more information. An arrow function is a clear syntactical representation of a function. It also makes it way more clear that functions are considered as first class objects in Javascript.

Back up, what’s a first class object?

A first class object is:

Treated like any other variable

it can be returned or used as an argument

It can be saved into data structures

Why is it more clear? It’s not obvious that the following example is a first class object:

Ye olde function block

JavaScript

1

2

3

functionphoneHome(){

return'Calling home!';

}

It makes sense that this function does something, but did you know you can also use it as a variable? Instead of just invoking phoneHome using phoneHome() we can simply pass it around our program as we would a variable using phoneHome.

With arrow functions, this link becomes a lot clearer. For example, here is the same function as above:

Arrow PhoneHome

JavaScript

1

constphoneHome=()=>'Calling home!';

This is much clearer that our function can operate like any other variable type.

This or that?

The main thing to know about arrow functions is that they inherit the context of the outer scope (wut?). They don’t maintain their own context, they refer to their parent for that information. Back in ye olden days of JS, each function created it’s own this. Basically, this means that each function had it’s own idea of context. Say we want to make a Kindle class in Java. It would probably look something along the lines of this:

Java Kindle

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

classKindle{

Stringname;

Books[]books;

doublebatteryLife;

Booleanreading

publicKindle(name,books,reading){

this.name=name;

this.books=books;

}

publicvoidreadBook(Book book){

open(book);

}

}

What we’ve done above is assigned a class (Kindle) and gave it three attributes/properties (name, books, batteryLife). We then gave it a function readBook(Book book). In JS we could make something similar:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

functionKindle(name,books=[],batteryLife,reading=false){

this.name=name;

this.books=books;

this.reading=reading;

functionreadBook(book){

openBook(book);

this.reading=true;// uh oh!

}

}

vark=newKindle();

The problem here is that our function readBook creates it’s own this, causing it to ignore the one we defined in our Kindle object! We can use arrow functions to defeat this problem, simply by writing readBook(book) {} as const readBook = (book) => {}.

So when you’re making a new object, try to use the old method of Javascript functions. Arrows are best suited for non method functions a fancy term for functions that don’t maintain their own dynamic context. And we want our objects to do exactly that! If I made a Person() class, I’d want const Bram = new Person() and const Ethan = new Person() to operate under two separate contexts. If you want to use a different context or if you want to create your own, using ye olde fashioned way is your best bet.

Basically, arrow functions boil down to two rules:
1. Don’t use them as constructors, it won’t go well because our this. is bound, meaning that our object will use the context from whatever called it
2. Arrow functions are great for functional programming paradigms! Try them for mapping and reducing!

Follow for more kewl coding stuff

Note: This post will already assume that readers know the basics of GraphQL. If you don’t know GraphQL or would like a refresher, I recommend the fantastic tutorial on the Apollo website.

Summary:

GraphQL provides a ton of side benefits beyond its main functionality as a query language for your API

It allows developers to understand their data model better

Keeping up with changes from external APIs becomes trivial

Since GraphQL is agnostic, you can throw it literally anywhere into a pipeline and it will make life easier

Clients relying on you will be impressed by the amount of ground your API covers

GraphQL makes it difficult to hide implementation details from clients

This problem can be minimized by adding another layer of abstraction on your client layer

It’s time for REST to take a rest

GraphQL is great. It helps solve a lot of problems that plague developers today and make the development more arduous for everyone involved. I don’t believe that GraphQL is a complete replacement for REST. However, when working together, GraphQL and REST can make developer and client lives easier.

Imagine that we run a food wiki. Our wiki uses a lot of external data to load in calories, nutrition info, etc. It also has a UI that allows users to choose what is important to them about their food and compare foods against each other. Basically, you can think of our food wiki like a personalized menu. Users who are on diets can search for foods with low fat or carb content. Users who don’t like certain foods can find alternative ones.

When building an app like this, the first thing we notice is that our service has a ton of use cases. Following best REST API practices, we can at most get two collections working in tandem. Our developers will quickly be swallowed by the use cases. How can we predict that a user will want to only look at the Vitamin C content of two fruits, and nothing else? If we use a regular REST API to consume from our data sources, we will have to add logic to filter out the fields we want in the front end. This might look something like:

GET /foodInfo/orange?fields=Vitamin_C

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

// Response from external API www.somefruitwebsite.com/fruit/orange

// we then use our query param 'Vitamin C' to filter out the fields we care about

constresponse={

"fruit":"Orange",

"calories":100,

"nutrition":{

"Vitamin_C":"500mg",

"Protein":"0g",

"Sugar":"14g"

}

};

// ES6 import to get pick from lodash

import{'pick'}from'lodash';

// return only the vitamin C field based on user query

constreturnSubset=(original,args=[])=&gt;pick(original,args);

returnSubset(response,['nutrition.Vitamin_C']);

Yo GraphQL, wanna come out here?

With GraphQL, this problem becomes much less annoying to solve. By leveraging the schema (the representation of our data model) and our resolver (effectively fetching from different places), we can let GraphQL do the heavy lifting of finding and assigning the fields that we care about. Due to this, we can intercept any[1] query a user might want to make. It’s just a graph after all!

Consumption to Production

GraphQL isn’t only great for your clients however. Due to the nature of resolvers, we can achieve a deeper relatinship with our data model. Resolvers are just functions. They don’t care which backend they point to, their only contract is with your schema. This allows developers to knock out two birds with one stone while implementing the resolvers. They’ll be able to understand the fields they get back from a database or an API as opposed to just using it as a proxy. For example, let’s say you have an object in this shape:

JavaScript

1

2

3

4

5

6

7

8

9

10

{

"id":"some_id123",

"food":"Papaya",

"statistics":{

"average_weight":"180g",

"calories":"67"

},

"taste_rating":7

}

Let’s say I want to calculate how much a user might enjoy a food ruit based off of calories and their personal “taste index”. Using resolvers, we can use our context parameter to assign a computation based on the user. We can use our arguments to fetch only the fields that pertain to a user (id and such), and we can use our obj to fetch trivial information (this is generally used when you already are building off of a skeleton. It also really comes in handy if you have a list of ids you want to resolve!) This is all included without putting any overhead onto the developer. You don’t have to think about field coercion, what types things have to be in, how to build a response that’s testable, etc. You can just focus on the implementation! It feels really good to have resolvers sometimes be as simple as these:

1)

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

Food:{

// this should still do error checking of course. Not every result is a 200

Because GraphQL waits for promises to resolve, we don’t even need to worry about keeping track of all the async code we’re running!NB: It also works with Promise.all() and async/await!

GraphQL will automagically pick out the fields you’ve specified in your query to return as well! And the best part is, if they change it to the shape below, we have options with how to deal with it!

We can:
1. If we are only interested in the one field as we are above, we can just change the path (#1 above)
2. Go into the schema and change the data model (#2 above)

JavaScript

1

2

3

4

5

6

7

8

9

10

{

"id":"some_id123",

"food":"Papaya",

"statistics":{

"average_weight":"180g",

"kcals":"67"// changed key name from calories to kcals

},

"taste_rating":7

}

GraphQL the Honey Badger

GraphQL don’t care. GraphQL don’t give a shit.

Because of how agnostic a GraphQL server is, we can also rethink microservices. We can start reframing the architecture diagram that we’re so used to seeing. IBM has a fantastic article on this that I highly recommend. But the idea basically is that we’re used to the idea of exposing an API for every microservice that has it’s own versions and complexities. When you add a GraphQL server layer on top of all of these APIs, teams don’t need to update their code every time a microservice they talk to updates. Simply edit the GraphQL server schema (using schema stitching) and downstream services won’t notice a thing! Everyone will just hit the /graphql endpoint anyway!

And More?!

This article just scratches the surface of why you should want to learn GraphQL. It doesn’t even address mutations, fragments, subscriptions, the info field of the resolver function, conditionals, directives and so on. GraphQL (while annoying to learn at first) is an invaluable tool that can fit anywhere on a pipeline. I highly recommend giving it a go!

[1] not really though. If a user is interested in some query where they want a dump of all the fields, it’s a hassle to build that query with GraphQL. Fragments can help but they don’t solve everything.

Follow for more kewl coding stuff

Why bother testing tho?

Code, like everything else in the universe, is subject to entropy. Code has a unique proclivity to decay literally overnight. Keeping up a project that does more than one trivial thing bug free is difficult, time consuming, and stressful, especially when requirements change and engineers switch hands. This is because all code is bad. Peter Welch wrote a phenomenal article on this topic here. Thankfully, writing tests will make this less painful.

With tests, we can save time for ourselves and a lot of pain. Tired of getting calls in the middle of the night when code fails somewhere? Run your test suite and see what happened. Wrote something months ago and forgot how it works? Run your test suite to see what your code is doing. Added a new feature but not sure if it broke anything else in your app? Run your test suite to see if it did. Good testing is a lot like putting money in savings. It kinda sucks right now because you can’t use it, but you’ll be so glad you did later.

Testing can be anything from a nice to have to critically important. If your code is written for an Reddit bot, people probably won’t notice if it goes down for a few hours. If your code is for a pacemaker, it’s vital that it’s well tested.

Unfortunately, writing tests is extra work. Sometimes, testing feels like you’re writing code twice. It requires time, and a lot of thinking about edge cases, before you’ve even written anything! Yikes. Before you start coding, check out these tips to make writing tests less stressful, no matter what language you’re testing in.

1) Use a paper and pencil

This one is first because it’s honestly the most important. Before you write a single line of code, it’s most important to familiarize yourself with your problem. Break the problem down as simply as possible. Ideally, each section should be broken down into the functions you’ll actually be writing.

Example: We need a web crawler to alert us every time our favorite shoes drop below $50 on Amazon:

We’ll need to connect to Amazon

We need to connect to our shoes page

And receive a successful response

We parse the response for the price

We check that the price is a valid number

We check the price < $50

Alert the user

Our app can send notifications

Our app sends a notification on a successful response only

We handle errors gracefully

This helps us in two ways. First, we can write all of these as test stubs before we actually write any code. Second, it helps us see what we need to write in the first place.

2) Set Up, Call It, and Get Out (SUCIGO)

Another important principle of TDD is that you don’t wan’t to be writing code twice. Unfortunately, test code suffers from entropy as fast as regular code does. The easiest way to prevent this is to make your tests dumb simple. Set up the state you want, call the method with that state, and get out ASAP. If our state is large and hard to follow, consider changing your function signatures to only accept slices of state.

These two functions do the exact same thing. The main difference is the input. We can easily manipulate the state of easy_state_add_5() but manipulating hard_state_add_5() will be harder to do.

3) Arrange, Act, Assert

Similar to tip #2, we want to prevent having to go back and rewrite tests as our codebase evolves. By using the Arrange, Act, Assert method, we can keep our tests simple. This is hard to follow in reality and many times your object under test will be complex. Due to this, multiple assertions can make sense. But the tradeoff is the test will become less clear as to why it failed.

Generally, single assertions are easier to track in the same way that small functions are easier to read.

4) Be lenient with your test cases

Tony Hoare went on record saying that inventing the null pointer was his biggest mistake.

“I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.”

We want our tests to be explicit and straightforward, but we don’t want them to be bottlenecks for development. Make sure to reach all edge cases, but don’t attempt to test code you didn’t write. This includes third party responses from other APIs, databases, and more. Most testing frameworks include ways to spy on and mock calls, so focus on harnessing those skills first. Then you’ll be able to focus just on the computation you wrote, and when errors come up you’re able to quickly deduce if it was because of your code or some extraneous error.

5) Mindset, Mindset, Mindset

The final tip is more meta than the rest, and can’t really be acted upon directly, either. If coding requires critical thinking about the process, testing requires critical thinking about the result. The next time you go off to war with your requirements in hand, different problems and solutions battling for your attention, take the extra time to think about what your goals are, and what tools you’ll need. This is analogous to walking into a tool shed and looking at your choices before you just dive into it with a drill and a prayer.

Testing is about what our programs should do, not about how they should do them. By familiarizing ourselves with different questions, we find different answers. This mindset makes our future engineering efforts more efficient as well.