- 27th Nov, 2023
- Aanya G.
31st Mar, 2024 | Dhanashree K.
Have you ever made a small change in form validation, tested it, deployed it, and then broken the next few pages or end points of your API?
Do you know why this might have happened or how to avoid it?
Unit Tests are your answer!!!
A unit test is a small piece of code that will test different parts of your project to verify if they are working as expected.
A unit test for the frontend can check if components are being rendered correctly if optional and conditional fields of a form appear as and when expected, and buttons are activated after correct validations, the list can go on endlessly.
For the backend, it can be to check if data upload works correctly if endpoints are sending the response in the correct format and order, the values and their types are as expected.
Let's say you make a small change and realize that it may affect 5 to 6 endpoints or pages, this will mean that you will manually test all the endpoints or pages and forms.
To do this you will need to set up your database with testing entities, populate them, update them, delete them, etc.
You spend half a day setting up, testing, and deploying it after which your QA will also need to spend a considerable amount of time to test all the affected modules.
The way to be fairly certain that nothing will break is to always write unit tests for every new feature, module, or change that you make, as soon as you make them.
This way all your existing modules and pages get tested before you push your code.
If you have a healthy collection of tests (provided you are not lazy) then just running tests after making changes will ensure that all your existing work still works (pun intended).
Unit tests save time for setting up the environment and database for the tests manually every single time.
One can write tests for the smallest to the biggest things like checking the cases or spaces in response messages to testing if data is uploaded from Excel and complicated calculations are being done correctly.
Or in the case of the front end, one can test from the color of the headers to the conditional rendering of components.
Tests the old code and also catches errors if you change any project-wide utility functions.
Example: The function celcius_to_farenheit(temp_celcius) now returns results up to 2 pt (assume previously returned 4 pt precision).
Now some of the previous use cases using 4pt precision will fail and from the failing tests you will be made aware of all the places this change is affecting.
Unit tests facilitate the early identification of bugs during the development process, as they are crafted prior to the actual implementation of code.
This practice ensures prompt detection and resolution of issues, thereby minimising the expenses associated with bug fixing in later developmental phases.
Honestly, I believe there aren't any, except that initially, it may feel time-consuming, but as you get the hang of it and realize how to make test cases it will become a small but important step in your SDLC.
Keep in mind that test folders and test functions must start with “test_” in Python and in react they must end with “.test.js”.
For starters (and my own bias) let's start with tests for Python code using the popular and powerful testing framework Pytest.
The first step to any testing, automated or manual, is to set up.
Here I must introduce you to fixtures.
Pytest’s fixtures feature permits developers to feed data into the tests.
For example, say we have several tests that all make use of the same data then we can use a fixture to pull the repeated data using a single function.
In Python, we use the assert keyword to check if a given condition is True.
Create a tests folder inside your app and add a conftest.py file which should look something like the code below.
@pytest.fixture(scope='session')
def app():
#Initialise the new application instance for the testing
# define or set up your app…
application.config.update({
'TESTING': True,
})
application.config.update({
'SQLALCHEMY_DATABASE_URI': config_data.get('SQLALCHEMY_TEST_DATABASE_URI')
})
with application.app_context():
db.init_app(application)
db.create_all()
yield app
@pytest.fixture(scope='session')
def testing_client(app):
#This method is being used to fetch the app object to test the general #user test cases. At the end of the session, the User is deleted from #cognito user pool.
yield app.test_client()
The snippet above creates a test client that we can use to query the local testing server while running tests.
Inside the testing client, one can perform all sorts of setups like creating a sample user’s profile in the DB, populating sample testing data, etc.
Now we want to see if a RESTful API endpoint returns a certain response.
We can write a test to check that.
def test_say_hello_positive(testing_client):
expected_response = { 'status': True, 'message': ‘Hello’}
api_response = org_admin_client.get(
'/api/v1/greet/hello', content_type='application/json' )
assert validate_status_code( expected=200, received=api_response.status_code)
assert validate_response( expected=expected_response, received=json.loads(
api_response.get_data()) )
In the above snippet:
To run such tests you need to use the pytest command in your terminal, the following are some ways to do that:
pytest tests/test_users.py:: test_say_hello_positive
pytest
pytest tests/test_users.py
To check the adequacy and sufficiency of the tests written by us we can use the coverage package to check how much code our tests are “covering”.
coverage run -m --source=app/views pytest && coverage report --fail-under=50
Since we are covering Python and Django is a rather popular use, we will see some basic snippets for Django’s own TestCase package.
Here instead of fixtures, we use the setup function which can be test case specific or you can create a reusable one for multiple tests.
from Django.test import TestCase
from myapp.models import Animal
class AnimalTestCase(TestCase):
def setUp(self):
Animal.objects.create(name="cat", sound="meow")
def test_animals_can_speak(self):
cat = Animal.objects.get(name="cat")
self.assertEqual(cat.speak(), 'The cat says "meow"')
In case you are using Django’s TestCase to write tests, the following are some commands to run them:
$ ./manage.py test animals.tests
$ ./manage.py test animals.tests.AnimalTestCase
$ ./manage.py test animals.tests.AnimalTestCase.test_animals_can_speak
It is only fair that we cover unit testing in a frontend framework as well, so let's take a small example from React.
JEST Framework is one of the popular JS testing frameworks for React and comes with React, which can be verified from your project’s package.json.
To run tests: jest –watch Or in React you can use the command npm test run
Let’s say you wish to check the heading of your Login Component which looks like this.
function Login(){
.
.
return(
<>
<div className="login_block">
<h2 data-testid = 'login'>Please Enter Username and Password</h2>
//form…..
</div>
</>
)
}
export default Login
We can test the Login component’s heading using the snippet given below.
First, create a test folder in your src, add a file named login.test.js and paste the snippet inside it.
import { render, screen } from '@testing-library/react'
import Login from './components/login;
test("login heading", () => {
render(<Login/>);
const element = screen.getByText(/Please Enter Username and Password/i);
expect(element).toBeInTheDocument();
})
Let’s break down the above snippet line by line:
I hope these basic examples help you out with starting your unit testing journey.
Happy coding!!!
Get insights on the latest trends in technology and industry, delivered straight to your inbox.