Category Archives: CS-443

The Intersection of Manual Testing, Automated Testing, Equivalence Class Testing, and Security Testing

Hello guys, welcome to my second blog. In my previous blog, I explored JUnit testing, focusing on boundary value analysis and how to use assert Throws for exception handling in Java. Those concepts helped us understand how to write robust test cases and ensure that our software behaves correctly at its limits.

Building on that foundation, this post expands into a broader discussion of manual vs. automated testing, equivalence class testing, and security testing. These methodologies work together to create a comprehensive testing strategy that ensures software is functional, efficient, and secure.

Introduction

Software testing is a critical part of the software development lifecycle (SDLC). Among the various testing methodologies, manual testing, automated testing, equivalence class testing, and security testing play vital roles in ensuring software reliability, performance, and security. This article explores these techniques, their strengths, and how they work together.

Manual vs. Automated Testing

Testing software can be broadly categorized into manual testing and automated testing. Each approach serves specific purposes and has its advantages.

Manual Testing

Manual testing involves human testers executing test cases without automation tools. It is useful for exploratory testing, usability testing, and cases requiring human judgment.

Pros:

  • Suitable for UI/UX testing and exploratory testing
  • Requires no programming skills
  • Provides a human perspective on software quality

Cons:

  • Time-consuming and repetitive
  • Prone to human errors
  • Inefficient for large-scale projects

Automated Testing

Automated testing uses scripts and testing frameworks to execute test cases automatically. It is ideal for regression testing and performance testing.

Pros:

  • Faster execution and scalable for large projects
  • Reduces human errors
  • Works well with continuous integration/continuous deployment (CI/CD)

Cons:

  • High initial setup cost
  • Requires programming expertise
  • Not suitable for exploratory testing

Example: Java Automated Test Using Selenium

Here is a basic example of using Java to automate a test: I have a simple Calculator class and an Add method. There is a test that runs without human intervention once executed

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

public class CalculatorTest {
    @Test
    public void testAddition() {
        Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);
        assertEquals(5, result); 
    }
}

Equivalence Class Testing

Equivalence Class Testing (ECT) is a black-box testing technique that reduces the number of test cases while ensuring comprehensive test coverage. It divides input data into equivalence classes, where each class represents similar expected outcomes.

How It Works:

  • Identify input conditions
  • Categorize inputs into equivalence classes (valid and invalid)
  • Select one representative input from each class for testing

Example: Equivalence Class Testing in Java

Consider a function that validates age input:

public class AgeValidator {
    public static String validateAge(int age) {
        if (age >= 18 && age <= 60) {
            return "Valid Age";
        } else {
            return "Invalid Age";
        }
    }

    public static void main(String[] args) {
        System.out.println(validateAge(25)); // Valid input class
        System.out.println(validateAge(17)); // Invalid input class
        System.out.println(validateAge(61)); // Invalid input class
    }
}

Security Testing: A Critical Component

Security testing ensures that applications are protected against cyber threats. This is increasingly crucial with rising cyberattacks and data breaches.

Key Security Testing Techniques:

  • Penetration Testing: Simulating cyberattacks to identify vulnerabilities.
  • Static Code Analysis: Reviewing source code for potential security flaws.
  • Dynamic Analysis: Testing a running application for security weaknesses.
  • Fuzz Testing: Inputting random data to identify unexpected behavior.

Example: Basic SQL Injection Test

A common security vulnerability is SQL Injection. Here’s an example of a vulnerable and a secure implementation:

