Skip to main content

Unibench

Technology

Should a Test Engineer Feel Bad About Finding Too Many Defects/Bugs in the Product?

Author: Chathura Dhanushka Date: 15/03/2024 Introduction: The role of a Test Engineer in software development is paramount, as they are responsible for ensuring the quality and reliability of the software before it reaches the end-users. However, there often arises a question of whether a Test Engineer should feel bad about finding too many defects in a product. This article aims to delve into this question, exploring different perspectives and shedding light on the Test Engineer’s role in defect detection, as well as the broader context of software quality assurance. Understanding the Test Engineer’s Role: Test Engineers play a pivotal role in the software development lifecycle (SDLC). Their primary responsibility is to design, implement, and execute test cases to verify that the software meets the specified requirements and functions as intended. Test Engineers utilize various testing methodologies, tools, and techniques to identify defects or bugs in the software and ensure its overall quality and reliability. The Importance of Finding Defects: Detecting defects during testing is crucial for ensuring the quality and reliability of the software. Identifying defects early in the development process enables the development team to address them promptly, reducing the risk of these issues affecting the end-users. Moreover, finding defects before the software is released helps mitigate potential risks, such as customer dissatisfaction, financial losses, and damage to the organization’s reputation. Test Engineer’s Perspective: From a Test Engineer’s perspective, finding defects in the software is not a reflection of failure or incompetence. On the contrary, it demonstrates the Test Engineer’s dedication, attention to detail, and commitment to delivering high-quality software. Each defect uncovered presents an opportunity for improvement and contributes to enhancing the overall quality and reliability of the product. Impact on Team Dynamics: In some cases, Test Engineers may feel apprehensive about reporting too many defects, fearing that it may strain their relationship with other team members or reflect negatively on their performance. However, fostering a culture of collaboration, openness, and continuous improvement within the team is essential in overcoming such concerns. Test Engineers should communicate effectively with their peers, providing constructive feedback on the defects identified and working collaboratively towards resolving them. Managing Expectations: It is essential to manage expectations regarding the number of defects found during testing. While finding defects is a natural part of the testing process, it is unrealistic to expect a defect-free product. Factors such as software complexity, time constraints, resource limitations, and changing requirements contribute to the presence of defects in the software. Test Engineers should focus on identifying critical defects that pose significant risks to the product’s functionality or usability. Continuous Improvement: Test Engineers should view defect identification as an opportunity for continuous improvement. By analysing the root causes of defects and sharing lessons learned with the team, Test Engineers can contribute to the development of more robust testing strategies and processes. Additionally, leveraging automation tools, adopting best practices in testing, and embracing new technologies can help streamline the defect detection process and enhance efficiency. Conclusion: In conclusion, Test Engineers should not feel bad about finding too many defects in a product. Instead, they should embrace their role as quality assurance professionals and recognize the value they bring to the software development process. Detecting defects early, communicating effectively with team members, managing expectations, and striving for continuous improvement are essential aspects of a Test Engineer’s responsibilities. By adopting a positive mindset and focusing on delivering high-quality software, Test Engineers can contribute to the success of the project and the satisfaction of end-users.

Technology

Unleashing the Power of Modern Java

