TDD Changed My Life

TDD Changed My Life

Test-driven development is a software development process that relies on the repetition of a very short development cycle: requirements are turned into very specific test cases, then the software is improved to pass the new tests, only.

Test-driven development is a software development process that relies on the repetition of a very short development cycle: requirements are turned into very specific test cases, then the software is improved to pass the new tests, only.

It’s 7:15 am and customer support is swamped. We just got featured on Good Morning America, and a whole bunch of first time customers are bumping into bugs.

It’s all-hands-on-deck. We’re going to ship a hot fix NOW before the opportunity to convert more new users is gone. One of the developers has implemented a change he thinks will fix the issue. We paste the staging link in company chat and ask everybody to go test the fix before we push it live to production. It works!

Our ops superhero fires up his deploy scripts, and minutes later, the change is live. Suddenly, customer support call volume doubles. Our hot-fix broke something else, and the developers erupt in synchronized git blame while the ops hero reverts the change.

Why TDD?

It’s been a while since I’ve had to deal with that situation. Not because developers stopped making mistakes, but because for years now, every team I’ve led and worked on has had a policy of using TDD. Bugs still happen, of course, but the release of show-stopping bugs to production has dropped to near zero, even though the rate of software change and upgrade maintenance burden has increased exponentially since then.

Whenever somebody asks me why they should bother with TDD, I’m reminded of this story — and dozens more like it. One of the primary reasons I switched to TDD is for improved test coverage, which leads to 40%-80% fewer bugs in production. This is my favorite benefit of TDD. It’s like a giant weight lifting off your shoulders.

TDD eradicates fear of change.
On my projects, our suites of automated unit and functional tests prevent disastrous breaking changes from happening on a near-daily basis. For example, I’m currently looking at 10 automated library upgrades from the past week that I used to be paranoid about merging because what if it broke something?

All of those upgrades integrated automatically, and they’re already live in production. I didn’t look at a single one of them manually, and I’m not worried at all about them. I didn’t have to go hunting to come up with this example. I popped open GitHub, looked at recent merges, and there they were. What was once manual maintenance (or worse, neglect) is now automated background process. You could try that without good test coverage, but I wouldn’t recommend it.

What is TDD?

TDD stands for Test Driven Development. The process is simple:

Red, Green, Refactor

  1. Before you write implementation code, write some code that proves that the implementation works or fails. Watch the test fail before moving to the next step (this is how we know that a passing test is not a false positive — how we test our tests).
  2. Write the implementation code and watch the test pass.
  3. Refactor if needed. You should feel confident refactoring your code now that you have a test to tell you if you’ve broken something.

How TDD Can Save You Development Time

On the surface, it may seem that writing all those tests is a lot of extra code, and all that extra code takes extra time. At first, this was true for me, as I struggled to understand how to write testable code in the first place, and struggled to understand how to add tests to code that was already written.

TDD has a learning curve, and while you’re climbing that learning curve, it can and frequently does add 15% — 35% to implementation times. But somewhere around the 2-years in mark, something magical started to happen: I started coding faster with unit tests than I ever did without them.

Several years ago I was building a video clip range feature in a UI. The idea was that you’d set a starting point and an ending point for a video, and when the user links to it, it would link to that precise clip rather than the whole video.

But it wasn’t working. The player would reach the end of the clip and keep on playing, and I had no idea why.

I kept thinking it had to do with the event listener not getting hooked up properly. My code look something like this:

video.addEventListener('timeupdate', () => {
  if (video.currentTime >= clip.stopTime) {
    video.pause();
  }
});

Change. Compile. Reload. Click. Wait. Repeat.

Each change took almost a minute to test, and I tried a hilariously large number of things (most of them 2–3 times).

Did I misspell timeupdate? Did I get the API right? Is the video.pause() call working? I’d make a change, add a console.log(), jump back into the browser, hit refresh, click to a moment before the end of the clip, and then wait patiently for it to hit the end. Logging inside the if statement did nothing. OK, that’s a clue. Copy and paste timeupdate from the API docs to be absolutely sure it wasn’t a typo. Refresh, click, wait. No luck!

