Enhancing Test Clarity and Diagnostics in Abstractions
Tags: testing, javascript, productivity, tutorial
Published: 2023-08-15 13:31:00

At Woovi, we strongly believe in maintaining high testing standards as an important part of our development approach. We make sure to include tests along with our features, and by using different simplifications, we ensure that we can quickly and safely put these tests together.

For both our front-end and back-end systems, we mainly use Jest as our primary testing tool. This flexible tool helps us thoroughly check how well our software parts work. Furthermore, we've created specific functions for each environment that make our testing process smoother.

### The Importance of Error Stack Traces
The error stack trace is a crucial tool for diagnosing issues in our development work. It gives us valuable information about why failures happen and helps us find ways to fix them. For example:

```tsx
it("should sum two numbers", async () => {
  expect(1 + 2).toBe(4);
});
```

In this basic example, we intentionally make a mistake. The error message that comes up won't just show that something went wrong, but it will also show exactly where the mistake happened. This makes it easier to figure out and fix the problem.

![sum-test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wtt8z2larsgyvlhy17o.png)

### Introducing Abstractions

To demonstrate the utility of abstractions in testing, consider the following code segment:

```tsx
const assertSum = (numbers: number[], result: number) => {
  const [x, y] = numbers;
  expect(x + y).toBe(result);
};

it("should sum two numbers", async () => {
  assertSum([1, 2], 4);
});
```

In this case, we've put the test steps inside the `assertSum` function, which makes it easier to reuse the code. If something goes wrong, the error message it gives will be like this:

![sum-test-2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hh48kymhw7ezle105a36.png)

At first glance, it looks like a useful message, it's easy to find which test broke since we only have one.

### Navigating Complexity

In real-world scenarios, our codebases encompass multiple tests and numerous operations. For example:

```tsx
const assertSum = (numbers: number[], result: number) => {
  const [x, y] = numbers;
  expect(x + y).toBe(result);
};

it("should sum two numbers", async () => {
  assertSum([1, 2], 3);
  assertSum([1, 6], 7);
  assertSum([9, 2], 4);
  assertSum([4, 6], 12);
});

it("should sum two numbers with two decimal places", async () => {
  assertSum([12, 21], 89);
  assertSum([16, 65], 80);
  assertSum([91, 20], 111);
  assertSum([40, 62], 102);
});
```

![sum-test-3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/49iu7ix09mqol9ejmcgg.png)

In these complex situations, doesn't this message tell us exactly where it broke, at which `assertSum` did it break? in which line? in a scenario closer to reality, you use your abstractions in several of your tests, and failing with that message only makes the situation worse, you don't know for sure which line failed, which of the operations failed.

### Enhancing Error Diagnostics

To make our abstractions even better at diagnosing issues, we use a try-catch block:

```tsx
const assertSum = (numbers: number[], result: number) => {
  const [x, y] = numbers;

  try {
    expect(x + y).toBe(result);
  } catch (error) {
    Error.captureStackTrace(error as Error, assertSum);
    throw error;
  }
};
```

This is the output:

![sum-test-4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mat6xzckk71ga55pkv0e.png)

This improvement makes the error stack trace more detailed, giving a complete picture of where the error happened, what was expected, and what actually occurred. This leads to a thorough diagnostic output that helps developers quickly solve the problem.

### Why to abstract the `expect`

You can have an abstraction with expect in cases where you will always check the same data.

A more realistic example would be where you mock a mutation operation and ensure that the name of the next operation to be executed is the name you expect.

```tsx
export const assertOperation = <T extends OperationType>(
  environment: RelayMockEnvironment,
  name: string,
) => {
  const queryOperation = environment.mock.getMostRecentOperation();

  const operationName = queryOperation.root.node.name;

  try {
    expect(operationName).toBe(name);
  } catch (error) {
    Error.captureStackTrace(error as Error, assertOperation);

    throw error;
  }
};
```

---

In Conclusion

At Woovi, our approach to testing focuses on being thorough, using abstractions, and being precise in diagnosing issues. With the help of Jest and carefully designed abstractions, we enable our development teams to create strong and top-notch software that not only meets but consistently goes beyond 
expectations.

---

[Woovi](https://woovi.com/) is a Startup that enables shoppers to pay as they please. To make this possible, Woovi provides instant payment solutions for merchants to accept orders.

---

Photo by <a href="https://unsplash.com/@dtravisphd?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">David Travis</a> on <a href="https://unsplash.com/photos/aVvZJC0ynBQ?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>