Series: Software Engineering Fundamentals | Part Part 13 of 19 > Delivered at Universidade Potiguar (UnP) in 2010
In this lecture, we dived into Software Testing—not as a boring list of types or QA certifications, but as a creative, strategic activity embedded in every stage of real software development. I reminded the class that testing isn’t a gatekeeping step, it’s part of the design process. Every good engineer should see testing as part of their toolkit, not someone else’s responsibility.
We started with a provocative idea: “Testing doesn’t prove a system works. It proves that it doesn’t always fail.” That resonated because it shifts the burden: we’re not chasing perfection, we’re designing confidence.
Testing as a Tool for Understanding
We introduced testing as a learning mechanism. Students were asked to list five reasons why testing matters. Among their answers: “to discover bugs,” “to verify if the system works,” “to gain confidence.”
We discussed how tests help expose ambiguities in requirements, flaws in integration, and even usability holes. I showed how a test-first mindset clarifies scope before you write the first line of code.
Here’s a basic unit test for a class that calculates delivery time:
@Test
public void testExpressDeliveryCalculation() {
DeliveryCalculator calculator = new DeliveryCalculator();
int days = calculator.calculate("express", 120);
assertEquals(1, days);
}
This simple test forces you to define what “express delivery” means and what rules drive its calculation. A good test reveals missing business logic before it becomes customer frustration.
Types of Tests and When to Use Them
We covered various test types: unit, integration, system, acceptance, UI, performance, security. But instead of giving definitions, I challenged each team to match a test type to a bug they had faced in the past. It created lively conversations.
We talked about test responsibility too:
- Developers write unit and integration tests.
- Test teams handle system, UI, and exploratory testing.
- Users validate via acceptance testing.
To reinforce this, we wrote a functional test for login flow:
def test_user_login():
user = create_user("maria@example.com", "secure123")
response = client.post("/login", data={"email": "maria@example.com", "password": "secure123"})
assert response.status_code == 200
assert b"Welcome, Maria" in response.data
Functional tests like this one focus on what the system should do, regardless of internal implementation.
Building Trust Through Testing
I introduced the class to the “Testing Ice Cream Cone” problem—too many UI tests, too few unit tests. We reflected on the Test Pyramid and how to balance effort for speed and reliability.
Students analyzed a system they were building and proposed how to refactor tests following a more scalable approach. One group realized they had no performance tests, even though their app relied on fast response time.
We then discussed how to evolve a test suite to support system growth. I introduced this TDD-style test for an evolving pricing rule:
describe PricingEngine do
it "applies 10% discount for students" do
price = PricingEngine.new(base: 100, user_type: "student").final_price
expect(price).to eq(90)
end
end
Even a single failing test like this one can steer architectural decisions and refactoring.
Exercises That Make Testing Tangible
To wrap the session, I split the class into test squads. Each team had to build a minimal test suite for a fake e-commerce feature using only:
- One requirement
- One user flow
- One key constraint (e.g., response time < 2s)
They shared strategies, tool choices, and how tests helped them find bugs they didn’t even know existed. We ended by compiling a list of “bugs found through tests”—a visual reminder that tests are not a cost, but a lens.
Facilitators can apply the same exercise in onboarding, retrospectives, or hackathons. It encourages teams to take ownership of testing—not as checklist, but as insight.
Posted as part of the Software Engineering course journal. Today we learned that testing isn’t about proving perfection—it’s about designing confidence and building systems that evolve gracefully.
Series Navigation
- Introduction: Part 1 - Why Software Engineering?
- Previous: Part 12 - Requirements & Testing
- Next: Part 14 - Test-Driven Development
- Current: Part 13 - Software Testing
- Complete series: Why Software Engineering? | Taming Complexity | Waterfall Model | Evolutionary Models | Agile Mindset | Scrum Productivity | Scrum Cycle | XP Quality & Courage | XP Principles & Practices | XP in Practice | Domain-Driven Design | Requirements & Testing | Software Testing | Test-Driven Development | Unit Testing with JUnit