Finally, I placed a console.log() outside the if statement. “This can’t help,” I thought. After all, that if statement was so simple, there’s no way I could have screwed up the logic. It logged. I spit my coffee on the keyboard. WTF?!

Murphy’s Law of Debugging: The thing you believe so deeply can’t possibly be wrong so you never bother testing it is definitely where you’ll find the bug after you pound your head on your desk and change it only because you’ve tried everything else you can possibly think of.
I set a breakpoint to figure out what was going on. I inspected the value of clip.stopTime. undefined??? I looked back at my code. When the user clicks to select the stop time, it places the little stop cursor icon, but never sets clip.stopTime. “OMG I’m a gigantic idiot and nobody should ever let me anywhere near a computer again for as long as I live.”

Years later I still remember this because of that feeling. You know exactly what I’m talking about. We’ve all been there. We’re all living memes.

Actual photos of me while I’m coding.

If I was writing that UI today, I’d start with something like this:

describe('clipReducer/setClipStopTime', async assert => {
  const stopTime = 5;
  const clipState = {
    startTime: 2,
    stopTime: Infinity
  };
  assert({
    given: 'clip stop time',
    should: 'set clip stop time in state',
    actual: clipReducer(clipState, setClipStopTime(stopTime)),
    expected: { ...clipState, stopTime }
  });
});

Granted, superficially, that looks like a whole lot more code than clip.stopTime = video.currentTime. But that’s the point. This code acts like a specification. Documentation, along with proof that the code works as documented. And because it exists, if I change the way I position the stop time cursor on the x, y axis, I don’t have to worry about whether or not I’m breaking the clip stop time code in the process.

Note: Want to write unit tests like this? Check out “Rethinking Unit Test Assertions”.
The point is not how long it takes to type this code. The point is how long it takes to debug it if something goes wrong. If this code broke, this test would give me a great bug report. I’d know right away that the problem is not the event handler. I’d know it’s in setClipStopTime() or the clipReducer() which implements the state mutation. I’d know what it’s supposed to do, the actual output, and the expected output — and more importantly — so would a coworker, 6-months into the future who’s trying to add features to the code I built.

One of the first things I do in every project is set up a watch script that automatically runs my unit tests on every file change. I often code with two monitors side-by-side and keep my dev console with the watch script running on one monitor while I code on the other. When I make a change, I usually know within 3 seconds whether or not that change worked.

For me, TDD is far more than a safety net. It’s also constant, fast, realtime feedback. Instant gratification when I get it right. Instant, descriptive bug report when I get it wrong.

TDD Taught Me How to Write Better Code

I’m going to admit something embarrassing: I had no idea how to build apps before I learned TDD with unit testing. How I ever got hired would be beyond me, but after interviewing hundreds and hundreds of developers, I can tell you with great confidence: there are a lot of developers in the same boat. TDD taught me almost everything I know about effective decoupling and composition of software components (meaning modules, functions, objects, UI components, etc.)

The reason for that is because unit tests force you to test components in isolation from each other, and from I/O. Given some input, the unit under test should produce some known output. If it doesn’t, the test fails. If it does, it passes. The key is that it should do so independent of the rest of the application. If you’re testing state logic, you should be able to test it without rendering anything to the screen or saving anything to a database. If you’re testing UI rendering, you should be able to test it without loading the page in a browser or hitting the network.

Among other things, TDD taught me that life gets a lot simpler when you keep UI components as minimal as you can. Isolate business logic and side-effects from UI. In practical terms, that means that if you’re using a component-based UI framework like React or Angular, it may be advantageous to create display components and container components, and keep them separate.

For display components, given some props, always render the same state. Those components can be easily unit tested to be sure that props are correctly wired up, and that any conditional logic in the UI layout works correctly (for example, maybe a list component shouldn’t render at all if the list is empty, and it should instead render an invitation to add some things to the list).

I knew about separation of concerns long before I learned TDD, but I didn’t know how to separate concerns.

Unit testing taught me about using mocks to test things, and then it taught me that mocking is a code smell, and that blew my mind and completely changed how I approach software composition.

All software development is composition: the process of breaking large problems down into lots of small, easy-to-solve problems, and then composing solutions to those problems to form the application. Mocking for the sake of unit tests is an indication that your atomic units of composition are not really atomic, and learning how to eradicate mocks without sacrificing test coverage taught me how to spot a myriad of sneaky sources of tight coupling.