Features and Capabilities for Everyday Programming Since its inception, Java has come a long way, and its versatility has resulted in widespread adoption across diverse domains, including enterprise, mobile app development, embedded systems, and more. Java’s popularity can be attributed to its robustness, platform-independence, and support for cross-platform frameworks, which makes it a reliable choice for software development across different domains and platforms. Photo by John Schnobrich on Unsplash From functional programming constructs like lambdas and streams, to improved security and performance, each new release of Java continues to push the boundaries of what’s possible in modern programming. Over the past few years, the JDK team has added a ton of features to Java, including new language constructs, improved libraries, and enhanced tools for development and debugging. Java went through a significant evolution with the release of Java 8, which brought transformational changes revolutionizing the way developers wrote Java code. latest stable version (Java 20), was released in march 2023 with several enhancements in terms of performance and security. These features have made Java more powerful, flexible, and efficient than ever before, enabling developers to write cleaner, more concise code and deliver better software faster. Whether you’re working on a small project or a large-scale enterprise application, the latest advancements in Java have something to offer everyone. In this article, we’ll explore some of the top features that have been added to Java in recent years, and how they are changing the way we write code. 01. Lambda expressions Lambda expressions were introduced in Java 8 as a way to provide concise and functional programming constructs to Java. Prior to Java 8, anonymous inner classes were often used as a convenient way to define behaviors such as Event Handling, Callbacks, Adapters etc. Lambda expressions are essentially anonymous functions that allow you to pass behavior as data. They are often used with functional interfaces, which are interfaces with a single abstract method. In this case, the addActionListener method of the Button class expects an argument of type ActionListener, which is a functional interface with a single abstract method actionPerformed(ActionEvent e). The data type of the lambda expression’s parameter e is therefore inferred to be ActionEvent, which is the parameter type of the actionPerformed method. Inner classes are not deprecated in Java, but they are not as commonly used as they once were because lambda expressions provide a more concise syntax and better performance in many cases. Photo by Artturi Jalli on Unsplash 02.Functional interfaces In a functional programming paradigm, functions are treated as first-class citizens, meaning they can be passed as arguments to other functions, returned as values, and stored in variables. This style of programming is focused on the behavior of functions, rather than the state of objects. To support functional programming constructs, Java introduced functional interfaces in Java 8. A functional interface is an interface with a single abstract method, which is used to represent a function. This makes it possible to use lambda expressions to pass behavior around, and treat functions as first-class citizens. This allows Java to support some core features of functional programming not limited to, 01.Higher-order functions: Functions that take other functions as arguments or return functions as results. 02.Immutable data: Data that cannot be modified after it is created. This allows for safe sharing of data between functions. 03.Functional composition: The ability to combine multiple functions into a single function. In this example, we create a Runnable instance using a lambda expression that defines the behavior of the run() method. We pass this lambda expression directly to the Thread constructor, without the need to create a separate class or method. When we start the thread, the lambda expression is executed in a separate thread, printing the message “Hello Runnable, Running now!” to the console. Here are some of the functional interfaces introduced as a part of the java.util.function package. 01.Consumer: A functional interface with a single method accept(T t), which takes an argument of type T and returns no value. It is used to define a function that consumes a value and performs some operation on it. 02.Function: A functional interface with a single method apply(T t), which takes an argument of type T and returns a value of type R. It is used to define a function that transforms a value from one type to another. 03.Predicate: A functional interface with a single method test(T t), which takes an argument of type T and returns a boolean value. It is used to define a function that tests a value and returns a boolean result. 04.Supplier: A functional interface with a single method get(), which takes no arguments and returns a value. It is used to define a function that produces a value. 03.Optional <T> Generally ‘null’ refers to the absence of a value. It is a special value that represents a variable or object reference that doesn’t refer to any object. Here are few reasons null is considered to bad in java. Trying to access a null object reference leads to a common runtime error called NullPointerException, which can be difficult to diagnose and fix. We often have to write extra code to check for null references, which can lead to cluttered and less readable code, and it can also add overhead to the application’s performance. Null can be ambiguous; if a method returns a null value, the caller can be confused as to whether it was intentional or if the method failed to execute correctly. Null can break contracts; null can break contracts when it violates the assumptions of interfaces or other parts of the program that rely on non-null values. Optional <T> is introduced in java 8, as a clean and concise way of dealing with values that may be present or not, without relying on null references. An Optional instance can either contain a non-null value, or it can be empty, indicating the absence of a value. A monad is a programming concept that provides a way to chain

Technology

A Personal Journey on Transitioning Into AWS Serverless

