Featured Blogs
- August 1, 2024
Introduction to Test Cases
In the software testing process after requirement analysis and test plan, we have a Test design phase. In this phase, we need to write the test cases from different test scenarios as per the requirement of an application. Test cases are instructions for testers to follow to ensure programs are functioning properly. Test case writing converts user requirements into a set of test conditions and descriptions that indicate how a system is functioning. Applications must be tested through these test cases to find out how the system behaves under all possible conditions. A clear understanding of the application and the testing process can make writing test cases that identify defects easier.
While writing such test cases we need to follow some factors that make it perfect for all the perspectives. Here are some factors to get through the process.
1. Effectiveness
Writing test cases in an effective manner is a very important thing. Many people think that writing too many test cases covers all the use cases. But in actuality, it increases the load of the resource and also wastes too much time. So we need to think effectively while writing test cases so that it provides maximum coverage with fewer test cases. It could be further improved by dividing them into subsets like there is such test cases be common for some test scenarios.
2. Simplicity
Test cases should be written in simple words. No complex things should be included regarding the inputs and outputs of the test case. For that everyone involved in the test case writing process should use the same format for designing them. We should aim to write them in a way that even a non-technical person can also understand the purpose of a test case.
3. Understanding
Before writing a test case we should have a proper understanding of a requirement. For example, there could be many common things in each application that are sure to bring out errors. For that, we did not need to handle it specially.
4. Reusability
Test cases should be written in a way that they become reusable units for the future for creating new test cases. Testers can refer to the previous test cases to create a new one which makes it easier. So while creating a test case we should also keep in mind that it might be reused in a different scenario, a different but similar project. It saves time and efforts of resources.
5. Regression testing
When new features are added old test cases should be rerun to make sure nothing tested has affected and outputs are the same as expected. These regressive test cases are part of the test case policy. When test cases are re-run, some may give unexpected results which may not always be because of new changes in the code but rather a test case that is inappropriate under new criteria. Therefore, having regressive testing in mind it would help rewrite a test case when changes are made.
6. Self review
After test cases are written, review them from the tester's perspective and verify that the test case appropriately addresses the scenario and would be easy to execute. Make sure the test case is understandable to everyone and the results are appropriate.
7. End-user perspective
Software testing provides user quality software. Errors that are found through test cases are removed during the testing process. When writing a test case, think of the end user and how the user experience would be affected should the test case fail to cover a scenario from the end user's perspective.
8. Not-feature
Most test cases are written to test the features offered by an application. However, it is also necessary to test for not-features, i.e. those abilities of the software that should not exist and may be used to exploit the application. These kinds of test cases are part of exploratory testing.
9. Management
The use of proper management tools and systems is important for test cases because the count of test cases increases very fast. So it is important to use a proper management tool from the beginning. We can use simple spreadsheets or specialised tools which are available in the market as per our budget to manage test cases more effectively.
10. Peer review
If you review the test cases which are written by yourself then no matter how many times you go through it. It is possible that you will not find anything. For this, peers should review the test cases from the others or developers for the better result. After the review is done by others, the tester should make the changes that are suggested. It makes our test cases more reliable and consistent.
11. Backward Approach
Initially, when we write a test case we have expected the result or the output, and we write in that aspect to get that output. But in the backward approach instead of thinking what the result would be for a certain input, we can think of what inputs could be for a certain output. In this way, we can try with invalid inputs to get the same output.
- July 31, 2024
Promises, Async/Await: For Async Code Execution
Promises help us to manage the flow of code execution when working with tasks that might take some time to complete or not completed yet but will be completed in future either with success or failure such as making network requests, reading files. Promises provide a way to handle asynchronous operations and their end result.
Promises have three States
- Pending
- Fulfilled
- Rejected
Let’s understand promises with example of withdrawing money from ATM
Suppose you're going to withdraw money from an ATM Machine 🏧. You insert essential details for withdrawal like amount pin etc. This process sends a request to the bank's server to check your account balance and this process will take some time. If a sufficient amount is available for withdrawal then it will resolve your request and deduct the amount from your account and dispense cash. If amount is not sufficient then it will reject your request and sends error message “Insufficient Amount”
function withdrawingFromATM(amount) {
const balance = 1000;
return new Promise((resolve, reject) => {
setTimeout(() => {
if (amount <= balance) {
const withdrawalAmount = balance - amount;
resolve(`withdraw amount ${withdrawalAmount}`);
} else {
reject(`Insufficient funds!!!`);
}
}, 2000);
});
}
withdrawingFromATM(5000)
.then((message) => {
console.log(message);
})
.catch((error) => {
console.error(error);
});
Promise Methods
Promise.all
Promise.all() is a function that takes an array of promises as input and returns a single promise.This promise is resolved If all the promises given to it are resolved, if any one of them is rejected then promise.all will be rejected.
Promise.any
Promise.any() is a method that takes an array of promises as input and returns a single promise. This single promise will fulfil if any of the input promise is fulfilled or it rejects if all the input promise is failed.
Promise.allSettled
Promise.allSettled() is a method that takes an array of promises as input and returns a single promise. This single promise will fulfil if all of the input promise is fulfilled with array of objects which defines end result of each promise
// Let’s Consider a example of withdrawal money from multiple accounts
const promisesArray = [
printAccountStatement(""),
printAccountStatement("account2"),
printAccountStatement("account")
];
function printAccountStatement(account) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (account && account.length > 0) {
resolve(`${account}'s monthly statement`);
} else {
reject(`Invalid account Number`)
}
}, 500);
});
}
// Promise.any Example
Promise.any(promisesArray).then(results => {
console.log(`\nresult ${results}`)
})
.catch(error => {
console.log(`Error: ${error}`)
});
// Promise.all Example
Promise.all(promisesArray)
.then(results => {
results.forEach(result => {
console.log(`\nresult ${result}`)
})
})
.catch(error => {
console.log(`Error: ${error}`)
});
// Promise.allSettled Example
Promise.allSettled(promisesArray)
.then(results => {
results.forEach(result => {
console.log(`\nresult ${result.status}`)
})
})
.catch(error => {
console.log(`Error: ${error}`)
});
Promise Chain Promises Chain is used to handle a number of asynchronous operations that are depending on each other. In the promise chain output of the first asynchronous operation is used as the input of the second one.
Let’s understand promise chain with example of ATM Machine.
Suppose you are going to withdraw money from money. When you perform a withdrawal money transaction from an ATM, It performs a chain of promises to handle various operations. It will perform a sequence of steps for withdrawal. First, It will check the balance of your account. If sufficient balance is available for withdrawal then another promise that will deduct the amount from your account after deducting the amount of money another promise will print receipt for withdrawal operation.
// Let’s Consider a example of withdrawal money from multiple accounts
const promisesArray = [
printAccountStatement(""),
printAccountStatement("account2"),
printAccountStatement("account")
];
function printAccountStatement(account) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (account && account.length > 0) {
resolve(`${account}'s monthly statement`);
} else {
reject(`Invalid account Number`)
}
}, 500);
});
}
// Promise.any Example
Promise.any(promisesArray).then(results => {
console.log(`\nresult ${results}`)
})
.catch(error => {
console.log(`Error: ${error}`)
});
// Promise.all Example
Promise.all(promisesArray)
.then(results => {
results.forEach(result => {
console.log(`\nresult ${result}`)
})
})
.catch(error => {
console.log(`Error: ${error}`)
});
// Promise.allSettled Example
Promise.allSettled(promisesArray)
.then(results => {
results.forEach(result => {
console.log(`\nresult ${result.status}`)
})
})
.catch(error => {
console.log(`Error: ${error}`)
});
Async/Await
Callback hell means a situation in JavaScript where nested callbacks are used to handle asynchronous operations. This makes our code difficult to read, understand, and debug due to complexity. To resolve this async/await is used. async/await, which provide more structured and readable ways to handle asynchronous operations.
An async function is a function that always returns a promise. It allows you to use the await keyword within the function. The await keyword is used to pause the execution of an async function until the promise is resolved or rejected. we can use try/catch blocks to handle errors in async functions. How to use async/await you can refer to the below example.
var balance = 1000;
function checkBalance() {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (balance != 0) {
resolve(balance);
} else {
reject("Balance is 0")
}
}, 1000);
});
}
function withdraw(amount) {
return new Promise((resolve, reject) => {
setTimeout(() => {
balance = balance - amount
if (amount <= balance) {
resolve(amount);
} else {
reject("Insufficient balance");
}
}, 500);
});
}
function printReceipt(amount) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`Withdrawal Amount: ${amount} and your current balance is ${balance}`);
}, 500);
});
}
async function withdrawMoney() {
try{
console.log("withdrawing process started")
let balance = await checkBalance()
let withdrawalAmount = await withdraw(490)
let receipt = await printReceipt(withdrawalAmount)
console.log("receipt", receipt)
}catch(error) {
console.log(`Error: ${error}`)
}
}
withdrawMoney()
- July 31, 2024
Exploring the Stack Data Structure
A stack is a linear data structure in which all the insertion and deletion of data are done at one end only. Stacks can be implemented by using other data structures like, array and linked lists.
The Stack is widely used in many applications of internal compute processing. One of the widely but least known applications is of having it being used in converting and evaluating Polish notation expressions.
- Infix
- Prefix
- Postfix
Comparative to arrays and linked lists, which allows us to insert and delete values from any place. The stack is a linear data structure, and all the insertion and deletion of its values are done at the same end which is called the top of the stack.You can imagine the stack of plates and pile of books to understand the stack better. As the item in this form of data structure can be removed or added from the top only, it establishes the property that the last item to be added will be the first item to be removed. so you can say that the stack follows the Last In First Out (LIFO) structure.
- July 31, 2024
A Journey Through Data Structures
"A data structure is a storage technique that is used to store and organize data. It is a way of arranging data on a computer memory so that it can be accessed and updated efficiently."
Data Structures are typically used to organize, process, retrieve and store data on computers memory for efficient use. Having the right understanding and using the right data structures helps software engineers write the right code.
There Are Two Types Of Data Structures
Linear Data Structure: If the elements of a data structure result in a sequence or a linear order then it is called a linear data structure. Every data element is connected to its next element and sometimes also to its previous element in a sequential manner. Example - Arrays, Linked Lists, Stacks, Queues, etc.
Non-linear Data structure: If the elements of a Data structure result in a way that the traversal of nodes is not done in a sequential manner, then it is a Non-linear data structure. Its elements are not sequentially connected, and every element can attach to another element in multiple ways. Example - Hierarchical data structure like trees.
Important Of Data Structures
- Data structures are a key component of Computer Science and help in understanding the nature of a given problem at a deeper level. They’re widely utilized in Artificial Intelligence, operating systems, graphics, and other fields. If the programmer is unfamiliar with data structure and Algorithms, they may be unable to write efficient data-handling code.
- A strong grasp of this is of significance if you want to learn how to organize and assemble data and solve real-life problems
- Good understanding of data structures will help you write efficient code in your day to day work
As we have understood the importance of data structures, we should also remember that knowing when to use the right one is also equally important. Not using the right data structure will create a lot of problems with efficiency and performance.
Classification Of Data Structure
Short Summary
Array: An array is a collection of similar data elements stored at contiguous memory locations. It is the simplest data structure where each data element can be accessed directly by only using its index number.
Queue: A collection of items in which only the earliest added item may be accessed. Basic operations are add (to the tail) or enqueue and delete (from the head) or dequeue. Delete returns the item removed. Also known as "first-in, first-out" or FIFO.
Stack: linear data structure that follows a particular order in which the operations are performed. The order may be LIFO(Last In First Out) or FILO(First In Last Out). LIFO implies that the element that is inserted last, comes out first and FILO implies that the element that is inserted first, comes out last.
Tree: A tree data structure is a hierarchical structure that is used to represent and organize data in a way that is easy to navigate and search. It is a collection of nodes that are connected by edges and has a hierarchical relationship between the nodes.
Graph: A Graph is a non-linear data structure consisting of vertices and edges. The vertices are sometimes also referred to as nodes and the edges are lines or arcs that connect any two nodes in the graph. More formally a Graph is composed of a set of vertices( V ) and a set of edges( E ). The graph is denoted by G(E, V).
In our next article, we will explore “Stack” data structure in detail. Till that time, Namaste! 🙏.

