Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jest - import multiple tests in a describe block, reusing variables defined in beforeEach()

I am familiar with RSpec where it is very easy to reuse test cases by writing shared examples

shared_example_for 'a cute pet' do 
  it 'tests that the pet is a small' { expect(pet.size).to be_lesser_than(10) }
  it 'tests that the pet can smile' { expect(pet.can_smile?).to be }
end

describe 'The Octocat' do
  let(:pet) { Octocat.new }

  it_behaves_like 'a cute pet'
end
...
describe 'The Doge' do 
  let(:pet) { Doge.new }

  it_behaves_like 'a cute pet'
end

Is there an equivalent in Jest ? Something that would let me reuse variables set in beforeEach() blocks ? I am trying to find a way using something like the following :

# __tests__/cuteness.js
export const cutenessTests = function() {
  test('it is small', () => {
    expect(petSetInBefore.length).toBeLesserThan(5)
  })
  test('it can smile', () => {
    expect(petSetInBefore.canSmile).toBe(true)
  })
}

# __tests__/famous_animals.test.js
import { cutenessTests } from './cuteness'

describe('Famous animals', () => {
  let petSetInBefore;

  describe('Octocat', () => {
    beforeEach(() => {
      petSetInBefore = new Octocat();
    })

    cutenessTests.bind(this)()
  })
})

The important here is that I am trying to share multiple test definitions and not just one, otherwise I could have passed the petSetInBefore to the shared function.

EDIT : each of my tests and nested describe are likely to alter my test environment and objects, so the beforeEach is used to restore a proper test environment. Here is a better example

class Octocat {
  get strokeFor(time) {
    this.strokeTime = this.strokeTime + time
    if (this.strokeTime <= 10) {
      this.mood = 'happy'
    } else {
      this.mood = 'bored'
    }
  }
}

class Doge {
  get strokeFor(time) {
    this.strokeTime = this.strokeTime + time
    if (this.strokeTime <= 5) {
      this.mood = 'happy'
    } else {
      this.mood = 'bored'
    }
  }
}

const cutenessTests = function() {
  describe('when stroked for a short while', () => {
    beforeEach(() => {
      petSetInBefore.strokeFor(1);
    })

    test('it is happy', () => { expect(petSetInBefore.mood).to(eq('happy')) }

    describe('when stroked too much', () => {
      beforeEach(() => {
        petSetInBefore.stroke(1000);
      })

      test('it gets bored', () => { expect(petSetInBefore.mood).to(eq('bored')) }
    })

    describe('when stroked a little longer', () => {
      beforeEach(() => {
        petSetInBefore.strokeFor(4);
      })

      test('it is still happy', () => { expect(petSetInBefore.mood).to(eq('happy')) }
    })
  })
}

EDIT2: Here is a repl.it based on Gui3's answer

EDIT3 : the object can be altered before or during the reusable tests

describe('Famous animals', () => {
  let petSetInBefore;

  describe('Octocat', () => {
    beforeEach(() => {
      petSetInBefore = new Octocat();
    })

    describe('when it is not well rested', () => { 
      beforeEach(() => { petSetInBefore.wellRested() } // Extra object preparation / context before calling reusable examples
      cutenessTests()
    }),
    describe('when it is not well rested', () => { 
      // Calling reusable examples without extra context
      cutenessTests()
    })
  })
})
like image 890
Cyril Duchon-Doris Avatar asked Aug 20 '18 13:08

Cyril Duchon-Doris


People also ask

What is Jest beforeEach?

beforeEach(fn, timeout) ​Runs a function before each of the tests in this file runs. If the function returns a promise or is a generator, Jest waits for that promise to resolve before running the test. Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before aborting.

Does beforeEach run before describe?

If beforeEach is inside a describe block, it runs for each test in the describe block. If you only need to run some setup code once, before any tests run, use beforeAll instead.

What is beforeAll and afterAll in Jest?

Jest provides beforeAll and afterAll . As with test / it it will wait for a promise to resolve, if the function returns a promise. beforeAll(() => { return new Promise(resolve => { // Asynchronous task // ...

What is describe and test in Jest?

describe breaks your test suite into components. Depending on your test strategy, you might have a describe for each function in your class, each module of your plugin, or each user-facing piece of functionality. You can also nest describes to further subdivide the suite. it is where you perform individual tests.


1 Answers

If you still want beforeEach,

for reasons ... it works if you declare your variable in the global scope

let petSetInBefore; // here it works
describe('Famous animals', () => {
  //let petSetInBefore; // here it's undefined

  describe('Octocat', ()  => {
    //let petSetInBefore; // undefined too

    beforeAll(() => {
      petSetInBefore = new Octocat();
    })

    cutenessTests() // .bind(this) results the same
  });

  describe('Doge', () => {
    beforeEach(() => {
      petSetInBefore = new Doge();
    })

    cutenessTests.bind(this)()
  });
})

https://repl.it/@gui3/jestSharedTests

seems like the tests inside the shared function cannot share variables from beforeEach otherwise ...

like image 176
gui3 Avatar answered Oct 13 '22 13:10

gui3