That has made me a much better developer, and taught me how to write much simpler code that is easier to extend, maintain, and scale, both in complexity, and across large distributed systems like cloud infrastructure.

How TDD Saves Whole Teams Time

I mentioned before that testing first leads to improved test coverage. The reason for that is that we don’t start writing the implementation code until we’ve written a test to ensure that it works. **First, write the test. **Then watch it fail. Then write the implementation code. Fail, pass, refactor, repeat.

That process builds a safety net that few bugs will slip through, and that safety net has a magical impact on the whole team. It eliminates fear of the merge button.

That reassuring coverage number gives your whole team the confidence to stop gatekeeping every little change to the codebase and let changes thrive.

Removing fear of change is like oiling a machine. If you don’t do it, the machine grinds to a halt until you clean it up and crank it, squeaking and grinding back into motion.

Without that fear, the development cadence runs a lot smoother. Pull requests stop backing up. Your CI/CD is running your tests — it will halt if your tests fail. It will fail loudly, and point out what went wrong when it does.

And that has made all the difference.

Want to Learn More About TDD?

TDD Day is an all-day live webinar Friday June 14, 2019, brought to you by DevAnywhere.io and Leanpub.

You’ll learn:

  • Why TDD has taken over
  • Economics of software quality
  • Unit vs functional vs integration tests
  • 5 questions every unit test must answer
  • TDD the RITE way
  • Mocking is a code smell
  • Why testable software leads to better architecture
  • Causes of tight coupling
  • How to do more with pure functions
  • Unit testing React components

Register now.

8 JavaScript Testing Tools You Should Know in 2020

8 JavaScript Testing Tools You Should Know in 2020

8 JavaScript Testing Tools You Should Know in 2020. In this JavaScript testing tools tutorial, I will take you through reviews and analyses of the 2019 State of JavaScript report to show you JavaScript testing frameworks that will take center stage in 2020. What testing framework do you use: Jest, Mocha, Storybook, Cypress, Enzyme, Ava, Jasmine, Puppeteer,

In this piece, I will take you through reviews and analyses of the 2019 State of JavaScript report to show you JavaScript testing frameworks that will take center stage in 2020.

State of JavaScript

In the wake of 2019, we’ll take a look at the state of JavaScript as reflected in insights from State of JS, which surveyed over 21,000 developers across the world.

What is StateOfJS?

State of JS is basically an annual survey of everything JavaScript where opinions of developers who take the survey are recorded and beautifully visualized to show insights that signify the state of JavaScript in that year.

The data points cover frontend web development frameworks, databases, state management, relationships between frameworks, reception and ease of use, languages that compile to JavaScript, mobile frameworks, build tools, JavaScript testing tools, and a whole lot more.

It is a very comprehensive JavaScript survey that was started in 2016 by Sacha Greif and a few other helpers. Now it has over 21,000 developer responses in this latest edition.

Why StateOfJS?

In my opinion, State of JS is actually the very first JavaScript-only developer survey that is universally respected as far as the JavaScript community is concerned.

There are other very popular surveys like the StackOverflow developer survey, which draws on over 100,000 respondents. Jetbrain’s State of Developer Ecosystem Report is also pretty comprehensive, with over 6,000 respondents.

Testing tools

According to survey insights, these are the most in-demand testing tools by JavaScript developers in 2019 arranged in the order of importance determined by awareness, interests, and satisfaction.

Jest


Jest is one of the most popular JavaScript testing frameworks built and constantly maintained by the team at Facebook. It is a zero-configuration JavaScript testing framework recommended by React and the most easy to use.

Jest has a very impressive acceptance rate in 2018 and even more so this year among the JavaScript community. Jest is currently being used by over 1.5 million projects, according to GitHub data.

In the last 12 months, the retention rate has increased drastically from 39% to 61%. This is a recurrent growth rate, as it also increased from 2017 to 2018.

Also the percentage of people interested in Jest has reduced. The number is now at 22%, down from 29%. Most of them are now return users as shown by the retention insights.

Mocha

