A Foray Into Testing Algos

In my practice with Algorithms, I tend to code in my own editor instead of the built-in editors on LeetCode and AlgoExpert simple because I like the ability to save many different solutions, have an all-in-one store of all the problems I’ve worked on, and because I like the ability to build certain features and tests out myself. And speaking of testing, this is something I have been meaning to get into for a while and I decided to start now because it was just the other day that I had grown to have enough of commenting and uncommenting my tests. It’s time I work to automate my testing and, if I’m being honest, I hardly looked into how it is ‘supposed’ to be done before writing this post so this will be a bit of mutual exploration.
I will show you what I am working with currently, briefly explain the problem and solution, and then craft some way to automate my tests so I don’t have to manually administer each test individually.
Let’s get started.

It’s kind of a mess and shows no real separation of concerns, even for a personal academic project.

Here we have an algorithm that takes in an array of ordered integers as an input and outputs an ordered array of integers where each value in the input is represented by that value squared in the output.
Each value in the input array is a whole number, as alluded to before, but not each number is positive. When a negative number is squared it becomes positive and the significance of that for us is that when we are working with an input array containing negative numbers, we will have to sort the resulting squared array before returning it as our output.

A pretty basic algorithm, but good because it contains some important methods.

This solution really only has two parts to it:

  1. Loop through the input array, multiplying the value of each index with itself, Math.pow(<your_value>, multiple), and push that new value to a variable to hold an array of new values, newArr.
In, out, simple.

The above example shows a case where we are given an array input, we loop through storing the squared values in a separate array and then return that separate array without needing to re-sort it.

O(n log n), the built-in sort will sort our new array in place.

The above example shows a case where we are given an array input, we loop through storing the squared values in a separate array and then sort that array before returning it because our solution’s conditional returns true when it tests the first value in the input array and finds that -5 is less than 0.

Lines 57 & 58 for the previous example.

Now here are all of our tests which you may recognize. In the previous two examples, I would only leave one uncommented at a time to avoid a mess of numbers in my console and I am manually looking to see if the answer Node returns match the answer below each test. Not efficient and very time-consuming when you have a lot of tests and many edge cases, so let’s automate this.
The first thing I want to happen is for all the tests to be executed each time I run my file with Node. This means I need to create a function to hold all of my tests.

There’s a typo, can you find it?
  1. Here we create a function that wraps around all of our tests, and when we run this file with Node this function will execute all our tests on our sortedSquaredArray function.

So far we haven’t simplified anything, we’ve only added 3 lines of code: the function wrapper, the end of the block containing all of our tests, and the call to that test function. Well, what if we created a helper function that takes in our test input and compares it against our expected output, which we already have commented below each test? Let’s try it.

Your DRY alarm should be going off. Don’t repeat yourself.

Ok, so a lot’s changed in this picture so let me go over what happened step-by-step because it really is pretty simple.

  1. I created a function runTest. It will return whether sortedSquaredArray returns the expected value.

It’s important to note here that arrays are stored by reference and as such, each array is unique. That means we have to convert them into something else, like strings, before we can compare.

By storing out tests in an Object they can be referenced with little complexity and identified using the key.

Not bad to start but there are quite a few improvements I think we can make for readability among other things.
First off, we repeat console.log(runTest(input, expectedOutput); many times over and it’s rather hard to read so I’m going to clean that up.

  1. I’m going to put all of our inputs and outputs into an enumerable list. I chose to use an object over an array for this to reduce complexity and because we have a variable number of tests. That is we may add more in the future and it doesn’t matter what order the tests are executed in. Additionally, we can use the key to reference which test produced a particular result.

Now that we know everything works so far, what we should do is pass the appropriate values to our runTest function. Additionally, I will pass the key in as the third argument to runTest so I can reference it in my error message.

  1. We replace the console.log() with a call to our runTest function, passing in the parts of our object, as we enumerate through it. Additionally, I added a third argument which is the object key that I will use as an ID to identify which test we are currently on. We are getting mostly the same information passed into our runTest function so I really only had to alter the messaging to reflect the ID of the test that is currently being performed.

And I’m happy with where the tests are at now. There are still more things we can do to make this neater such as bringing in frameworks like LoDash or Jest or even just separating the tests out into another file and importing all the different versions of our solution in that test file. Then we can test everything out at once, give everything its own speed test, or do whatever else we can imagine. And don’t think I won’t… next article, haha!
Thanks for reading and as always, please feel free to reach out with your thoughts, I’m still figuring things out with best practices and such and I would love to hear your input and thoughts.

Frontend Software Developer and Security Technician with experience in Ruby, Rails, JavaScript, and React. Flatiron Software Engineering Alumni.