Testing is becoming more and more prominent in web development, especially since DevOps is growing. With continuous integration and deployments becoming a standard practice, it's important that you know how to keep your source code clean and make sure nothing crazy gets into prod or any of the other environments. One way to do that is with automated testing, although you still have to write those tests.
This is a quick tutorial over how TDD and BDD work in an agile environment and how you write unit tests in Jasmine. We'll go through the process of writing a few unit tests using TDD and BDD and explain why we're doing what we do and how we make the decisions to do them.
Background on TDD and BDD
Let's start with a little background on TDD and BDD. You can get a more in-depth, yet quick overview of both here: TDD/BDD overview In a nutshell, BDD describes how features will work and how users should be able to use them and TDD makes you write tests for these features before you write the code for them. Using both of these methodologies together will give you several advantages.
Your team will understand where the feature requirements come from and why they are necessary.
You will have wide testing coverage over your project which makes you produce higher quality code faster.
Documentation won't be an issue for any part of your code because any developer can go look at the tests to see how the code should work.
Writing tests before you write the code gives you a better understanding of the problem you are trying to solve.
Having tests for your code makes your CI/CD pipeline more reliable and consistent.
These are just a few advantages you get from writing tests. Now I'm going to go through the process of writing two tests in Jasmine.js. The same process can be used to write tests using any libraries on the front-end or back-end.
User stories
These are the two user stories that we will use in order to write our tests.
User story 1
As an admin user, I can deactivate all users with the permissions I select so that I can remove multiple people with the same permissions from the system at the same time instead of individually.
User story 2
As an account user, I can enable or disable two factor authentication so that I can decide how much security I want around my account information.
These aren't any fancy features, but they do have several tasks within each of them. Usually the next step is to meet with your team and break these user stories into tasks that you will then write the code for. Let's do a little of that. We might leave out a few things or condense them just to keep this short.
User story 1 tasks
- Make sure user is an admin
- Filter all users based on permissions selection
- Add functionality to remove users as a group
User story 2 tasks
- Make a new modal to update two factor settings
- Add a warning about information security if they don't enable two factor authentication
This is a rough breakdown of the tasks you can pull out of those user stories. Now that we have our tasks defined, we can start writing the tests.
Writing tests in Jasmine
For this quick tutorial, we'll be using Jasmine.js. It's pretty easy to pick up and it integrates well with most JavaScript frameworks. I like it for testing Angular applications. If you want the details about Jasmine and learn more about how to use it, check out their documentation: https://jasmine.github.io/api/edge/global Weβre not going to write a test for all of the tasks because that will leave you with a few practice exercises. We will write three tests so you can get a better understanding of how to write tests. Remember, we don't have any code implemented yet so you can call things whatever you like.
Make sure user is an admin
'use strict';
const testAdminUser = require('./adminUser.js');
describe('make sure user is an admin', () => {
let dummyUser = {
username: 'testdummy',
password: 't3StD4mM7'
};
it('check if user is logged in', () => {
expect(testAdminUser.login(dummyUser).response.success).toBe(true);
});
it('check if user has admin permissions', () => {
let userPermissions = [];
userPermissions = testAdminUser.login(dummyUser).response.permissions;
expect(userPermissions.includes('Administrator')).toBe(true);
});
});
Add functionality to remove users as a group
'use strict';
const testAdminUser = require('./adminUser.js');
describe('functionality to remove users as a group', () => {
let dummyPermission = 'Contributor';
it('gets a list of all users marked for deletion', () => {
expect(testAdminUser.getUsersForDeletion(dummyPermission).response).toBeDefined();
});
it('remove all users with the targeted permissions', () => {
expect(testAdminUser.deleteUsers(dummyUser).response.success).toBe(true);
});
it('check that the users have actually been removed', () => {
let listOfUsers = testAdminUser.getUsersForDeletion(dummyPermission).response;
expect(listOfUsers.length).toBeLessThan(1);
});
});
Make a new modal to update two factor settings
'use strict';
const testAdminUser = require('./adminUser.js');
describe('make a new modal to update two factor settings', () => {
let dummyUser = {
username: 'testdummy',
password: 't3StD4mM7'
};
it('check if new modal has been created', () => {
expect('#two-factor-modal').toExist();
});
it('check that two factor settings have loaded with the selected value', () => {
expect(testAdminUser.getTwoFactorSettings(dummyUser).response).not.toBeNull();
});
it('check that changing the two factor setting updates the value for the user', () => {
let selectedValue = false;
let isUpdateSuccessful = testAdminUser.updateTwoFactorSettings(dummyUser).response.success;
if (isUpdateSuccessful) {
expect(testAdminUser.getTwoFactorSettings(dummyUser).response).toBe(selectedValue);
}
});
});
These are some examples of how to write tests using Jasmine, but most test libraries follow a similar flow. You'll describe what you are trying to test and then you'll write some dummy conditions to perform those tests on the code you plan on implementing. This isn't the only way you could have written tests for these tasks so if you see room to make them better, write the tests yourself! That's where you'll get the most benefit anyways.
Writing tests isn't difficult, it just takes time to get used to. When you normally get a task and jump straight into the code, writing tests first seems complicated. Many developers immediately ask how you can write a test for code that doesn't exist yet. That's not the right way to look at it. You aren't necessarily writing tests to check the implementation of your code. You are writing tests to check that a feature is working the way the business side asked for.
That's why you can write a test before writing code. Once you have the test written, you basically have an outline for the code you need to write. So you write your code to pass the feature test. Then you refactor it to make it more efficient. That's how the TDD/BDD process works and it's pretty cool when you get the hang of it.
Why do you think developers are so split on whether to write tests or not? I know there are people out there with 20+ year careers in software that have never written a test and there are people who wouldn't dream of writing code with out tests first. Why is there such a divide? Is it because the whole testing thing is relatively new or is it because of the way business runs or something else? I'm really curious!
Hey! You should follow me on Twitter because reasons: https://twitter.com/FlippedCoding
Top comments (3)
I used to work at a company which didn't have test at all, we only manually tested our software, we didn't have any QA engineer and that was normal for me at that time. Nowadays on my current job is the other way around π€·ββοΈ I'm not even allowed to deliver code without tests.
Great article Milecia. Nice break down between BDD and TDD, then the Jasmine examples. I look for ward to the next article!
Thanks David! π