0% found this document useful (0 votes)
1 views

Unit Tests Without Harness

Designing unit tests is essential for verifying the functionality of individual software components in isolation, focusing on specifying test cases and procedures. Key components include well-documented test cases, which outline input data and expected outputs, and test procedures detailing execution steps. Effective unit test design incorporates both white box and black box testing strategies, emphasizes reusability, and addresses specific challenges in object-oriented programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
1 views

Unit Tests Without Harness

Designing unit tests is essential for verifying the functionality of individual software components in isolation, focusing on specifying test cases and procedures. Key components include well-documented test cases, which outline input data and expected outputs, and test procedures detailing execution steps. Effective unit test design incorporates both white box and black box testing strategies, emphasizes reusability, and addresses specific challenges in object-oriented programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 8

✍️ Topic: Designing the Unit Test

(6-Mark Answer Style – University Level | Can fill 2–3 pages handwritten)

✅ Introduction:

Designing unit tests is a critical step in ensuring that individual software components (units)
function correctly in isolation. Unit testing is the first level of testing in the software testing life
cycle and primarily focuses on verifying that each software unit performs as intended. The unit can
be a function, method, procedure, or class, depending on whether the system is procedural or
object-oriented.

🎯 Objectives of Unit Test Design:

The two main objectives when designing unit tests are:

1. Specification of Test Cases:

o Input data

o Expected output

2. Specification of Test Procedures:

o Steps to execute the tests

o How to verify and record results

📄 Components of Unit Test Design:

1. Test Cases:

o Each test case includes:

▪ Input values

▪ Expected output

▪ Preconditions/postconditions

▪ Purpose of the test

o These are often tabularized for easy understanding and reuse.

2. Test Procedures:
o Describe how to run each test case.

o Include initial setup, sequence of calls, expected state transitions, etc.

🧠 Example of a Test Case Table:

Test Case ID Input Expected Output Purpose

TC001 push(1), push(2) top() = 2 Test stack push & top

TC002 pop() on empty Error message Test pop on empty stack

🔍 Techniques Used:

• White Box Testing:

o Focuses on internal logic.

o Covers all possible paths, branches, and loops.

o Common techniques:

▪ Statement coverage

▪ Branch coverage

▪ Path coverage

▪ Data flow testing

• Black Box Testing:

o Based on functionality.

o No knowledge of internal code.

o Used in smaller COTS units.

o Useful when unit input/output behavior is well defined.

📝 Note: Though white box is more common at unit level, both methods can be used depending
on the context.

🧩 Special Considerations in OOP:


• Semantic Test Case Notation (Berard’s Model):

o Object_ID

o Test_Case_ID

o Purpose

o List_of_Test_Case_Steps

• Tests may include:

o Message sequences (method calls)

o Exceptions

o Interrupts

o Object states and transitions

♻️ Reusability of Test Cases:

• Test suites and test procedures may be reused from past projects.

• Helps in regression testing and maintaining consistency.

🔗 Relation to Test Plan:

• The unit test design is attached to the unit test plan.

• Includes:

o Test data

o Test logs

o Incident reports

o Resources required

🧪 Example – Stack Class Test Design:

Test sequence:
create(s,3), empty(s), push(s,item-1), push(s,item-2), push(s,item-3), full(s), show_top(s), pop(s),
show_top(s), empty(s)
Purpose:
To check stack operations and interaction of methods.

✅ Conclusion:

Unit test design ensures that each unit is thoroughly tested for correctness before integration.
Effective design includes well-documented test cases, test procedures, and appropriate selection of
testing strategies (white box, black box, or both). Reusability and completeness are key to ensuring
long-term quality and reduced maintenance costs.

Designing the Unit Tests

Part of the preparation work for unit test involves unit test design. It is important to specify:

1. The test cases (including input data, and expected outputs for each test case), and

2. The test procedures (steps required to run the tests).

These items are described in more detail in Chapter 7.

Test case data should be tabularized for ease of use and reuse. Suitable tabular formats for test cases
are found in Chapters 4 and 5.

To specifically support object-oriented test design and the organization of test data, Berard has
described a test case specification notation. He arranges the components of a test case into a
semantic network with parts:

• Object_ID

• Test_Case_ID

• Purpose

• List_of_Test_Case_Steps

Each of these items has component parts.

In the test design specification, Berard also includes lists of:

• relevant states

• messages (calls to methods)

• exceptions, and
• interrupts

As part of the unit test design process, developers/testers should also describe the relationships
between the tests. Test suites can be defined that bind related tests together as a group.

All of this test design information is attached to the unit test plan. Test cases, test procedures, and
test suites may be reused from past projects if the organization has been careful to store them so that
they are easily retrievable and reusable.

Test case design at the unit level can be based on use of the black and white box test design
strategies described in Chapters 4 and 5. Both of these approaches are useful for designing test cases
for functions and procedures. They are also useful for designing tests for the individual methods
(member functions) contained in a class.

Considering the relatively small size of a unit, it makes sense to focus on white box test design for
procedures/functions and the methods in a class. This approach gives the tester the opportunity to:

• exercise logic structures

• data flow sequences, or

• use mutation analysis

—all with the goal of evaluating the structural integrity of the unit.

