Search code examples
typescriptunit-testingts-jest

Jest: How to avoid boilerplate in beforeEach / afterEach


I have multiple service tests that have almost equal beforeEach() and afterAll(). What can be done to eliminate the boilerplate?

It looks simplified like this:

describe('Test',()=>{
  let serverTestbed:ServerTestbed;
  let myService:MyService;
  beforeEach(()=>{
    serverTestbed= new ServerTestbed()
    serverTestbed.run()

    myService= new MyService(serverTestbed.host,serverTestbed.port) 
    //^^ this is the only line that differs from test to test
  })

  afterEach(async ()=>{
    await serverTestbed.close()

  })
})

Is it possible to do something like this, where describeServerTest(..) contains the duplicate actions:

describeServerTest("test my service",(serverTestbed)=>{
  let myService:MyService
  //more varibales here

  beforeEach(()=>{
   myService= new MyService(serverTestbed.host,serverTestbed.port) 
   //other init stuff
  })

  it('test',()=>{
    myService.foo()
    expect(..)
  })
})

Solution

  • I assume you want to import the describeServerTest function in each test file rather than a global setup using setupFilesAfterEnv config.

    From the documentation Defining Tests , we know:

    Tests must be defined synchronously for Jest to be able to collect your tests.

    E.g.

    test-utils.ts:

    const serverTestbed = {
      host: '127.0.0.1',
      port: 5432,
    };
    type EmptyFunction = () => void;
    interface FunctionLike {
      readonly name: string;
    }
    export const describeServerTest = (name: number | string | Function | FunctionLike, fn: EmptyFunction): void => {
      describe(name, () => {
        beforeEach(() => {
          console.log('server testbed run');
        });
    
        afterEach(async () => {
          console.log('server testbed close');
        });
    
        fn();
      });
    };
    

    a.test.ts:

    import { describeServerTest } from './test-utils';
    
    describeServerTest('test a service', () => {
      it('should pass', () => {
        console.log('a service test case run');
        expect(1 + 1).toEqual(2);
      });
    });
    

    b.test.ts:

    import { describeServerTest } from './test-utils';
    
    describeServerTest('test b service', () => {
      it('should pass', () => {
        console.log('b service test case run');
        expect(1 + 1).toEqual(2);
      });
    });
    

    Test result:

     PASS  stackoverflow/75135319/b.test.ts (8.032 s)
      ● Console
    
        console.log
          server testbed run
    
          at Object.<anonymous> (stackoverflow/75135319/test-utils.ts:12:15)
    
        console.log
          b service test case run
    
          at Object.<anonymous> (stackoverflow/75135319/b.test.ts:5:13)
    
        console.log
          server testbed close
    
          at stackoverflow/75135319/test-utils.ts:16:15
    
     PASS  stackoverflow/75135319/a.test.ts (8.048 s)
      ● Console
    
        console.log
          server testbed run
    
          at Object.<anonymous> (stackoverflow/75135319/test-utils.ts:12:15)
    
        console.log
          a service test case run
    
          at Object.<anonymous> (stackoverflow/75135319/a.test.ts:5:13)
    
        console.log
          server testbed close
    
          at stackoverflow/75135319/test-utils.ts:16:15
    
    
    Test Suites: 2 passed, 2 total
    Tests:       2 passed, 2 total
    Snapshots:   0 total
    Time:        8.997 s, estimated 10 s
    Ran all test suites related to changed files.