For many entry-level developers, the most exciting part of work is writing a shiny new feature, while the rest of the things sound boring. Although writing code seems like more than 90% of the work to them, it is not true. Typically, engineers are involved in:
Requirement feasibility - Along with product, design, and business team(s).
Estimation - Giving ballpark/detailed estimates based on approximate/exact requirements.
Design - Building the technical design (LLD/HLD), finding out cross-team dependencies, etc.
Code - The implementation (most exciting one).
Dev testing - Devs should test their solution using manual and automated tests.
QA Testing - The QA team should also test the application using tools like Postman and end-to-end testing on the front end (if available).
Documentation - Making or updating technical documentation.
Communication - Supporting other engineers for integration, attending scrum calls, updating everyone on the status of things and blockers (if any).
Post-release support - Error monitoring, performance monitoring, support for bug-fixing, etc.
Seeing the above can seem overwhelming, but this is what happens in real-world projects. Also, it can now be said that implementation is probably not even half of the total time or effort. You might be thinking - what’s here to talk specifically about test cases? Testing is one of the most under-rated bits of SDLC. Although it is very obvious to test your product before delivering it to the end user, and testing happens everywhere, the emphasis of this post is on the importance of testing in the overall framework.
Just to explain - testing is not when your developers give the product to your QA team and they start testing. Testing should be hooked in your framework at various levels:
When a story is being discussed between the product and tech team(s), the QA team should also be part of the discussion and they should understand the product to write test scenarios.
Everyone should go through the test cases to make sure nothing is missed.
The product team should make sure that the product and design have handled all the cases in the product as mentioned by the QA team.
Developers should make sure that their design covers all of these scenarios.
Once development is complete, developers should manually test their code through some interface to make sure all these cases are covered. Later, they should add automated test cases for all the cases.
From my experience, the above is a rock-solid way to ensure that:
Everyone is on the same page in understanding the product and possible scenarios.
Feedback from everyone is incorporated as early as possible.
Testers start documenting the test cases even before developers write code, avoiding the need for design change in the later phase(s).
The cost of change is minimal and delivery is fast (due to 2 and 3).
It is easy to see which layer missed the test case(s) and caused a specific bug, which otherwise can become a blame game within the team.
Above all, team motivation is maintained.
Although the above section talks about the impact of a good testing framework for the entire lifecycle, I’d also like to talk about how a good test suite helps developer teams:
Language and Framework version upgrades - I’ve got to work on Ruby and Rails upgrades multiple times in my career. One of the primary requirements for such upgrades is to have good test coverage. If you don’t have one, you’ll need to get your app deeply tested. Although you’ll do that otherwise, having a good test suite adds to the confidence and helps to show a lot of issues early.
Database version upgrades - The same thing applies here also. Although for upgrading database versions, people generally dump a production snapshot to a new instance (to see if data and extensions are compatible with the next version), a test suite would add to the confidence here also.
Feature enhancements - Often the first version of the application is released as a small product to gather customer feedback. While building the second version, there can be both incremental changes and some changes in the existing product. At this point, we need to ensure that existing functionality doesn’t break, and automated test cases help to ensure the same.
Refactoring - Refactoring is an inevitable part of the maintenance phase. You’ll find the need to refactor a lot of things that might be written inefficiently due to deadlines or other reasons. Without test cases, refactoring can easily mess up the code and introduce bugs.
Enforcing Better low-level design - It’s very common for developers to write long methods and classes, spanning hundreds and thousands of lines. Such classes contain a model layer, service layer, external API calls, and many other things coupled together. When you write unit tests, you often mock some part of the code to test the other independently, but when it is hard to write tests of a specific class or method, there are high chances that your structure is not good. To write tests efficiently, you’ll often end up splitting the class (this realization comes with some practice). Follow the rule - If it is hard to write tests, better refactor the code for which you’re writing tests.
Testing is a serious business. Think of any product you use - if you get a good user experience, there are high chances that it is an outcome of a good process and a lot of testing. Everyone wants their product to get loved by their users, but few are willing to invest substantial effort in the testing and in improving the overall delivery process. To build a better product, hook the testing in your overall development cycle, not just in the end.
Take care and see you guys in the next post.