TDD — How you do test driven development

Learn
4 min readJan 14, 2018

So we know that testing is tasked at a specific method. Can you provide a bird’s eye view on how that is done?
First you create a test spec file, the file where you write your tests.
Then you import the code that you want to test into your test file.
Then you set the stage with ‘describe’ block indicating the intent of your test, which usually comes with a ‘beforeEach’ block that takes care of setting up the behavior of the particular test run and an ‘it’ block which is where the actual code execution happens and where the expectations are called.

What does ‘setting up behavior’ mean?
So you have a method and you are going to call it in your test. The method is going to unfold and the set of N things that have been written in the method is going to run. Your test is interested in the nitty gritties of how these N things happen. One particular test might want to focus on step m and say that given the step ‘m-1’ returns this particular value, does step ‘m’ do the next thing right? This making the ‘step m-1’ return a specific value is referred to as ‘setting up the behavior’.

Ok. So, firstly, you need to get a handle on step ‘m-1’ to be able to make it return a specific output. How do we get that handle?
Using spies.

What is a spy?
Spy corresponds to a function. Spy of a function has power over the function in that the Spy is able to know everything about how the function was used and also have the function return specific values or throw specific exceptions. The spy spies on the method. A spy would know more details about the suspect he is spying on. In this context, we are spying on a function.

So, when a spy spies a function, what all can the spy see?
A function’s spy can see the arguments of each and every invocation of the function.

Why would it be important to know what arguments were passed into a function?
When you are looking at a method without intending to delve into the depths of the steps it takes, then what really matters is that each and every step gets the right inputs. That is what we are trying to ensure. We are assuming that each of the method does what it is supposed to do. We are employing these methods. We just need to make sure that we are using it right which essentially means we are passing it the right arguments. So that is important from a Spying perspective.

Does being able to say what arguments were used for an invocation correspond to ‘Setting up behavior’ that we had started discussion on?
No, it does not. It falls more into the category of ‘expectations’ from the method.

Does the spy only spy on a method or does it do anything more?
If you think of detective movies, you see sleuths watching someone. You also see them taking their guns out and rushing into the building and manhandling them. They are still only called spies, not spy-cum-shooters. Similarly, our function spies are able to manhandle the function and force it to return specific values or throw specific exceptions.

We spoke about what the spy does? Before that, how do we create a spy?
let methodOneSpy:jasmine.Spy = spyOn(objOne, ‘methodOne’);
objOne — Name of the object on which the spy is to be installed.
methodOne — Name of the method on the object ‘objOne’ that needs to be replaced with a spy.

Ok — now with the spy, how do I effect behaviors?
methodOneSpy.and.returnValue(‘value-returned-in-this-invocation’);
methodOneSpy.and.returnValues[‘value-one’, ‘value-two’, ‘value-three’];

Can you make methods throw exceptions using spies?
Yes. methodOneSpy.and.throwError({a:1, b:2});

So when you have a spy defined on an object for a method and you invoke that method on the object, does the actual method get executed?
Spy by default stubs the method of which it is the spy meaning it does not call the method at all and just tracks it’s invocation arguments .

What if I want the actual method to be called and still want to use the Spy so that I can track the number of calls to the method for instance?
You can use the methodOneSpy.and.callThrough method on the spy to cause the corresponding actual method to get called.

You had referred to ‘expectations’ earlier. What does it mean?
So in your main method, you expect that the unfolding would cause a method M1 to get called k times. To validate that actual findings are in line with your expectations, you use the expect method that the Jasmine library provides. More common term for expectations would be ‘assertions

Example of expect calls.
expect(thing).toBe(realThing)
expect(result).toBeDefined()
expect(result).toBeNaN()

So you have a test spec, you import the code that you want tested, you create describe-beforeEach-It combinations for the various scenarios you want to cover. You create spies for the sub-methods that you want to force return specific values for to test later sub-method. You set up various values for the arguments of your function depending on the scenario you are running. Once the arguments and the stubbed behavior is all setup, you execute the code. Then you specify your assertions using expect statements. Then you run your test and the jasmine framework does the stubbing that you coded into the test and the spies that you had setup watch how many times which methods were called with what arguments. And then the expect statements check whether the actual values that the test data indicate matches with your specified expectation.

--

--