A personal outlook on my changeover — and adaptation — from traditionally managed-servers into AWS infrastructure Image source: https://serverguy.com/cloud/aws-migration/ Transitioning from any aspect into another in life can be daunting, and at the same time exciting. In IT, there are plenty of scenarios where this fact is actually applicable. One obvious example of homogenous transition is the ever-trivial version upgrades. From upgrading your dependencies to upgrading your runtimes, the range of problems you can face is tremendous. But as an attached sidecar, you almost always run into prodigious learning curves, where the return(s) of investments are positive when you think in terms of the progression of your career and as well as the advancements of your organisation’s systems. On the other hand, the range of problems might be tied to your organisational practices, and the quality of the upgraded constituents. The latter is one thing that we cannot control, and focusing on the former, a revealing consideration comes to my mind from the book “Software Engineering at Google” (read the online version here): it’s about the concept of “sustainability”. Sustainability of code, it says, depends on your ability to change — safely — what’s necessary for the lifetime of your code (this temporal factor is something that is forgotten quite often). If it’s otherwise, necessary and sufficient steps are required to cater for this without much delay. “Your organization’s codebase is sustainable when you are able to change all of the things that you ought to change, safely, and can do so for the life of your codebase.” — Winters et al. (2020). Software engineering at google: Lessons learned from programming over time. O’Reilly Media. This article, however, is not about what I called “homogenous transition” in the paragraph above. That is, it is not about upgrading versions of the same technology, but migrating/moving-on to a different technology — call it “heterogenous transition”. The obstacles you face might be different in nature, but depending on the quality of your decisions, the return(s) of investments can be plenty. It might also depend on the scale of your transition, i.e., you might be migrating one component of your architecture or you might be moving your whole architecture (with changes) into a different platform (via an IaaS, for instance). Or, it might be you, who’s migrating from one organisation to another, where the systems are totally different. And this, my dear reader, is the topic of conversation here. It all starts with a decision. I come from an organisational background where we managed our own servers for a large part of the organisation. There are plenty of reasons emphasised in general for managing your own servers, but one that has a bit less weight on is based on one party that’s involved in our contracts: the client. Client’s requirements and demands vary from domain to domain, and some clients do not prefer offloading server management to a third party (may it be privacy, security and/or for some other personal reasons). This was the reason for us managing our own. With the passage of time, the organisation evolved into more modern approaches, like containerising an application and orchestrating the containers. But still, those container images and orchestrators were deployed into our own server clusters: infrastructure managed by our own support teams. The personal journey in question starts with my decision to look for a better career opportunity. This decision was not least affected by technical parts of the organisation, but for other personal reasons where this article won’t trod into. Transitioning from one style of infrastructure to another have benefits and costs. It’s just a matter of weighing out the two and choosing either the former or the latter. Easily said than done, though. Facing it for the first time. This was what I saw for the first time: except the “Recently Visited” section was empty and the “Account” section was different. Can’t remember which “Region” it was by default. Some time ago, I stepped into the AWS universe. And this was not for a bootcamp or a training program. It was real-world application development. It was diving head-first into application development without knowing much about the end of the pipeline (deployment). Quite often development and deployment goes hand-in-hand, where decisions in one affects the other. For example, if you write code that accumulate too much garbage, your deployment aspects need to cater for that; otherwise, the applications would cease to run after some time. In summary, I knew how to write code alright, but didn’t know how to write code to be deployed in this new infrastructure. Did this transition affect in terms of development? Well, I cannot say that it did not — since it will rule out the fact that you must always be mindful when and where your application is going to be deployed. But, almost all of the business logic implementations were left in-tact just like we used to do when we were hosting our own infrastructure. I will try to some of the idiosyncrasies I saw in this transition in the next article.

Development Technology

How Git works- Part I – Object Storage