Mocha has been one of the most-used JavaScript testing frameworks for years now. It’s used with third party assertions, mocking, and spying tools like Chai or Enzyme. Aside from its massive ecosystem, Mocha has well-established options with a great documentation.

It is very flexible and open to a lot of extensions. Mocha has been used by over 900,000 projects, according to GitHub data.

According to the survey insights, the retention rate has grown from 39% to 42% in the last 12 months. About 8% of respondents have not heard about Mocha, and there is an awareness gap which Mocha is constantly filling, because last year the percentage was 10.

Storybook


Storybook is a development environment for UI components.

It allows you to browse a component library, view the different states of each component, and interactively develop and test components. It has been starred about 44,000 times on GitHub and has a massive userbase.

The retention rate of using Storybook doubled from 15% to 32% in 12 months, and the interest level also rose from 23% to 26% in the same timeframe.

Storybook was not even on developers’ radar last year, but there has been massive growth in 2019 and heading into 2020. It will be an interesting year.

Cypress

Cypress is a very fast and reliable JavaScript testing tool. It basically injects tests into a web application, provides a very clear and extensive documentation, and is in constant competition with another testing framework called TestCafe.

It is being used by over 25,000 projects, according to GitHub data.

The retention rate is 23%, and the interest rate is 28% for Cypress. There is an awareness gap, which Cypress is working to fill.

Enzyme

Enzyme is a JavaScript Testing utility for React that makes it easier to test your React Components’ output. You can also manipulate, traverse, and in some ways simulate runtime given the output.

Enzyme’s API is meant to be intuitive and flexible by mimicking jQuery’s API for DOM manipulation and traversal.

The retention rate grew from 20% to 23% in the last 12 months. There is also a bridge of awareness gap all the way from 46% to 38%.

Ava


Ava is a test runner for Node.js with a concise API, detailed error output, embrace of new language features, and process isolation that lets you write tests more effectively.

With Ava, you can ship more awesome code. It is currently being used by 47,000 projects, according to GitHub data.

Last year, about 72% of the JavaScript developers had heard of Ava. This year, that number has reduced to 69%, while the retention rate has grown to 5%.

Jasmine


Jasmine is a Behavior Driven Development testing framework for JavaScript.

It does not rely on browsers, DOM, or any JavaScript framework. As a result, it is suited for websites, Node.js projects, or anywhere that JavaScript can run.

According to survey insights, the retention rate slightly increased in the past 12 months. However, there is a growing gap in awareness, which Jasmine can try to bridge in 2020.

Puppeteer


Puppeteer, built by the team at Google, is a Node library that provides a high-level API to control Chrome or Chromium over the DevTools Protocol.

Puppeteer runs headless by default, but it can be configured to run full (non-headless) Chrome or Chromium. It’s also fast, since it’s native.

The survey insights show a retention rate of 24.3% for Puppeteer, and an interest rate of 24%. There is a need for more awareness for the product, although it is already being used by over 55,000 projects.

In summary

Puppeteer and Cypress were new entrants identified by JavaScript developers this year. Since 2016, it’s been Mocha, Jasmine, Jest, Enzyme, and Ava.

It seems there is a kind of even distribution of developers-to-testing-tools, although Jest seems to be leading the market with over 96% of its users being return users.

What testing framework do you use? Tell me down in the comments section.

Understanding Test-Driven Development with Javascript

Understanding Test-Driven Development with Javascript

In this Javascript tutorial takes you through the development of a simple feature in a test driven way. The basic ideas of TDD (Test-Driven Development) are shown, with explanations of why they might be useful.

In this talk Roy Osherove takes you through the development of a simple feature in a test driven way. The basic ideas of TDD are shown, with explanations of why they might be useful. We'll also do some refactoring for the tests and the production code. This talk is based on the upcoming book "The Art of Unit Testing in Javascript" by Roy Osherove.

Testing your JavaScript Code — TDD

Testing your JavaScript Code — TDD

In this article, I will be making use of the Jest testing framework. There are many other frameworks for testing but my current favourite is Jest and I will be using it in this article.

In this article, I will be making use of the Jest testing framework. There are many other frameworks for testing but my current favourite is Jest and I will be using it in this article.

First things first, **what is testing? **Testing (i.e software testing) is an investigation conducted to provide stakeholders with information about the quality of the software product or service under test.