Some black box-based testing is also done at unit level; however, the bulk of black box testing is
usually done at the integration and system levels and beyond.

In the case of a smaller-sized COTS component selected for unit testing, a black box test design
approach may be the only option.

It should be mentioned that for units that perform mission/safety/business critical functions, it is
often useful and prudent to design stress, security, and performance tests at the unit level if
possible.

THE CLASS AS A TESTABLE UNIT


In object-oriented programming, software is developed using classes and objects. When performing
unit testing in such systems, we need to decide what should be considered as the "unit" — either an
individual method or the entire class.
Each option has its own pros and cons, and testers must carefully choose the right component for
unit testing.

🔹 Option 1: Method as a Unit


• Testing individual methods gives more control.
• But if a method calls other methods in the same class, we must create extra code (test
harness) to simulate those other methods.
• This can be costly and time-consuming because we might end up duplicating parts of the
class.
• Still, this method-level testing ensures that all code paths and logic are verified, especially for
critical methods (mission/safety/business).

🔹 Option 2: Class as a Unit (Component Testing)


• Many developers prefer to test the whole class as a unit.
• This process is also called Component Testing.
• The idea is to check how all the methods in a class work together and interact with shared
data.

🔍 Why Choose the Class as a Unit?


• A class contains multiple methods that often depend on each other.
• Testing the class allows us to catch:
o Control flow errors
o Data flow errors
o Errors related to object-oriented features like:
▪ Encapsulation
▪ Inheritance
▪ Polymorphism
• We can also detect object management faults, like:
o Incorrect object creation
o Improper storage/retrieval

💡 Example: Stack Class


Suppose we have a class Stack with methods like:
• push(), pop(), empty(), full(), show_top()
To test this class:
• We perform a sequence of method calls, such as:
plaintext
CopyEdit
create(s,3), empty(s), push(s,item-1), push(s,item-2), push(s,item-3),
full(s), show_top(s), pop(s), pop(s), pop(s), empty(s)
This sequence helps check:
• Correct insertion and deletion
• Proper stack pointer changes
• State transitions (like full → not full, or empty → not empty)

🔁 Conclusion
Testing at the class level is more practical and often more efficient, especially in object-oriented
systems. It helps ensure that methods work both individually and together on shared data. Although
testing each method can give more control, testing the entire class gives a better picture of overall
class behavior, especially when dealing with method interactions and internal states.

✅ Issues in Class Testing


(Exact from Notes – Proper Sequence)
✅ What is Class Testing?
In object-oriented programming, we build code using classes (not just functions).
So during testing, instead of testing one small method alone, we often test the entire class,
including all its methods together.
But testing a full class has some challenges (issues). These are called the Issues in Class
Testing.
There are 4 main issues:

🔹 Issue 1: Testing All Methods in the Class (Adequately Testing Classes)


• A class can have 20–30 methods.
• Testing each one separately takes a lot of time and effort.
• Instead, testers test the class as a whole, and let some methods call each other during
testing.
• This saves time because some methods act like test drivers or helpers for others.
• But we must still be careful to check:
o Does every method work correctly?
o Do they interact properly?
• Example: In a Stack class with methods like push(), pop(), empty(), full(), and
show_top(), we test sequences like:
scss
CopyEdit
create(s,3), empty(s), push(s,item-1), push(s,item-2), full(s), pop(s), show_top(s)
• We don’t test every possible sequence (too many), so we choose the ones most likely
to find bugs.

🔹 Issue 2: Watching Object States (Observation of Object States and State Changes)
• Sometimes, methods don’t return any value — they just change the state of the
object.
• For example, push() in a stack just adds an item — it doesn't return anything.
• To check if push() worked, we use show_top() or full() to observe the new state.
• We must test if these state changes are correct.
• Example test sequence:
scss
CopyEdit
empty(s), push(s,item-1), show_top(s), push(s,item-2), show_top(s), full(s)
• This helps us watch how the object behaves when we use different methods.

🔹 Issue 3: Retesting When Internal Code Changes (Retesting of Classes – I)


• In OOP, encapsulation hides internal code from outside.
• So if we change the internal logic of a method (but not its name or inputs), we only
need to:
o Retest that class.
o We don’t need to retest other classes that use it.
• But if the changed class is a superclass (parent class), then:
o We must also retest all subclasses, because they might behave differently after
the change.
• Even adding a new subclass can affect old code — so we test again.

🔹 Issue 4: Retesting When Inherited Methods Are Used Differently (Retesting of Classes – II)
• Subclasses can inherit methods from parent classes.
• People often think: “The parent method worked fine, so no need to test it again.”
• ❌ Wrong! The same method can behave differently in a new context.
• Or the subclass may override the method with a new version.
• In both cases, we must test again.
• Example:
o Shape has a method display() which uses color().
o Triangle changes color().
o EquilateralTriangle defines a new display() which now uses the new color().
o This new combo must be tested, even if both methods were already tested
before.

✅ Conclusion
Testing classes is more than just running methods. We need to test:
• Whether all methods work properly
• Whether state changes are correct
• Whether changes in one class affect others
• Whether inherited or overridden methods behave as expected
These 4 issues help us plan complete and correct testing for object-oriented systems.

You might also like