Unit Tests Without Harness
Unit Tests Without Harness
(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.
o Input data
o Expected output
1. Test Cases:
▪ Input values
▪ Expected output
▪ Preconditions/postconditions
2. Test Procedures:
o Describe how to run each test case.
🔍 Techniques Used:
o Common techniques:
▪ Statement coverage
▪ Branch coverage
▪ Path coverage
o Based on functionality.
📝 Note: Though white box is more common at unit level, both methods can be used depending
on the context.
o Object_ID
o Test_Case_ID
o Purpose
o List_of_Test_Case_Steps
o Exceptions
o Interrupts
• Test suites and test procedures may be reused from past projects.
• Includes:
o Test data
o Test logs
o Incident reports
o Resources required
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.
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
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
• relevant states
• 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:
—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.
🔁 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.
🔹 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 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.