facebook

Software Testing Basics: A Practical Guide (What We Do)

A feature can demo perfectly in a sprint review and still fall apart within a week of going live, and in our experience, it’s rarely because the code was written carelessly, but because the edge cases nobody thought to check never got checked at all. 

We’ve watched this pattern play out across enough client projects at Agicent to know that the problem usually isn’t a lack of technical talent, but a testing gap that was never properly addressed.

So in this guide of “software testing basics,” we’d actually walk a new hire, or a founder evaluating their first dev partner, through before a build starts. Not a glossary or tool comparison, just what testing is, why it earns its place in a timeline, and how we build it into projects that have to work for real users.

What is software testing? 

Software testing is the act of testing to determine if an application is performing as expected, and as the surrounding code changes, the software continues to perform as expected. Under the hood of almost every test you write, you have two questions: Is this feature going to behave correctly, and will it behave correctly after the next release.

Some of the terms are used loosely, so it’s worthwhile and convenient to define them once and for all and move on.

  • Defect: The underlying flaw in the code. This is what a developer actually fixes.
  • Bug: The everyday word for a defect. Same thing, less formal.
  • Failure: The experience of a user who encounters a defect, such as a crash, incorrect answer or a simply non-loading screen.
  • Verification vs validation: Verification queries if software was constructed properly according to its specification. Validation is asking if the spec is the right spec to build. A feature may be verified correctly and thus not successfully passed validation if it addresses an issue that no one was concerned about.
  • Test case vs test suite: A test case is a particular scenario, containing steps, inputs, and a desired outcome. A suite is a collection of cases that are typically executed together, generally for functionality that is related or executed at the same stage of a release.

All of this is very real. But when a developer tells a product manager “bug” and a PM responds “failure”, the talk of prioritization is off track from the get-go. Shared vocabulary is what enables a distributed team, with client and agency, to discuss the right things.

Why testing gets skipped, and what that costs later 

Startups skip testing for a reason that makes sense in the moment: there’s a launch date, a limited budget, and testing seems like a step that you can go through without anyone knowing. Sometimes, that’s even the case, for a week or two. Then the cost is somewhere else.

The IBM Systems Sciences Institute has revealed that it is up to 100 times more expensive to correct a defect in production than a defect detected in design. The multiplier effect increases as it goes along. The losses are just as quick on the user-end. 

And when a large percentage of mobile users crash, they uninstall the app and do not return – and that’s not only a bug costing engineering hours later, it’s a cost to the users you paid to get your app.

We experienced the other side of that with Scowtt, their predictive sales and marketing intelligence platform, which we’ve been able to get from an early MVP up to a funded, in-house AI-powered, enterprise-ready product with tons of CRM integrations. 

The requirements of the QA bar for a scrappy MVP and the QA bar for a product enterprise that is trusted with their pipeline data are not the same, and the leap from the MVP QA bar to the product enterprise QA bar only happens if testing discipline follows the feature set. 

But a critical thing we do in MVP development services engagements from the get-go; we don’t sacrifice testing for a date, we scope it to be a singular part of the product that grows as it does.

The levels of testing: where in the stack you’re checking 

These are the 4 levels of testing, and each level of testing addresses a slightly different question, and finding the balance right is key to making your test suite fast rather than fat.

  1. Unit testing verifies the smallest piece of logic without depending on any other logic, that is, a single function or validation rule. It’s low cost to write, quick to run and is typically the first to execute on each pull request.
  2. Integration testing tests the behaviour of components when they are communicating. API calls connect with a database, or a service connects with a queue here. It’s also one of the places where a lot of real bugs tend to occur, where each individual piece can work just fine and then it gets to the time where it has to pass data on to something else, and that piece fails as well. 

This is the most important layer on UBTEO, a real estate marketplace that we built where sellers can list a property, create legal documents and only be matched with pre-approved buyers. 

Even if a listing service and a document generator pass their own tests, they can still create the incorrect documents if the handoff between them is not tested.

  1. System testing is done to validate the product as a complete system, end-to-end, against the original business requirements.
  2. UAT is a form of acceptance testing and ensures that the product does actually address the problem stakeholders or end users brought to it. It is the final stop before something ships and it’s where validation, not merely verification, gets its turn.

What types of testing beginners should actually focus on

Functional Testing is used to determine if a feature acts as it is supposed to. Login is correct with the proper credentials. Search returns the right results. And a form saves the data it was given.

Non-functional testing verifies a feature’s desirable side attributes that are not directly related to its functionality: response times, failure modes, security, endurance under load, etc.

A few points should be noted here about security testing that are more suitable to a discussion than to an exhaustive list: Compliance is not a checkbox that is checked once you’ve launched a HIPAA compliant platform, as we did on MediOrbis, a HIPAA compliant telemedicine platform that connects patients with specialists, and on Dawn Health, a HIPAA compliant preventive mental health platform for families. 

It is a requirement of the test in each release, because the data involved is the very type of data that can be a bug or a breach. 

In fact, performance and reliability testing are as important when a product has a real foot in the door. HASfit, our home fitness app used by 4 million people for 13 years, which was featured on NBC, TechCrunch, and Men’s Health on the way, and Wellcure, a natural health platform used by 50,000 people per day, running on AWS, are all examples of testing that’s not an afterthought in launch week. 

