I use JUnit unit tests to confirm each function behaves as expected, along with integration tests to check modules working together.
For functional testing, I focus on inputs, outputs, and boundary conditions.
For non-functional testing, I add checks for performance (response times), security (input validation and safe handling of data), and reliability (how the program responds to errors).
Running tests automatically in a CI pipeline and reviewing results regularly ensures the software stays both functional and secure.
I start with user stories and acceptance criteria that describe exactly what the user expects.
Each story is turned into one or more JUnit tests so the program can only be marked complete if the tests pass.
Using examples with real input and output makes requirements concrete.
I also gather feedback on small iterations, adjust tests, and refine the code until user needs are fully met.
I design software with testability in mind.
That means writing small, modular functions, using clear interfaces, and applying dependency injection so mocks and stubs can be used in tests.
I define contracts early (method signatures, data models) and write tests against them.
I also plan for non-functional qualities by considering performance, maintainability, and security from the start.
My workflow is test-driven: write a failing JUnit test, implement the code to pass it, refactor, and repeat until the system is complete.