A DSL for testing Income Tax Calculations

Ankit Solanki
Nov 3, 2016 · 2 min read

At ClearTax, being accurate is one of our most crucial requirements. We have to ensure that each and every tax return prepared by ClearTax is correct. As a result, we put a lot of emphasis on testing our tax-related code.

Testing income tax calculations is challenging:

  1. Test cases may be very detailed and complex. This is challenging because it makes adding test cases harder — introduces friction.
  2. You cannot test against your own calculations. Your assertions need to be independently verified — either by doing the calculations by hand, or against the tax department’s official implementation.
  3. You might need domain knowledge to figure out what the actual edge conditions are.

This problem was not easy to solve, but by the last tax season, we ended up with an approach that’s proven pretty successful.

Building a DSL

We ended up building an internal DSL for writing test cases.


.OnDate(2016, 10, 20)

.ShouldHave(I.IncomeTax, 65000);

This DSL ended up with three main types of statements:

  • With statements, which add data to a tax return
  • Should statements, which are assertions
  • Configuration statements: setting calculation date, etc (income tax calculations are time-dependent)

Adding a very complex test case is actually pretty easy now:

.OnDate(2016, 6, 1) .ShouldHaveDeduction(D.Sec80TTA, 10000)
.ShouldHave(I.GrossTax, 19570)
.ShouldHave(I.Refund, 10430);

The tooling would give auto-complete and make it easy for us to add any types of test cases. This has drastically simplified the process of adding new test cases.

Programmatic Tests, Code Generation

We went further: the test framework would also keep track of which commands it has received. This means that you can replay actions taken, or serialise a test execution as code.

> "test.WithSalary(600000).WithInterestIncome(100000).OnDate(2016, 10, 20).ShouldHave(I.IncomeTax, 65000);"

Finally, we wrote a wrapper on top of this test framework to translate the test case into a format that the government’s tools understand, and auto-generate assertions.

> "test.ShouldHave(I.IncomeTax, 65000).ShouldHave(I.EducationCess, 1950);"

As a result:

  • We can programatically generate test cases (using reflection or any other mechanism)
  • Automatically generate assertions against a neutral implementation
  • Convert these test cases into code, and add the concrete test cases to our test suite

The implications are huge. We can do exhaustive testing — just write code to generate all types of edge conditions instead of thinking about it yourself!

Using this approach, we have been able to make ClearTax much more robust. We are strong believers in building internal tools for our own use, building leverage for ourselves.

This DSL hides a lot of complexity within, but the end user only has to deal with a simple and logical interface. You don’t have to worry about the internal implementation, you can define a test case at a higher level. This helps us move faster, while ensuring we are always accurate :)

Interested in working with us? We’re hiring! Drop me a note on ankit@cleartax.in.

ClearTax Engineering

Simplifying financial lives of Indians. https://cleartax.in

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store