Vulnerable Code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class SQLInjectionVulnerable {
    public static void getUserData(String userId) {
        try {
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
            Statement stmt = conn.createStatement();
            String query = "SELECT * FROM users WHERE id = " + userId; // Vulnerable to SQL Injection
            ResultSet rs = stmt.executeQuery(query);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Secure Code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class SQLInjectionSecure {
    public static void getUserData(String userId) {
        try {
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
            String query = "SELECT * FROM users WHERE id = ?";
            PreparedStatement pstmt = conn.prepareStatement(query);
            pstmt.setString(1, userId);
            ResultSet rs = pstmt.executeQuery();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

How These Testing Methods Work Together

Each testing approach contributes uniquely to software quality:

  • Manual and automated testing ensure functional correctness and usability.
  • Equivalence class testing optimizes test case selection.
  • Security testing safeguards applications from threats.

By integrating these methods, development teams can achieve efficiency, reliability, and security in their software products.

Conclusion

A strong testing strategy incorporates manual testing, automated testing, equivalence class testing, and security testing. Leveraging these approaches ensures robust software quality while improving development efficiency.

For further exploration, consider:

From the blog Rick’s Software Journal by RickDjouwe1 and used with permission of the author. All other rights reserved by the author.

Unit Testing and Testable Code


Unit testing is a fundamental practice in software development, ensuring that individual units of code work as expected. However, the real challenge often lies in writing code that is easy to test. Poorly designed, untestable code can complicate unit testing and introduce expensive complexity. In this blog post, we’ll explore the importance of writing testable code, the common pitfalls that make code hard to test, and the benefits of adopting testable coding practices.

The Significance of Unit Testing

Unit testing involves verifying the behavior of a small portion of an application independently from other parts. A typical unit test follows the Arrange-Act-Assert (AAA) pattern: initializing the system under test, applying a stimulus, and observing the resulting behavior. The goal is to ensure that the code behaves as expected and meets the specified requirements.

However, the ease of writing unit tests is significantly influenced by the design of the code. Code that is tightly coupled, non-deterministic, or dependent on mutable global state is inherently difficult to test. Writing testable code is not only about making testing less troublesome but also about creating robust and maintainable software.

Common Pitfalls in Writing Testable Code

Several factors can make code challenging to test, including:

  1. Tight Coupling: Code that is tightly coupled to specific implementations or data sources is difficult to isolate for testing. Decoupling concerns and introducing clear seams between components can enhance testability.
  2. Non-Deterministic Behavior: Code that depends on mutable global state or external factors (e.g., current system time) can produce different results in different environments, complicating testing. Making code deterministic by injecting dependencies can address this issue.
  3. Side Effects: Methods that produce side effects (e.g., interacting with hardware or external systems) are hard to test in isolation. Employing techniques like Dependency Injection or using higher-order functions can help in decoupling and testing such code.

Benefits of Testable Code

Adopting testable coding practices offers several benefits:

  1. Improved Code Quality: Testable code is typically well-structured, modular, and easier to understand. This leads to higher code quality and reduces the likelihood of bugs.
  2. Easier Maintenance: Code that is easy to test is also easier to maintain. Changes can be made with confidence, knowing that unit tests will catch any regressions.
  3. Faster Development: With a robust suite of unit tests, developers can iterate quickly and confidently, reducing the time spent on manual testing and debugging.
  4. Enhanced Collaboration: Clear and testable code promotes better collaboration among team members, as the intent and behavior of the code are easier to comprehend.

Conclusion

Writing testable code is a crucial aspect of software development that extends beyond the realm of testing. It encompasses good design principles, decoupling, and the elimination of non-deterministic behavior and side effects. By focusing on writing testable code, developers can create software that is not only easier to test but also more robust, maintainable, and reliable. Embracing these practices ultimately leads to higher quality software and more efficient development processes.

All of this comes from the link below:

https://www.toptal.com/qa/how-to-write-testable-code-and-why-it-matters


From the blog CS@Worcester – aRomeoDev by aromeo4f978d012d4 and used with permission of the author. All other rights reserved by the author.

Always Improving Software

In this post, I’ll be discussing my thoughts primarily on Chapter 1 of Apprenticeship Patterns, a book I had to read parts of for another class. I really like the idea of being a “Software Apprentice,” as I think it is a good representation of the attitude I have adopted over the years. I’ve always viewed myself as someone who is constantly learning and evolving in this field, and the apprenticeship model seems to align perfectly with that. It’s not about arriving at some endpoint but about growing, making mistakes, and improving.

What stood out to me the most was the idea of viewing the development process as a journey, not a destination. Chapter 1 introduces the concept of “Software Craftsmanship” as more than just technical skill, but also as a mindset of continuous learning. This was particularly thought-provoking because I think there’s a tendency, especially early in our careers, to think of coding as a task to be completed or a skill to be mastered and then moved beyond while in pursuit of some computer science career. However, the idea that becoming a Software Craftsman is an ongoing process—a commitment to constant improvement and a mindset that thrives on learning—is something that I think is a better description. It shifted my perspective on what it means to be good at coding- it’s not enough to know how to do something today, it’s about always finding a way to improve your code in the future.

In the past, specifically in my career, I’ve often gotten caught up in the day-to-day rush of just getting whatever piece of code done. I’ve rushed to get things coded, with the focus being on completing projects rather than developing deeper expertise or refining my approach to coding. After reading this chapter, I think I’m more likely to focus on the long-term value of writing quality code and the importance of standards. It’s not just about writing code that works; it’s about writing code that lasts, is readable, and contributes to a more sustainable project. I’m going to start to approach my coding tasks with a deeper goal, rather than rushing through them for the sake of completion.

I think this is particularly applicable to Software Quality Assurance and Testing because QA is, in essence, a form of ensuring that our code is always improving. Not only do we need to find a balance between constantly improving before release and actually releasing, but even after releases, we still need to be looking for future requirements and improvements. There is no end in sight for software testing, ever. I think this constant forward motion, driven by constantly increasing requirements, is inseparable from the idea of being a software apprentice.

From the blog Mr. Lancer 987&#039;s Blog by Mr. Lancer 987 and used with permission of the author. All other rights reserved by the author.

Always Improving Software

In this post, I’ll be discussing my thoughts primarily on Chapter 1 of Apprenticeship Patterns, a book I had to read parts of for another class. I really like the idea of being a “Software Apprentice,” as I think it is a good representation of the attitude I have adopted over the years. I’ve always viewed myself as someone who is constantly learning and evolving in this field, and the apprenticeship model seems to align perfectly with that. It’s not about arriving at some endpoint but about growing, making mistakes, and improving.

What stood out to me the most was the idea of viewing the development process as a journey, not a destination. Chapter 1 introduces the concept of “Software Craftsmanship” as more than just technical skill, but also as a mindset of continuous learning. This was particularly thought-provoking because I think there’s a tendency, especially early in our careers, to think of coding as a task to be completed or a skill to be mastered and then moved beyond while in pursuit of some computer science career. However, the idea that becoming a Software Craftsman is an ongoing process—a commitment to constant improvement and a mindset that thrives on learning—is something that I think is a better description. It shifted my perspective on what it means to be good at coding- it’s not enough to know how to do something today, it’s about always finding a way to improve your code in the future.

In the past, specifically in my career, I’ve often gotten caught up in the day-to-day rush of just getting whatever piece of code done. I’ve rushed to get things coded, with the focus being on completing projects rather than developing deeper expertise or refining my approach to coding. After reading this chapter, I think I’m more likely to focus on the long-term value of writing quality code and the importance of standards. It’s not just about writing code that works; it’s about writing code that lasts, is readable, and contributes to a more sustainable project. I’m going to start to approach my coding tasks with a deeper goal, rather than rushing through them for the sake of completion.

I think this is particularly applicable to Software Quality Assurance and Testing because QA is, in essence, a form of ensuring that our code is always improving. Not only do we need to find a balance between constantly improving before release and actually releasing, but even after releases, we still need to be looking for future requirements and improvements. There is no end in sight for software testing, ever. I think this constant forward motion, driven by constantly increasing requirements, is inseparable from the idea of being a software apprentice.

From the blog Mr. Lancer 987&#039;s Blog by Mr. Lancer 987 and used with permission of the author. All other rights reserved by the author.

Always Improving Software

In this post, I’ll be discussing my thoughts primarily on Chapter 1 of Apprenticeship Patterns, a book I had to read parts of for another class. I really like the idea of being a “Software Apprentice,” as I think it is a good representation of the attitude I have adopted over the years. I’ve always viewed myself as someone who is constantly learning and evolving in this field, and the apprenticeship model seems to align perfectly with that. It’s not about arriving at some endpoint but about growing, making mistakes, and improving.

What stood out to me the most was the idea of viewing the development process as a journey, not a destination. Chapter 1 introduces the concept of “Software Craftsmanship” as more than just technical skill, but also as a mindset of continuous learning. This was particularly thought-provoking because I think there’s a tendency, especially early in our careers, to think of coding as a task to be completed or a skill to be mastered and then moved beyond while in pursuit of some computer science career. However, the idea that becoming a Software Craftsman is an ongoing process—a commitment to constant improvement and a mindset that thrives on learning—is something that I think is a better description. It shifted my perspective on what it means to be good at coding- it’s not enough to know how to do something today, it’s about always finding a way to improve your code in the future.

In the past, specifically in my career, I’ve often gotten caught up in the day-to-day rush of just getting whatever piece of code done. I’ve rushed to get things coded, with the focus being on completing projects rather than developing deeper expertise or refining my approach to coding. After reading this chapter, I think I’m more likely to focus on the long-term value of writing quality code and the importance of standards. It’s not just about writing code that works; it’s about writing code that lasts, is readable, and contributes to a more sustainable project. I’m going to start to approach my coding tasks with a deeper goal, rather than rushing through them for the sake of completion.

I think this is particularly applicable to Software Quality Assurance and Testing because QA is, in essence, a form of ensuring that our code is always improving. Not only do we need to find a balance between constantly improving before release and actually releasing, but even after releases, we still need to be looking for future requirements and improvements. There is no end in sight for software testing, ever. I think this constant forward motion, driven by constantly increasing requirements, is inseparable from the idea of being a software apprentice.

From the blog Mr. Lancer 987&#039;s Blog by Mr. Lancer 987 and used with permission of the author. All other rights reserved by the author.

Software Testing Thinking Hats

The blog post I’ve chosen talks about the different mind sets that you should use when tackling different situations. When writing tests, it helps to write tests with different goals so that you don’t forget to test every aspect of some section of code. The blog author talks about how trying to do the tests out of order or without any order in mind you can forget things and cause it to take more time in the long run. The author categorizes these as “hats” to wear during each progressive stage. There is the ambitious hat which is for when you are testing the code in the way the customer will be using the code. There is then an uncomfortable hat which is for when your tests are failing, and you need to fix what you can in your testing. The confident hat is the one you can wear while you are refactoring your code and running just to confirm everything passes. The author also makes specific mention to not try and wear multiple hats at once.
From my experience in the classes I’ve taken so far, I’ve done some built in testing in files for whole programs that I myself have written, and so I know exactly what the code should do, what I need to have pass in order to meet the homework criteria. In these cases, because I am both the author of the code and the author of the test cases, I am able to make edits in both parts with the knowledge of exactly what the test cases should be. This has helped in my current class so far but as we move forward, we will be approaching testing like it is done in the workplace and when the people testing the code are a separate group that has not touched the program code. I’ve definitely seen myself as the coder that the author describes who frantically moves around the code and tests trying whatever I can to get everything to work instead of approaching it with any methodology. I want to become both better and more efficient at testing code as it will help me write code also and further my understanding of what makes a complete program. Testing is a big part of working in the computer science field and I know that quality assurance is very important, so I look forward to both learning more about this within the course and doing more blogs about the industry.

Chris James – The TDD Thinking Hats

From the blog CS@Worcester – Computer Science Blog by dzona1 and used with permission of the author. All other rights reserved by the author.

Effective Exception Testing in JUnit 5: Boundary Value Testing and AssertThrows

Introduction

In modern software development, ensuring that a program handles exceptions correctly is crucial for building robust applications. Exception testing in JUnit 5 allows developers to verify that their code properly handles error scenarios, improving reliability and maintainability. This blog post explores key techniques such as Testing for Exceptions in JUnit 5, Boundary Value Testing, and using AssertThrows to create effective test cases.

Testing for Exceptions in JUnit 5

JUnit 5 provides a streamlined way to test exceptions in Java applications. Unlike JUnit 4, which required using the expected attribute or @Rule, JUnit 5 introduces Assertions.assertThrows(), offering a more flexible and readable approach.

Example of Exception Testing in JUnit 5

Consider a method that calculates the square root of a number. If a negative number is provided, it should throw an IllegalArgumentException.

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

class MathUtilsTest {

    double calculateSquareRoot(double number) {
        if (number < 0) {
            throw new IllegalArgumentException("Number must be non-negative");
        }
        return Math.sqrt(number);
    }

    @Test
    void testCalculateSquareRootException() {
        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
            calculateSquareRoot(-5);
        });
        assertEquals("Number must be non-negative", exception.getMessage());
    }
}

This test verifies that the method correctly throws an exception when given invalid input, ensuring robustness.

Boundary Value Testing

Boundary Value Testing (BVT) is a technique used to test the limits of input values. It focuses on edge cases, such as minimum and maximum values, where software is most likely to fail.

Example: Boundary Testing for Age Validation

Consider a function that validates a user’s age for registration, allowing only ages between 18 and 65.

boolean isValidAge(int age) {
    return age >= 18 && age <= 65;
}

Boundary tests should check values just inside and just outside the valid range:

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

class AgeValidatorTest {
    
    @Test
    void testAgeBoundaries() {
        assertTrue(isValidAge(18)); 
        assertTrue(isValidAge(65));  
        assertFalse(isValidAge(17)); 
        assertFalse(isValidAge(66)); 
    }
}

BVT ensures the system correctly distinguishes between valid and invalid inputs.

Testing for Exceptions with AssertThrows

The assertThrows method in JUnit 5 simplifies exception testing, making tests more readable and maintainable. It helps validate that methods correctly handle invalid inputs by throwing the expected exceptions.

Example: Division by Zero Handling

Consider a simple method that performs division:

int divide(int dividend, int divisor) {
    if (divisor == 0) {
        throw new ArithmeticException("Cannot divide by zero");
    }
    return dividend / divisor;
}

We can use assertThrows to verify proper exception handling:

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

class DivisionTest {

    @Test
    void testDivideByZero() {
        ArithmeticException exception = assertThrows(ArithmeticException.class, () -> {
            divide(10, 0);
        });
        assertEquals("Cannot divide by zero", exception.getMessage());
    }
}

This ensures that the division method correctly throws an exception when dividing by zero.

Conclusion

Testing exceptions effectively is a vital part of software quality assurance. JUnit 5’s assertThrows method, combined with Boundary Value Testing, enables developers to create thorough test cases that improve the reliability and robustness of applications. By writing well-structured exception tests, developers can prevent unexpected failures and ensure their applications behave as expected under various conditions.

For further reading, check out:

#JUnit5 #ExceptionTesting #AssertThrows #BoundaryValueTesting

From the blog Rick’s Software Journal by RickDjouwe1 and used with permission of the author. All other rights reserved by the author.

More JUnit

On my previous post, I stated that I was introduced to JUnit. I stated its definition, and a vast list of its annotation uses.

Now I have moved past that and started applying that knowledge. Last class, my group and I were unit testing with JUnit 5. We ran and made tests in the Rectangle class. We also ran tests using Gradle, which makes testing easier.

On that note I was looking through an article that gave me more information on JUnit testing. It is called: JUnit Testing Framework – A Comprehensive Guide | HeadSpin. It retreads on known ground but gives step by step guides on how to access and download certain things. One thing I can commend it for is the detail in certain parts of the blog. It feels like a more advanced version of the previous one like, the previous one would be used for a quick Google search while this one would be used for a step-by-step guide.

It also introduces new information such as debugging. I personally know barely anything about debugging. I have seen it but I never really understood its function. I remember in freshman year I assumed that it would aid in fixing coding mistakes so I tried it. Unfortunately, it did not do anything.

Now, how does debugging work in JUnit? There are two modes: Java Debug Wire Protocol and Standard Streams. Java Debug Wire Protocol is the one that is more likely to be recommended. It can be used broadly and provides a lot of information from test execution. In Standard Streams everything that would usually go to the main console window would be printed to the standard output stream. Unfortunately, this provides less information than Java Debug Wire Protocall so is better fit for simple tests.

I also learned some troubleshooting techniques for Java applications. I learned that one way to troubleshoot is to use log files or profiles to gather information about the application. Also when there are issues with libraries or testing frameworks, it is good to check their documentation and forums.

Overall, I feel like the article should have described more about debugging in JUnit. I feel like I got a good understanding of Standard Stream, but I do not feel like I fully grasped Java Debug Wire Protocol. The definition provided was not enough. Maybe because the article assumed the reader knows debugging and/or used it. I am not sure. Nevertheless, It was nice to learn more about JUnit. I hope to use it again in the future.

From the blog CS@Worcester – My Journey through Comp Sci by Joanna Presume and used with permission of the author. All other rights reserved by the author.

Introductory Blog CS-443

Hello and welcome to my blog!

My name is Elio Ngjelo and I am taking Software Quality Assur & Test, CS-443. This is where I will be posting blogs on the different topics I will be learning throughout the semester.

Thank you for reading!

From the blog CS@Worcester – Elio&#039;s Blog by Elio Ngjelo and used with permission of the author. All other rights reserved by the author.

First CS-443 Blog

This is my first blog for CS-443. It will include posts related to software testing.

From the blog CS@Worcester – My first blog by Michael and used with permission of the author. All other rights reserved by the author.