DEV Community

Sebastian Wessel for PURISTA

Posted on

PURISTA - Tests with Jest, Sinon.js and Testcontainers

In the journey of crafting the PURISTA TypeScript backend framework, the need for automated software testing became increasingly apparent.
PURISTA's extensive amount of adapters and integrations with third-party solutions makes it impractical to conduct manual testing repeatedly.
In this article, we dive into the pivotal role of automated testing in ensuring the reliability and stability of PURISTA's offerings.

Moreover, enabling developers to effortlessly test their code built on top of PURISTA stands as a pivotal aspect of the framework.

Jest

Jest offers a comprehensive test suite where you can efficiently organize, implement, and execute your tests. With approximately 1490 contributors, Jest has played a pivotal role in simplifying the process of writing tests, as demonstrated below:

import { asyncOperation, syncOperation } mytestfile.test.js

describe('My testfile works', ()=>{
  it('is exected to be ok', () => {
    const result = syncOperation()
    expect(result).toBe('OK')
  })

  it('works with async await', async ()=>{
    const result = asyncOperation()
    await expect(result).resolves.toBe('OK')
  })
})
Enter fullscreen mode Exit fullscreen mode

As the number on GitHub shows: This awesome tool is used by 10.4 millions!
This unbelievable number shows, how important it is.

A massive shout-out to Jest and all its dedicated contributors! Consider starring it on GitHub, giving a shout-out on social media, or mentioning it in your readme file to show your appreciation. 🌟👏

We use Jest together with @swc/jest, which speeds up our test runs dramatically.
They don't lie with "Super-fast alternative for babel-jest or ts-jest without type checking".
It's the second great tool by Vercel we use at PURISTA.

Sinon.js

When writing tests, you probably will come to the point where you need to mock, fake or spy on something. Even if the test framework is providing some helpers for it, you should have a look at Sinon.js.

At PURISTA, we primarily leverage Sinon.js to deliver a seamless testing experience for developers.
We provide for example the getLoggerMock function, which returns an easy way to test logging, without poluting the test runner output.

This function looks like this:

export const getLoggerMock = (sandbox?: SinonSandbox) => {
  const info = sandbox?.stub() || stub()
  const error = sandbox?.stub() || stub()
  const warn = sandbox?.stub() || stub()
  const debug = sandbox?.stub() || stub()
  const trace = sandbox?.stub() || stub()
  const fatal = sandbox?.stub() || stub()

  const mock: Logger = {
    info,
    error,
    warn,
    debug,
    trace,
    fatal,
    getChildLogger: () => mock,
  }

  return {
    stubs: {
      info,
      error,
      warn,
      debug,
      trace,
      fatal,
    },
    mock,
  }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we've created stubs, which are a form of mock implementations. You can use them to check whether a stub is called, how many times it's called, if it's called with specific parameters, and more. Additionally, Sinon.js offers sandboxing, a highly recommended feature. It prevents your tests from interfering with each other, ensuring clean and reliable results.

As an example on how it might look:

import { Logger } from '@purista/core'
import { createSandbox } from 'sinon'

function syncOperation(logger: Logger) {
   logger.info('info log')
   return 'OK'
}

describe('My testfile works', ()=>{
  const sandbox = createSandbox()

  afterEach(() => {
    sandbox.reset()
  })

  it('is exected to be ok', () => {
    const loggerMock = getLoggerMock()

    const result = syncOperation(loggerMock.mock)
    expect(result).toBe('OK')
    expect(loggerMock.stub.info.calledOnceWith('info log')).toBeTruthy()
  })

})
Enter fullscreen mode Exit fullscreen mode

Sinon.js is unbelievably helpful, and the 328 contributors are doing an incredible job. It's no surprise that Sinon.js is trusted by over 574k developers. Show them some love with a big shout-out and a star on GitHub!

If you haven't used Sinon.js before, it's definitely worth giving it a try.

Testcontainers

To be honest, Testcontainers wasn't on our radar initially.
However, we soon realized the need for a viable solution to conduct integration tests for:

Testcontainers was the gamechanger here.
It is super easy to spin up a docker container and run tests against a real running system.

To show you, how simple it is:

Here is the example, on how start a full RabbitMQ before we run our tests:

import { GenericContainer, StartedTestContainer } from 'testcontainers'

const AMQP_PORT = 5672

describe('@purista/amqpbridge', () => {
  let container: StartedTestContainer

  beforeAll(async () => {
    container = await new GenericContainer('rabbitmq:alpine')
      .withExposedPorts({ host: AMQP_PORT, container: AMQP_PORT })
      .start()
  })

  afterAll(async () => {
    await container.stop()
  })
Enter fullscreen mode Exit fullscreen mode

It significantly enhanced our local testing process. Furthermore, Testcontainers is a versatile tool available for multiple programming languages.

A heartfelt thank you to all the contributors of Testcontainers for this incredible project! We deeply appreciate the outstanding work you've put into it.

If you're searching for content ideas for the next article, consider crafting a cool piece about Testcontainers and its practical applications. Dive into how to harness its power effectively.


Now that we've explored the powerful tooling required to build PURISTA itself, let's shift our focus to the components under the hood that make PURISTA tick.

Be sure to check out the upcoming article in this series!

Top comments (0)