If you are a software developer, the likelihood that you use Git as your version control system is very high. Additionally, there is a good chance that you may encounter a circumstance where the commit history has been messed up at least once. What do you normally do in such situations? I guess you find some commands on Stack Overflow and, depending on the luck you have that day, you might resolve the problem quickly. Have you ever attempted to understand how Git functions? If the answer is yes, you may also be aware of the strength and solace it offers. However, if the response is negative, you might never consider it necessary. First of all, you must have adequate knowledge of Git’s operations. Let’s start by creating a local Git repository. Create an empty directory called “my_repo” and navigate inside it using the terminal. Run the command git status. It will indicate that it is not a Git repository (fair enough). So, how can we make a directory a Git repository? It’s simple! Run the command git init. Then you will notice that a new hidden directory called “.git” has been created, which stores all the magic that Git creates. Now, let’s see what is inside this directory. (I have used the tree /f .git command in Windows to visualize the tree) Content of the initial .git folder: Let’s add a file called “file1.txt” to the “my_repo” directory. Add simple text content (If you need to get the same result as shown, add a single line “first line of content” to the file), and run the command git status. You will see the following output: Git will not track anything automatically. You can verify it by looking at the “.git” directory. The above output explains how to track this file. Now let’s run git add file1.txt. Then, check the “.git” directory’s content. Two new files and one directory are now available in the “.git” folder. Let’s understand what these are. Git is a version control system, so it is required to store all the files that we track, version them, and provide a way to access these versions. The “objects” directory is used by Git for this purpose. Git has four types of objects: blobs, trees, commits, and annotated tags. Blobs: All the files (not directories) are stored as blobs. These are generated when you stage changes with the git add command. Hence, the file name “faed594a40f2d0aa7e45c5f26138cc24316565” is a blob that contains the content of “file1.txt”. But why is it in a directory called “d4”? The file name is also strange. This name is a hash generated by Git using the content of the file. Git provides a command by which you can generate this hash. Now you see “d4” is the first two letters of the hash, and “faed594a40f2d0aa7e45c5f26138cc24316565” is the remaining part. This directory works like an index. All the data with hash values starting with “d4” will end up in this directory, which increases the performance when searching for them. These files are in binary format. But Git provides commands to identify the type and content of these objects. Before understanding what this index file is, let’s add a directory called “sub” and copy “file1.txt” to this directory, renaming it to “file2.txt”. Then use the command git add . to track these changes. Now check the “.git/objects” directory. Did you notice that nothing new was added? It’s because “file2.txt” has the same content, so there’s no need to duplicate the same hash. A single blob but two files, I know that you are guessing about what is to be in the index file now. Given that it is also a binary file, we need the help of Git to view it. That’s what you call indexing or staging in Git. Every time you make a change in a file and stage it, a new blob will be created, and the index file keeps track of these changes. Even a single letter change in a file that is staged will create a new blob because the hash is changing, and the file will refer to the new blob hash. However, this is not enough for version controlling the files. That’s where commits come into play. Let’s commit the changes. Let’s see what has been added to the “.git/objects” directory. Before examining them, let’s think about what parts are missing in order to have proper version control. Remember that blobs do not contain any information about their file names or the directory in which they exist. During the staging phase, we have this data in the index file, and we need a similar file that contains this data. Since every file belongs to a directory, an obvious solution is to introduce an object that represents a directory and has references to its files and subdirectories. That’s what Git does by introducing an object type called a tree. In our case, we have two directories at the root of the repository, and the subdirectory is called “sub”, which means we have two tree objects. We can find and examine the two tree objects using the “git cat-file” command. Now, think about what the missing part is by asking the question “What version are we controlling?” The answer is the repository directory, which in our case is the “my_repo” directory. We already have a tree object for this directory. The only part required is a reference to it, which is the commit object. The commit object also contains details about the author and the timestamp. This is a special commit because it is the first commit. From the second commit onwards it will have pointers to its parent commits. Now we know the fundamental components of the Git storage model. However, branching, merging, garbage collection, and the remote repository are still lacking. These components are what make the true version control system complete. Let’s examine these ideas in more detail In the Part 2 of this blog post.

Contact Us

Let’s talk about building something great together.
Reach out—we’re here to answer questions, explore ideas, or start a new partnership.

Contact Us Phone