Best Practices for Debugging Javascript Callbacks- How do YOU do it?

Before reading the debugging chapter on FCC I had my own way of debugging code. This “method” I developed I figured out by myself through trial and error. Since I’ve never spoken to anyone about it or had any formal training on it, I don’t know if it is a sound approach or if there are better ways so I’d like to share it with the FCC community and ask for your feedback.

So far this method has served me well and I use it to debug PowerShell. However, since learning JS I’ve ran into problems where this method doesn’t work sometimes. I think the issue is not JS per se but the fact that my JS code is using callbacks, and I don’t think this method is well suited to them (PowerShell doesn’t use callbacks).

There are two goals I want to achieve from this post; one - demonstrate this “method” and get your feedback on it. Do other programmers debug like this or is it flawed? Is there a better approach to it? Two, come up with similar method, which works for callbacks. This will make more sense with an example. Imagine the following basic PS script:


get-process –t | out-file c:\temp\file.txt

For those of you that don’t know PowerShell, the get-process is a command that lists all processes running on the PC and memory allocation etc. The | is known as the pipeline and is used to say you want to pass on the results of get-process to out-file. Therefore, this basic command gets a process list and saves it to a file.

Let’s say this errors (the error is irrelevant), here is my “method” to debug it:

  • I can see that there are two commands and that the second command is dependent on the first command running successfully, so I run the first command at PS terminal by itself and see what happens.

  • At the terminal I type get-process –t and press enter and it errors (the code above is wrong on purpose). I look up the command and find I have a syntax error; the –t parameter doesn’t exist and I shouldn’t be using it so I remove it.

  • I now test it again by typing get-process at the terminal and press enter. It works. I now know this part of my script is working so I run the whole command again.

  • At the terminal I type the full fixed up code get-process | out-file c:\temp\file.txt but it still errors. So now my reasoning is that the next issue is with the out-file command.

  • I follow the same troubleshooting steps above on just the out-file c:\temp\file.txt command until I fix it. Let’s say the error was a typo and it now works.

  • Since both commands now work individually, I go back and re-run the original command as a final test and it works.

I am assuming that the above approach is what others use too before loading up a full-blown debugger; when the code is simple and you can easily trace the steps there is no need to perform debugging. Notice I say “I am assuming” as I don’t know any coders I can ask to confirm this, can some please confirm if my assumption is correct? UPDATE ON THIS – I did some researching and came across the concept of Unit Testing so I think I have been doing this without realising, is this correct?

NOTE: For those of you that know PS you may notice that my code above isn’t entirely accurate; I am aware of this and don’t need you to correct me - the point of the example is to show you my debug approach, not fix the code.

Now let’s imagine the code is more complex (but not by much). Let’s say the code has 10 functions. Some of those functions will return their outputs to other functions. Imagine that one function in particular ultimately calls all the other functions through chaining. Now in order to troubleshoot this function, each time I make a change to it I need to call it again, which means it calls all other functions again. This can be tedious and sometimes just one of those 10 functions may take ages to run, ie maybe it downloads a large JSON file. Let’s say I want to bypass this one function that downloads the JSON file to speed up my troubleshooting. What I’ll do instead is run that function and return it’s results to a temp variable. I’ll then pass that variable into wherever the previous function was called. This allows me to continue my troubleshooting without actually calling the time consuming JSON function. Once I finally fix the issue, I then go back, delete the temp variable and revert the code to call the JSON function again. My question is, is this approach normal? do others do it? If not, then what is a better approach?

Now that I’ve explained all the above we are coming to the main reason I raised this post; this method doesn’t work with callback functions. The reason for this (I think) is because they do not return a value like normal functions do; therefore I can’t output the results of the first function to a variable, which I will then later pass on to the callback function to unit test it . So how do I unit test the callback function only if it is expects an argument (from the function that called it) and I can’t give it one? If I was somehow able save the results from the previous function to a variable (like with normal functions), I could test the callback function by itself by passing this variable in as an argument; but since the original function doesn’t support returning a value I can’t do this. Surely this would be a major drawback to debugging callbacks so I’m guessing there is an equivalent way to do this and I just don’t know it yet. Or maybe I am looking at this the wrong way. Any ideas?

I know this is a massive post but please try to answer all my questions as each one of them has been bugging me for some time now.

Thanks,

1: Do other programmers debug like this or is it flawed?
Some do some don’t. What works for me is that I try to read the errors it gives me and try to understand why it happens. Also learning about, JS itself for me works well. Since JS can be tricky for example: Null says it is an object in the error but, it is not it’s like “hello” or 2 a primitive value.

2: Is there a better approach to it? There are many way’s to debug a code
See the list bellow of a few common debug tools.

  1. Sublime Web Inspector. If you use Sublime Text Editor for your JavaScript editing, then Sublime Web Inspector allows you to debug from the same program you edit with. …
  2. Js Bin. JS Bin is an open source collaborative web development debugging tool. …
  3. JavaScript Debug . …
  4. Theseus. …
  5. JS Hint. …
  6. Aardwolf. …
  7. Debug . …
  8. Vorlonjs.

You’ve kinda figured out the basics here, which is good – the issue you’ll have is that you’re just basically poking your programs with a stick. Which is fine for a while, but people have been doing this for a while and there are tools built to make your life easier.

A test runner is what you want to have set up here. It does what it says in the tin: you write a set of functions that run your functions, and the test runner runs them in order, comparing the result with what you expect, and it displays the responses (most importantly, a list of the ones that have failed).

Normally you pair that with an expectations library (and optionally extra testing libraries), which will provide some useful language/library specific functions you can use in your tests.

So to unit test, instead of manually writing out what you’re doing at the minute, you do something like (I’m using the syntax of the JavaScript Jest test runner, which comes with a built in expectations library)

// describe is optional, denotes a block of
// things testing one thing, it just prints a
// nice message and splits things up:
describe ("add function", () => {
  // "it" (or "test") denotes one specific test:
  it("adds two integers", () => {
    // "expect" is from the expectations library,
    // just makes it easier to read than
    // `add(1,1) === 2`. In starts to make more
    // sense when the thing being tested is
    // more complex
    expect(add(1, 1)).toEqual(2);
  });

  it("adds two floats", () => {
   expect(add(0.1, 0.1).toEqual(0.2);
  });

  it("adds an integer and a float", () => {
    expect(add(1, 0.1).toEqual(1.1);
  });

  it("throws if attempted with non-numeric input", () => {
    expect (() => add(true, 1)).toThrow();
    expect (() => add(2, "test")).toThrow();
    expect (() => add([], 3)).toThrow();
    expect (() => add(4, {})).toThrow();
  });
});

For asynchronous, callback code, the test runner should handle it transparently, so for example:

it("should return something eventually", async () => {
  expect(await asyncAdd(1, 2)).toEqual(3);
});

So you should be able to test callback/async code almost as easily as synchronous code.


However, you need to bear in mind that a unit test tests one unit of code, hence the name. And

That you think this probably indicates your code needs rewriting. You should be able to unit test each one of those functions in isolation, just passing them test values and getting the output you expect.

And if you need to test a sequence of functions that all depend on each other, then you just test the outer interface. All the functions go into some other function that calls the sequence. You then put input into that function and test it produces the correct output. In this case (an integration test) you should not care how the internal functions work. They can be covered by unit tests if necessary, all you should care about is if an input produces the expected output.

There’s a lot more to this (and note debugging in JS is pretty good, the browser tools are generally excellent). Plus there are other tools, for example Typescript (which is fantastic and can be absolutely invaluable), but I’ll stop there for the minute