HackerEarth allows you to write Full stack test cases using any framework. For example, in this instance, the platform auto-evaluates a full stack question on the basis of JUnit test cases. An equal score is assigned to each of the test cases. If a candidate passes all the test cases, they receive the total score assigned to the problem.
However, if the code satisfies certain test cases but fails some test cases, the score obtained is proportional to the number of test cases the code fulfills.
HackerEarth generates an XML file on runtime to evaluate the JUnit test cases.
For most frameworks, this can be enabled using the JEST Testing framework and Jest JUnit XML Reporter.
The specific libraries differ by the framework. However, any testing library which generates an XML Report is supported on HackerEarth.
Writing test cases
You can write both backend and frontend test cases in the given format.
Frontend test cases
The given sample code is a test script written in JavaScript using the Jest testing framework and the testing library as react library.
import React from 'react';
import '@testing-library/jest-dom';
import { render, screen, act, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Login from './src/components/Login';
import Register from './src/components/Register';
import { BrowserRouter } from 'react-router-dom';
import io from 'socket.io-client';
describe('Frontend Test', () => {
describe('Testing Socket IO', () => {
beforeEach(() => {
jest.resetModules();
});
it('Register component success', async () => {
jest.doMock('socket.io-client', () => {
const mSocket = {
emit: jest.fn(),
on: jest.fn(),
};
return jest.fn(() => mSocket);
});
const io = require('socket.io-client');
const ENDPOINT = 'localhost:8000';
const mockSocket = io(ENDPOINT);
render(
<BrowserRouter>
<Register socket={mockSocket} />
</BrowserRouter>
);
const usernameInp = screen.getByRole('textbox', { name: /username/i });
const passwordInp = screen.getByLabelText(/password/i);
const emailInp = screen.getByRole('textbox', { name: /email address/i });
const regBtn = screen.getByRole('button', { name: /register/i });
userEvent.type(usernameInp, 'test1');
userEvent.type(passwordInp, '123456');
userEvent.type(emailInp, 'test1@gmail.com');
userEvent.click(regBtn);
await waitFor(() => {
expect(mockSocket.emit).toBeCalledWith('register', {
email: 'test1@gmail.com',
password: '123456',
username: 'test1',
});
});
});
});
});
Backend test cases
const {registerUser, loginUser } = require("./src/SocketFunctions");
describe("Backend Test", () => {
const user = {
username: "test1",
email: "test1@hotmail.com",
password: "123456",
images: [],
};
const loggedUser = {
_email: "test1@hotmail.com",
};
test("Register User Success", async () => {
const response = registerUser(user);
expect(response).toMatchObject({ user });
});
test("Register User Failed", async () => {
registerUser(user);
const response = registerUser(user);
expect(response).toMatchObject({ error: "User already exists!" });
});
test("Login User Success", async () => {
registerUser(user);
const response = loginUser(user);
expect(response).toMatchObject({ user: loggedUser });
});
});
XML files examples
The standard format for an XML file is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="[Test suite name]" tests="[Total number of tests]" failures="[Number of failures]" errors="[Number of errors]" time="[Total execution time]">
<testsuite name="[Test suite file name]" errors="[number of Errors]" failures="[number of failures]" skipped="[Number of skipped tests]" timestamp="[Execution timestamp]" time="[Test suite execution time]" tests="[Total number of tests]">
<testcase classname="[Test class/module name]" name="[Test case name]" time="[Test case execution time]">
</testcase>
<!-- Add more <testcase> elements for additional test cases -->
</testsuite>
<!-- Add more <testsuite> elements for additional test suites -->
</testsuites>
You can replace the placeholders within square brackets with the appropriate values for your specific test report. Feel free to add or remove <testsuite> and <testcase> elements as needed to represent your test suites and test cases.
Example file
1. If the total score assigned to the problem is 100 and a candidate passes all 5 test cases, they will be allocated the total score assigned to the problem that is 100.<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="jest tests" tests="5" failures="0" errors="0" time="0.641">
<testsuite name="main.test.js" errors="0" failures="0" skipped="0" timestamp="2023-06-01T09:08:54" time="0.594" tests="5">
<testcase classname="Tests" name="Create Image Details" time="0.001">
</testcase>
<testcase classname="Tests" name="All Images Route" time="0">
</testcase>
<testcase classname="Tests" name="Single Image Route" time="0">
</testcase>
<testcase classname="Tests" name="Delete Image Details" time="0">
</testcase>
<testcase classname="Tests" name="Update Image Details" time="0.001">
</testcase>
</testsuite>
</testsuites>
Explanation
- The <testsuites> element represents the collection of test suites.
- The name attribute indicates the name of the test suite, which is "jest tests".
- The tests attribute specifies the total number of tests executed in the test suite, which is 5.
- The failures attribute denotes the number of failed tests, which is 0.
- The errors attribute indicates the number of tests with errors, which is 0.
- The time attribute specifies the total time taken to execute the tests in seconds, which is 0.641.
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="jest tests" tests="5" failures="3" errors="0" time="0.75">
<testsuite name="main.test.js" errors="0" failures="3" skipped="0" timestamp="2023-06-01T09:10:56" time="0.706" tests="5">
<testcase classname="Tests" name="Create Image Details" time="0.001">
</testcase>
<testcase classname="Tests" name="All Images Route" time="0.001">
</testcase>
<testcase classname="Tests" name="Single Image Route" time="0.002">
<failure>Error: expect(received).toBe(expected) // Object.is equality
Expected: 2
Received: 1
at Object.toBe (/home/hackerearth213/Downloads/TestCases/ImageAPI/backend/main.test.js:21:19)
at runTest (/home/hackerearth213/Downloads/TestCases/ImageAPI/backend/node_modules/jest-runner/build/runTest.js:444:34)</failure>
</testcase>
<testcase classname="Tests" name="Delete Image Details" time="0">
<failure>Error: expect(received).toBe(expected) // Object.is equality
Expected: 2
Received: 1
at Object.toBe (/home/hackerearth213/Downloads/TestCases/ImageAPI/backend/main.test.js:25:18)
at runTest (/home/hackerearth213/Downloads/TestCases/ImageAPI/backend/node_modules/jest-runner/build/runTest.js:444:34)</failure>
</testcase>
<testcase classname="Tests" name="Update Image Details" time="0">
<failure>Error: expect(received).toBe(expected) // Object.is equality
Expected: 2
Received: 1
at Object.toBe (/home/hackerearth213/Downloads/TestCases/ImageAPI/backend/main.test.js:29:19)
at runTest (/home/hackerearth213/Downloads/TestCases/ImageAPI/backend/node_modules/jest-runner/build/runTest.js:444:34)</failure>
</testcase>
</testsuite>
</testsuites>
Evaluation of full stack question
The evaluation takes place in two steps which are as follows:
- Sample evaluation command is executed to evaluate sample test cases.
- Evaluation command is executed to evaluate the main test cases.
Sample evaluation command
Sample Evaluation commands are the commands required to evaluate the project based on the sample test cases. When you click Run code in the full stack IDE, this command is automatically executed to evaluate the sample test cases.
Sample evaluation commands vary with different frameworks. For example:
- For NodeJS, it can be npm run test
- For Spring, it can be mvn test
- For Django, it can be python3 test.py or pytest test.py
For our sample project:
- The sample evaluation command for the front end is “npm run sample_tests”, which will evaluate the test cases present in the hackerearth-fs-spring-react-sample\frontend\sample.test.js file.
- The sample evaluation command for the backend is “mvn test -Dtest=com.hackerearth.backend.sample.*Tests ”, which will evaluate the test cases present in the hackerearth-fs-spring-react-sample\backend\src\test\java\com\hackerearth\fullstack\backend\sample\sampletests file.
The sample evaluation commands can be a single command or multiple commands (depending on the Framework)
Evaluation command
Evaluation commands are the commands required to evaluate the project based on the main test cases. When you click Submit code in the full stack IDE, this command is automatically executed to evaluate the main test cases.
For our sample project:
- The sample evaluation command for the front end is “npm run main_tests”, which will evaluate the test cases present in the hackerearth-fs-spring-react-sample\frontend\main.test.js file.
- The evaluation command for the backend is “mvn test -Dtest=com.hackerearth.backend.main.*Tests ”, which will evaluate the test cases present in the hackerearth-fs-spring-react-sample\backend\src\test\java\com\hackerearth\fullstack\backend\main\BackendApplicationTests file.
- The Evaluation commands can also be a single command or multiple commands (based on the frameworks).
Notes
- Ensure folder/files containing maint test cases are marked as hidden while creating the question to prevent the candidate from viewing them.
- To hide a file or a folder, follow the given steps:
- Go to the project editor.
- Right-click the file/folder containing the main test cases.
- Select Hide from the menu.
This successfully will hide your test cases.
Evaluation report path
The evaluation report path is the path where the evaluation report of the project is stored. The file is parsed as per Junit and Xunit formats and hence the output file can be in either format. Ensure that this file is in XML format. The naming of the file can be configured inside the project.
The same name has to be keyed in place of the Evaluation report path.
Example: If a Project generates an XML file in frontend/tests/ directory, and the file name is junit.xml then the Evaluation report path should be frontend/tests/junit.xml
For our sample project:
- In the case of the front end, The package.json file contains Junit as a dev dependency and also all the Junit-related configurations. Note that the “outputDirectory” and “outputName” parameter is consistent with the Evaluation Report path field in the UI.
A full stack question is evaluated automatically. HackerEarth assigns the score equally to the backend and frontend test cases for a full stack question. For example, if a full stack question has 2 frontend test cases and 8 backend test cases and a candidate is able to pass all the frontend test cases but partially passes the backend test cases, they will be assigned the score accordingly. If a question is allocated 100 marks and the candidate passes 2 frontend test cases, they will receive 50 marks for the frontend test case, and the rest 50 marks will be divided into the 8 backend test cases. If the candidate passes 4 of them, they will receive 25 marks out of 50 and the total score they will receive for the whole question will be 75 out of 100.
This is how a typical full stack question is evaluated on the HackerEarth platform. Different frameworks require different test cases and the given example is just an example of a sample project. To learn more about how to create a custom framework full stack question, read this article. You can also check our sample full stack question here.