That might have sounded like a lot, let me break it down. Software testing involves the process of checking your code either manually or automatically (well semi-automatically) to make sure the code functions as needed.

The test we will be referring to in this article is the automated test, the tests you want to create to help you find all (or as many as possible) edge cases in your code. I will talk more on the automated tests soon, but first let’s talk about why we test codes.

Types of Test

  • **Unit testing — **is the level of testing that checks individual units (components) of our code in isolation from others, purpose is to ensure each unit of the code functions as designed.
  • **Integration testing — **is the level of testing that combines units as a group and tests how they work together, purpose is to expose the faults that might exist in the interaction individual units.
  • **End-to-End testing — **is the level of testing that checks the flow of a system from beginning to end as a single application and that it behaves as expected, purpose is to identify system errors and strengths and to ensure that the data on the system is secure across all levels of the system.

Why we should test our code

Main reason for testing your code is to have a very stable code, you want your code to do exactly what you want it to do, and you do not want to have to do this test manually, by checking each part. What you do is called an automated test, you use testing frameworks like **Jest. **You write a test suite expecting your code (function) to work in a certain way or return a particular value, if your code does pass this, you know you need more edge cases in your test, except you have enough and have covered it all, but when your test fails, you go back to your code / functionality, you refactor the code and test again — this process is called regression testing.

The popular saying in testing is that your test is meant to fail the first time you run the test.

TDD — Test Driven Development

This is the process of developing a product (software), that relies on repetitions of test and develop cycle, the automated test is meant to come first and fail first time it is run, then you write the function for the test.

The following sequence of steps is generally followed:

  • Add a test
  • Run all tests and see if the new one fails (if there are old tests)
  • Write some code
  • Run tests
  • Refactor code
  • Repeat

The way the Testing framework (in our case — jest) knows the file that it should read the tests from is by checking for any file with the .test.jsor .spec.js extension. I must warn you, TDD requires much more consideration and time than ‘regular’ programming.

Let’s setup

To be able to perform all tests below or even write your own tests, you need to setup your environment for that.

// In your terminal do the following
$ npm init

$ npm install --save-dev jest
// Then in your package.json file
// change the value of test in scripts to jest

{
  "scripts": {
    "test": "jest"
  }
}

Enough talk, let’s get our hands dirty — not literally. We will write test for a mini project I created earlier to explain OOP, I already did the testing while building the mini project, I will simply be walking you through what testing is about and how you can perform various tests, then I will show you what I did while testing an earlier created project.

The first thing I did was check that when I create an instance of a Constructor function, it does work. That way I am sure that my constructor is created properly and then I can continue my work from that point.

Then I wrote my constructor function, and keep refactoring it, until it passed my above test.

Let us take a small detour, I will do us a small test and sum function here, so you can see the entire process. First thing we will do is to write a test we think should work for a sum function, which should be something like 2+3 = 5.

So first thing as best practise is to write the test first;

const sum = require('./sum') // assuming name of file will be sum 
                            // and they are in the same directory
test(‘Add 1 and 2 to give 3’, () => {
    expect(sum(1,2)).toBe(3);
});

This will fail the first time because we are yet to write our sum function, then we will write the sum function to make sure we pass this test

function sum(a, b) { return a + b }
module.exports = sum; // so we can import it in the Jest file

This is straightforward and as we can see this will work if we test in our console or even terminal (maybe using node). Now we need to consider edge cases, for instance there is this one you may or may not know,

0.1 + 0.2 = 0.3 // wrong
0.1 + 0.2 = 0.30000000000000004 // correct and this is not only in JavaScript

So how to we test for this edge case, we can go ahead using other matchers to resolve that kind of issue, we can also check to be sure it returns a value (crooked way to go). For this, we will check to make sure it returns a value like 0.300…4.