- July 31, 2024
Error Monitoring: What, Why and How?
What?
World 🌎 is booming with mobile and web applications, Everyday those mobile and web applications offer new features to gets many new customers. When these features are offered, it becomes very important that they fit into the app’s ecosystem without causing any big impact on the existing stability of the app. To achieve this goal, we need to use all the tools in our basket. Knowing good error monitoring tools and understanding the practice itself is one way to ensure that stability we seek gets achieved for the app.
Error monitoring represents the practice which ensures monitoring of known and unknown behaviour of the system/app. Sone pe suhaga would be to get the notification on our communication tool whenever something unexpected happens while monitoring the behaviours.
If we combine the monitoring and alerting mechanism together and implement it on the app, we think we will be on the right track of achieving our goal which is to ensure the stability of the app.
As we have defined the What part of the error monitoring, let’s continue forward with Why part. 🚀
Why? 🤔
It Improve Customer Experience 🤯🤯
Good and satisfying customer experience is an important part of software development. So we have to ensure that our application provides the best customer experience possible. If our application may crash sometimes and not work as expected then customers may not prefer to use our application and we will never know why they are not using our application. Error monitoring helps us to fix those critical issues as quickly as they are identified even if customers don’t report those errors. It will increase customer satisfaction.
It Saves Your Revenue Loss 💰💰
Let’s assume e-commerce 🛒 web applications with large number of customers. As they are using our web application, It may happen due to critical issues in our application, some functionalities are not working as expected and customers face some runtime failures. This makes our application difficult to use for customers. If we aren't aware about the issue then it will affect more and more customers and also give poor customer experience. Those errors could directly lose customers and affect business revenue.
Error monitoring helps us to fix those critical issues as quickly as they are identified. As a result we can reduce the number of affected customers and provide a smooth and better customer experience. which makes our pocket happy. 🙂
It Increase Error Traceability 🧑💻 🧑💻
Let’s consider we have that same e-commerce 🛒 web applications with shopping carts, search and filtering products features, Where customer can search for products, add items to cart and perform checkout. Consider a scenario where a customer is searching for products and clicking a button to add an item to cart and suddenly getting stuck and can’t process further due to an runtime exception.
To fix these we need to know what actually went wrong and when it happened and how it happened?. If your application has multiple services then it could be difficult for you to find out the root cause of the problem.
With proper Error monitoring in place, When something goes wrong in our application, we can collect useful information about issues like error stack (source of error), URL and IP address and notify immediately to the developer so they can find out the root cause of the issue through error stack and other information and fix it quickly. It will reduce debugging time for developers.
How?
To understand this part of the idea, we can take an example of one web application.
Web applications are generally categorised into two broad components: Frontend and Backend(we are skipping other components like, dev-ops, QA etc for easier understanding). For assuring better stability of applications, we need to monitor both components really well. Let’s see how we can monitor our front-end apps.
Monitoring Frontend Apps
Frontend Monitoring means tracking the issue from the end-user's perspective. Frontend monitoring depends on the user's system configuration or network connections. In order to ensure that our application works smoothly on all devices. We need to monitoring different aspects of the FrontEnd component like,
UI glitches (UI Responsiveness)
Network API failure
Slow running JavaScript code
Website Errors (For Example Javascript Type Error)
Monitoring Backend Apps
Backend monitoring means monitoring all the parts of the backend system. For example, some of the parts could be,
- Issues with databases (slow query, connection issue)
- Issues with databases (slow query, connection issue)
- Endpoint’s health
- Hardware problems (high memory usage) and many more etc
Now as we know, what we need to monitor, we should use some of the well known and proven tools so that we don’t reinvent the wheel. 😀
Tools For Error Monitoring
1. Sentry
2. Raygun
3. Rollbar
4. LogRocket
5. Edit Heading
Thank you for reading this far!. We are working on a detailed article on one of the error monitoring tools and will soon publish it. Article will contain information about how that tool can be integrated and used on a system.

