The FIRST RIGHT-BICEP Strategy for Unit Testing Excellence

Share on your favorite social sites

Don’t worry, I’m not going to ask you to hit the gym and build your biceps. Instead, I want you to apply the principles of Unit Testing covered in this blog in your everyday work. This approach can help you develop a mindset that prevents you from introducing bugs to the QA team or into production.

Unit testing (and testing in general) can be complex, with countless scenarios to consider. Identifying these scenarios and ensuring an application or feature doesn’t fail in production can be challenging.

In this blog, I want to share a framework or thought process that I’ve been using for 18 years. It’s been invaluable for validating requirements and considering all possible scenarios for a solution, feature, or code. I now apply this same framework when designing and validating solutions to identify any missing pieces or gaps, ensuring the best possible outcome.

Before we dive in, keep in mind that I first encountered this framework during my first year on the job as a unit test developer for a C program for telecommunications base stations. You can find the original concept in the book Pragmatic Unit Testing by Jeff Langr, with Andy Hunt & Dave Thomas. In this blog, I’ll provide an overview of the key topics covered in detail in the book.

Writing Effective Unit Testing?

Writing unit tests is often mistakenly equated with learning frameworks like JEST, JUnit, and NUnit, but the critical thought process behind identifying which unit test cases to write is frequently overlooked. Mastering the art of writing efficient unit tests offers the following benefits:

  • Clean System design with modularization
  • Minimal effort to maintain unit test cases
  • Maximum coverage and reduced TAT for bug fixes

Now let’s briefly understand the concept of FIRST, RIGHT-BICEP.

FIRST Principle for Unit Tests

Every unit test you create and implement should adhere to the FIRST principles, regardless of the scenarios you plan to address.

Fast

The unit tests you create should be fast and focused on the specific feature being tested. Unit tests are meant to accelerate your development process, not hinder it.

Isolated

Each unit test should be independently executable, meaning each one should include its own setup and teardown logic to ensure the application returns to its original state before and after execution, regardless of the order in which the tests are run.

Repeatable

Every time you run a unit test, the results should be consistent and reproducible for every deployment or release.

Self-Validating

Each unit test should validate the expected output for the given scenarios. A unit test case may include one or more assertions to determine whether the test passes or fails.

Timely

Establish a process and allocate dedicated time for developers to write unit tests as part of the development cycle. This can happen once a feature is ready, during development, or even as part of a test-driven development approach, depending on the current state of the application or product.

RIGHT-BICEP

With above characteristics of Unit Testing taken care, next step would be to identify the scenarios, the RIGHT-BICEP comes in here.

RIGHT

The first category of unit tests you should write tests your code with correct inputs to verify that you receive the expected output. In other words, you need to test the happy path first and ensure these tests pass.

Boundary

You should identify scenarios that involve boundary conditions of the inputs to your code, method, or properties. These boundaries include null values, maximum and minimum values, poorly formatted data, and other specific boundaries relevant to the code under test.

Inverse Relationship

Verify the expected results by testing the inverse logic. For example, you can validate the output of a division operation by using multiplication or check addition through subtraction. In my experience, these scenarios are often the most complex and challenging to identify for the code being tested.

Cross Check

Can you verify the output using an alternative approach to achieve the same results? For instance, if you are writing logic to filter a list of active orders based on status, you can write an alternate query to the database to obtain the same list, allowing you to validate both lists against each other.

Error Conditions

How will your code handle errors from downstream and upstream applications? Testing error conditions is a critical category that ensures your application or code manages issues gracefully and prevents crashes.

Performance

Testing the performance of your code to ensure optimization is another crucial category to address, especially for cloud solutions with distributed architectures.

Testing Boundaries with CORRECT Way

When testing boundary conditions for the features or the code, another question is to ensure all boundary conditions are met and covered. The CORRECT method ensure you cover the same.

Conformance

This is to ensure the code validates for all formats of expected input data and then assert (PASS/FAIL) the test accordingly if the format is not complaint.

Ordering

When it comes to boundary conditions, you need to validate if the inputs re being passed with right order, does order make a difference to the output , these scenarios to be validated.

Range

These scenarios you will ensure the max, min value of input and how the code/application handles the same.

Reference

Does your code refer to data from upstream or downstream services? your code should validate scenarios on how it would behave due to issues with data that is being referenced from external sources

Existence & Cardinality

Your code should work seamlessly when the data is not existing, values could not null, the order Id received may not have relevant data in database, any other scenarios related to data availability need to be validated and ensure covered as part of scenarios.

Time

Finally, the time relevancy for the data, absolute and relative, validate the scenarios of concurrency, statefulness and stateless , other factors where your code logic depends on the time needs to be covered.

Conclusion

It might look like too many things to remember and apply, but the results you get with this strategy is something that will reduce the rework, improve design and code quality, and eventually allow the business to release features and product at more consistent and stable way.

Any queries or need mentoring, feel free to message me

Leave a Reply

Your email address will not be published. Required fields are marked *