test(‘Add 0.1 and 0.2 to be close to 0.3’, () => {
    expect(sum(0.1, 0.2)).toBeCloseTo(0.3);
}

Reason I chose to use **toBeCloseTo **as the matcher (method) for this is because when dealing with decimal values, there are possibilities for more values after the decimal point, depending on what computation is performed on the values, so instead of looking for the exact, I am making sure it is somewhere in that range.

Now you can see how we went from a test, wrote the function to pass the test, expanded our edge case, wrote a test for it and then we will still pass with our function. You can now go ahead to make sure the parameters are always numbers, also test for negative values.

Find below a test suite, the sum function we have above will fail some of the test, you will need to refactor it to pass all the tests.

// this is to connect the sum function with the test file, so Jest knows where the functions are.
const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

test('add -1 to 3 to equal 2', () => {
  expect(sum(-1, 3)).toBe(2);
});

test('add a string and a Number to equal NaN', () => {
  expect(sum('tolu', 3)).toBe('NaN');
});

test('adds two strings to equal NaN', () => {
  expect(sum('decagon', 'institute')).toBe('NaN');
});

test('add two negative numbers -4 and -9 to equal -13', () => {
  expect(sum(-4, -9)).toBe(-13);
});

test('enter undefined values to output - enter parameters', () => {
  expect(sum()).toBe('You have not entered any paramter')
});

test('adds numbers in string \'23\' to \'3\' to equal 26', () => {
  expect(sum('23', '3')).toBe(26);
});

test('Insert NaN as the only argument to equal NaN', () => {
  expect(sum(NaN)).toBe('NaN');
});

test('Adding special characters like ! and ( to equal NaN', () => {
  expect(sum('!', '*')).toBe('NaN');
});

test('Adding 2 Decimal numbers - 0.1 and 0.2 to be close to 0.3', () => {
  expect(sum(0.1, 0.2)).toBeCloseTo(0.3);
});

test('Adding 2 values 0.915 and 0.11 to equal 1.025', () => {
  expect(sum(0.915, 0.11)).toBeCloseTo(1.025);
});

Before we go deep, let me introduce you to Jest expect Api

The Jest expect Api has a number of matchers you can use, It looks like Jest already accounted for all the things possible to be tested, you can test for truthiness, numbers, objects, arrays containing certain values, and much more. When you’re writing tests, you often need to check that values meet certain conditions. expect gives you access to a number of "matchers" that lets’ you validate different things. We obviously can’t talk about all the matchers or methods possible, we will however talk about .toBe() , .toMatch() , and .toEqual() . To learn more about expectvisit the Jest expect Api documentation page.

.toBe()

.toBe() is used to compare primitive values, It calls Object.is to compare values, which is better for testing than === , according to the Jest documentation. You should not use .toBe() with floating point values, like in the case of 0.1 + 0.2 above, it does not resolve to 0.3 , making .toBe()inappropriate for the test, but .toBeCloseTo() is the right method to use for floating-point numbers.

test(‘Add 1 and 2 to give 3’, () => {
    expect(sum(1,2)).toBe(3); 
});

.toMatch()

.toMatch() is used to compare a string against a regular expression (regex) or against another string. For example, you might not know what a function returns but you know it should have a particular text in it, you can create a regex to test to be sure it is the correct thing that it returns; let us use returnString() as our function, it returns 'I am learning testing with you, yes you!' , remember we do not know it returns the above, we are going to then write a test to check using regex and also another test for when we are sure of what we want it to return, we can also use just a part of the expected string.

test(‘A statement on learning’, () => {
    expect(returnString()).toMatch(/testing/); // using regex
    expect(returnString()).toMatch('I am learning testing with you, yes you') // using the exact string you want returned
    expect(returnString()).toMatch('I am learning ') // using just a part of the string
});

.toEqual()

.toEqual() is used to compare that two objects are same to the detail (checking all properties of the Object). It also calls Object.is to compare values, which is better for testing than === (strict equality), according to the Jest documentation.

const obj1 = {
    isArticle: true,
    title: 'Testing your JavaScript Code — TDD'
}
const obj2 = {
    isArticle: true,
    title: 'Testing your JavaScript'
}
const obj3 = {
    isArticle: true,
    title: 'Testing your JavaScript Code — TDD'
}
test(‘Checking two objects to make sure they are qual’, () => {
    expect(obj1).toEqual(obj2); // wrong
    expect(obj1).toEqual(obj3); // correct
});

You can run the above to be sure. You should also check out the Jest Documentation to learn more about Jest and testing in general.

With our not so little detour, I guess you should have familiarised yourself with Jest, it’s expect Api and what is happening in our test file. Now back to the subject matter, we will add some more test for our OOP functions, I will explain some of the tests here and show you what your output should look like when you are yet to pass your tests and when you do pass all. If you want a peek at the full test suite and the functions, you should clone the repository.

So let’s add the test cases;

// Import the necessary files for the test to work.

// ...

test('Creating a student Record', function() {
  var jon = new User('Jon Snow', '[email protected]', 'iknownothing', 'teacher');
  var arya = new User('Arya Stark', '[email protected]', 'agirlhasnoface');
  expect(jon.createRecord('4.3', 'Very brilliant chap', '0032')).toBe('Student Record Successfully added');
  expect(arya.createRecord('3.0', 'Decent Attempt', '0012')).toBe('You do not have permission to do this');
});

test('Reading a record by Id', function() {
  var cersei = new User('Cersei Lannister', '[email protected]', 'neckkk', 'admin');
  var sansa = new User('Sansa Stark', '[email protected]', 'ithoughtyouwerethewisest');
  expect(cersei.readById(1)).toEqual(expect.objectContaining({grade : '4.3'}));
  expect(sansa.readById(1)).toMatch('You do not have per');
})

// ...

test('Editing a Student Record', function() {
  var jon = new User('Jon Snow', '[email protected]', 'iknownothing', 'teacher');
  var petyr = new User('Petyr Baelish', '[email protected]', 'iamlittlefinger', 'parent');
  expect(jon.updateStudentRecord(1, '4.5', 'This young chap keeps breaking his own records')).toMatch('Student Record');
  expect(petyr.updateStudentRecord(1, '4.34', 'Nothing lasts, so keep working hard')).toBe('You do not have permission to do this');
});

test('Deleting a student Record', function() {
  var jon = new User('Jon Snow', '[email protected]', 'iknownothing', 'teacher');
  var cersei = new User('Cersei Lannister', '[email protected]', 'neckkk', 'admin');
  expect(cersei.createRecord('4.25', 'His head is really on his neckkk', '0117')).toBe('Student Record Successfully added');
  console.log(db.studentRecords);
  expect(cersei.deletedAStudentRecord(2)).toBe('Student Record Deleted');
  expect(jon.deletedAStudentRecord(1)).toMatch('You do not have ');
  console.log(db.studentRecords);
});

The first test case above is for the creation of a student record, we first instantiate the user we want to use to create the record from the User Constructor, then we call the .createRecord method on the user — jon.createRecord('4.3', 'Very brilliant chap', ‘0032’) , and then we use the .toBe() matcher, to check if it returns the exact return value. Notice me checking for when a student (user without permission) is trying to do same function and what it returns.

The second test checks for when an Admin reads a student record by its’ Id. As you can see we have use the .toEqual() and .toMatch() methods in this test. Like the other cases, we create a user to test, we then perform the .readByIdmethod — cersei.readById(1) , we then use the .toEqual() matcher and then you can see the expect.objectContaining() — this part now expects a return value that will be an object and should contain what is stated in the parenthesis — trick here is to add the object literal {} in the parenthesis before the key: 'value' pair, this will help resolve it into an object. And then on the next line we are testing for when a user without permission attempts to carry out the function and then we use the .toMatch() method and then we test against a part of the full string.

Coverage

This is one part of jest I love so much, it helps you know how much of your code you have covered. You are able to know the parts of your code you are yet to test, and you know you have done a good job when you hit full 100 on all parts of your code.

As you can see, I have not hit 100 on all, with a few edge cases tested I am able to complete this coverage to hit a full 100. You should strive for 100 but do not kill yourself in the process (i.e. do not wast valuable time), there are more things to do than trying to hit 100 but before you stop trying to kill yourself make sure you hit a 92% at the least.

To get coverage in your tests add --coverage to your scripts in package.json .

{
  "scripts": {
    "test": "jest --coverage"
  }
}

Below is my complete Suite — with 100% coverage.

beautiful isn’t it 😍

Running Tests

Running a test suite or just one test or any test at all is really simple and straightforward, all you need is npm run test . When you enter this in your terminal and tap on your enter button (while you are in your project folder), jest will find the test files and run the test.