- July 31, 2024
Docker meets Gully Cricket
Introduction
Docker has become the go-to solution for containerization, revolutionizing the way applications are deployed and managed. But have you ever wondered how Docker can be explained using a relatable, everyday scenario? In this blog post, we'll dive into the world of gully cricket and demonstrate how Docker can be likened to organizing and managing a game of gully cricket. So, grab your bat and get ready to explore the world of Docker with a gully cricket twist!
1. Setting Up The Playing Field
Just as a gully cricket game requires a playing field, Docker creates an environment for running applications. In gully cricket, the field may be a small neighborhood park or a narrow street. Similarly, Docker provides a lightweight, isolated environment called a container where applications can run without interfering with each other. Each container represents a specific component of your application, such as a web server, database, or microservice.
2. Building A Team
In gully cricket, forming a team is essential. Similarly, Docker allows you to build your team of containers. Each container is self-contained and holds all the necessary dependencies and libraries required to run a specific component of your application. Just like a cricket team requires players with different roles (batsmen, bowlers, and fielders), Docker allows you to build a team of containers with specialized functions, enabling better separation of concerns and scalability.
3. Docker Images (The Cricket Gear)
In gully cricket, players equip themselves with various gear, such as bats, balls, and protective gear. In the Docker world, these gears are represented by Docker images. A Docker image is a lightweight, standalone package that contains everything needed to run a piece of software, including the code, runtime environment, libraries, and dependencies. Docker images act as a blueprint for creating Docker containers.
4. Docker File (The Game Strategy)
Before starting a game of gully cricket, it's crucial to plan the game strategy. Similarly, in Docker, you define your game strategy using a Docker file. A Docker file is a text file that contains instructions for building a Docker image. It specifies the base image, dependencies, and configuration needed for your application. It's like creating a playbook that guides the setup of your Docker containers.
5. Docker Compose (The Match Organizer)
In gully cricket, organizing a match involves coordinating players, scheduling, and ensuring everyone is in the right place at the right time. Docker Compose plays a similar role by allowing you to define and manage multi-container applications. With Docker Compose, you can specify the configuration for your entire application stack, including multiple containers, their dependencies, networks, and volumes. It simplifies the process of orchestrating your Docker containers and brings them together to form a cohesive application.
6. Scaling Up (More Players, More Fun)
Gully cricket games often attract more players, creating an opportunity for larger teams. Similarly, Docker enables scaling up your application by adding more containers to handle increased traffic or workload. Docker's scalability and flexibility allow you to effortlessly spin up additional instances of containers when demand surges and scale them down when the rush subsides, ensuring your application is always responsive and available.
7. Deploying On Different Fields (Portability With Docker)
Gully cricket games can take place in various locations, such as different parks or streets. Docker offers a similar portability advantage, allowing your application to run consistently across different environments. With Docker, you can package your application and its dependencies into a container, making it portable and independent of the underlying infrastructure. This means you can deploy your application on different platforms, be it your local development machine, a cloud provider, or even a colleague's computer, with minimal configuration changes.
Conclusion
We appreciate your interest in gully cricket Docker and invite you to be a part of this exciting series. Together, let's master Docker and unlock its potential in streamlining application development and deployment.