It’s what makes an app strong and robust to sustain its user base for years, not weeks. There’s also a classification regarding the amount of the tester’s knowledge of the internal code:

Black-box testing: the testers do not know anything about the code, they only look at the user’s point of view.

White-box testing is performed using full knowledge of the internal logic to verify certain code paths and code branches, typically at the unit or integration level.

Grey-box testing is in between, having partial knowledge of the architecture and an outsider’s point of view, and that proves to be very valuable at the integration level.

Last but not least, manual and automated testing are not competitive. Manual testing is the place for exploratory testing, new features and usability decisions, as a human will see things that a script will not see. 

Automated testing is worth its weight on anything that must be run in exactly the same manner, each and every time: regression checks, CI pipelines, release gates. The “business” rule that most teams settle on: Find problems with manual testing, prevent them from happening again with automated testing.

The Software Testing Life Cycle (STLC) in a Sprint

The software testing life cycle may seem formal, but it is nothing more than the repetition that keeps a release from getting out of hand.

Planning: The scope and risk define the planning. What’s being tested, what is likely to fail, and what are the implications of “ready to ship” for this release?

Test design transforms the requirements into actual scenarios, outlining the data required to execute them and distinguishing between manual and automated parts.

Environment setup is what enables the tests to be run in the first place, including staging environments and configuration, and whether to deal with dependencies as real, mocked, or sandboxed.

Execution and reporting: Where tests actually run, and where the results are recorded, including any defects, and sufficient evidence is attached for no one to guess what occurred, is called execution and reporting.

Closure: It’s easiest to skip the closure, and the most beneficial. What went wrong, what was missing, what needs to be changed for the next cycle.

On GigzzApp, a messaging app for teens and young adults looking for gig work nearby, such as lawn mowing, tutoring and coaching, we observed how it impacted the real world while building. 

It’s situations like age verification, location accuracy, and payment flows that surround informal work that only come to light when you deliberately walk through the STLC and would not have appeared in a quick smoke test. 

That’s the kind of foundation we lay for all custom web application development projects – we incorporate testing throughout the build process, not at the end.

How to write a test case that actually catches bugs

A hundred test cases is not enough to get real coverage. A few clear ones are needed that are tied to real risks. 

A helpful set of starter cases includes positive cases (proper input/expected success), negative cases (incorrect input/expected error message), boundary cases (the minimum and maximum), and regression cases (tests that will prevent a previous bug from sneaking back).

Here’s what it should sound like in an actual maintenance format:

Title: User cannot log in with an incorrect password 

Precondition: An account exists for test@example.com 

Steps: 

  1. Navigate to the login page.
  2. Enter the correct email address.
  3. Enter an incorrect password.
  4. Click “Sign In.”

Expected result: A clear error message is displayed when logging in. No sessions are created.

A good test case is clear and unambiguous, and it should be clear what went wrong and why, without having to meet and discuss it.

Writing a defect report that the developers do not return to you for more details

A vague bug report generates back-and-forth that is more time consuming than the bug itself. A good one has a title (what went wrong, where did it happen), minimal steps to reproduce it, what it should have done, what it did do, a screenshot or a snippet of a log file, the environment it occurred in, and a severity level that is not argued, but kept simple.

  • Title: Checkout fails silently when an item is added to cart and a promo code with a special character is entered.
  • Steps to reproduce: Add an item to cart, enter a promo code with a special character, click apply, proceed to checkout.
  • Expected: Error message or rejected code, with clear feedback. 
  • What actually happens: The page will refresh without any sound, the cart will empty, and an error message will not be displayed. 
  • Environment: Chrome 124, staging build. 
  • Severity: High, affects the conversion flow directly.

This is a team-wide habit. It also does more for the working relationship of QA and developers than anything else I have found in this list; taking the guesswork out of a 5-minute fix that becomes a half-day investigation.

Tools worth knowing, grouped by the decision they help you make 

A good testing stack isn’t a long list of tools. The smallest set that provides quick and accurate feedback.

Test management: Jira and Azure DevOps take care of the workflow and defect tracking for test management, whereas TestRail and Zephyr manage the test cases. 

Unit testing: JUnit or TestNG for Java, Jest or Vitest for JavaScript and TypeScript, pytest for Python. 

API and regression testing: At the service layer, where one typically encounters more stable and consistent service boundaries, Postman and Rest Assured serve most needs for API and regression testing, as opposed to UI-heavy flows. 

UI testing: Playwright, Cypress, and Selenium are still the preferred options and should be reserved for the few journeys that truly can’t be broken. 

Dependency control: With tools such as WireMock or Testcontainers, you can have a suite run without relying on live third party services, which can be the largest source of flakiness in CI. 

Performance testing: k6 and JMeter give you a baseline to catch regressions before real users do. 

It is really up to you what you want to work on – more speed, more stability, more coverage or more speed in the debugging of a failure. A more relevant question than “which tool is trending” is “which tool is successful”.

How to be sure whether your product or your dev partner is being properly tested

It’s the most frequent question we get asked most often by founders who aren’t developers themselves and need a way to check without reading code. A short list works better than a long one. 

  • Test cases are written before coding a feature, not after something breaks.
  • There is a staging environment that is very similar to production that you can trust.
  • Defects are recorded in some place, with a clear owner, not in someone’s head.
  • Regression tests are performed not only for major releases but also before the release.
  • A security pass occurs prior to the incident, not in response to it.

We adhere to this same list for projects such as Essel Tech, a hiring platform that connects with more than 1,000 vetted candidates accurately with 100+ companies on AEC and emergency response projects. 

The wrong match engine, even when it’s wrong from time to time, is a breach of the trust the platform relies on, so it’s not an option there either. 

That’s also why testing is not done after launch in our case. It’s not just during the sprint before it, but it continues to be tested in website maintenance services, with regression and security checks continuing after the initial release.

Testing is only half the experience: UX and security carry the rest 

A confusing app that is bug free, is still a failure for the user, and a well-designed app with data leakage is a failure for the user even more. Testing determines if a feature is successful or not. It doesn’t indicate if people want to use it or if their data is safe during use.

As an example, the UX is just as important as the functional part of platforms like maternity care, which is what we built and ended up being featured by Apple, IRTH. 

It’s not like Apple could afford to be lax on usability when it’s dealing with a sensitive topic like birth experience reviews for Black and brown moms. If you want the fundamentals we lean on for that side of the work, we’ve written them up in user experience basics. 

On the secure side, it’s the same discipline that is in place in MediOrbis and Dawn Health: functional testing can detect faulty features, while security testing can detect the ones that are hidden and quietly exposed. 

Either this is not used to its full potential until it is made to or it is left under-invested until something compels the use of it to its full potential. Our cloud security tips piece is about where that investment should be directed first.

A first testing checklist, if you’re starting from scratch

If you are a lean team or early stage founder and don’t know where to start, this is the order that seems to work.

  1. Choose one of the stronger flows: Signup, login, or checkout. Any breaking would be the most damaging.
  2. Create 8 – 12 test cases: Most of the actual risk is encompassed by a combination of positive, negative and boundary cases.
  3. First run it by hand: Manual runs bring surface requirements that don’t become “locked in” by automation.
  4. Automate what pays back weekly: Begin by writing some unit tests for business logic, integration tests for anything that interacts with a database or an API, and finally, one or two end-to-end tests for the flow that shouldn’t fail.
  5. Make it CI-ready: Before expanding test coverage any further, stabilize your test data and dependencies.

If this loop is successful every time, scaling test coverage is no longer something you scale but something you do.

How we add testing into every project 

Testing does not take place at the end of a build at Agicent. Part of the reason is that MVPs and full products get shipped without a 2 a.m. rollback. It becomes even more important when AI-assisted or AI-generated code becomes a larger component of the development of products.

 In Tide 360, where we built agentic AI employees to handle executive, financial and operational tasks, testing an AI system’s decisions is different from testing a UI flow, as the results of a test case can be truly different from one run to the next. It’s a real problem, it’s what we go through on every AI development services project.

There is one thing to learn from all this – the pursuit of perfection is not the point of testing. It’s all about gaining the confidence, proof and repeatable process so that a release no longer appears to be a risky business.

FAQS

Verification checks that the software was built correctly against its specification. Validation checks that the specification was actually the right thing to build in the first place. A feature can pass one and fail the other.

Coverage percentage alone isn't the answer. A better signal is whether your highest-risk flows, login, payments, or whatever a failure would hurt most, have regression tests that run automatically before every release.

Testing should start before the first build is ready for review, because waiting until development is finished makes it harder and more expensive to catch problems. Even during planning, the team can identify risky flows, define expected behavior, and decide what needs to be tested as the product takes shape.

Start with the parts of the app where a failure would have the biggest impact, such as login, payments, account creation, core workflows, or anything tied to customer data. Once those flows are covered, testing can move into less critical features and the edge cases around them.

Yes, because automated tests are good at checking known scenarios repeatedly, while manual testing can uncover behavior the team didn't anticipate. A strong testing process usually uses both, with automation handling repeatable regression checks and manual testing looking at new features, usability, and less predictable edge cases.

Ask how testing fits into their development process rather than simply asking whether they have a QA team. A team that takes testing seriously should be able to explain what gets tested, when it gets tested, which features are considered high risk, and how they make sure existing functionality still works before a release.



Sudeep Bhatnagar
Co-founder & Director of Business
Sudeep Bhatnagar

Talk to our experts who have been running successful Digital Product Development (Apps, Web Apps), Offshore Team Operations, and Hardcore Software Development Campaigns. During the discovery session, we'll explore the opportunities and Scope of the work and provide you an expert consulting on the right options to achieve the outcomes.

Be it a new App Development project, or creation of an offshore developers team, or digitalization of your existing market offerings - You'll get the best advise and service and pricing. We are excited to speak to you!

Book a Call

Let’s Create Big Stories Together!

Mobile is in our nerves. We don’t just build apps, we create brands.

Choosing us will be your best decision.

Relevant Blog Posts