Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jest equivalent to RSpec lazy evaluated variables (let)?

In rspec you can do something like this:

let(:input) { 'foo' }
before_each do
   setup_some_thing(input)
end

context 'when input is bar do
  let(:input) { 'bar' }
  it 'does something different' do
  end
end

context 'when input is baz do
  let(:input) { 'baz' }
  it 'does something else different' do
  end
end

This allows you to define a method call or instantiation of a large object as a sum of its smaller parts. You can then override those individual small parts inside different contexts. The idea being that you create a happy path before each test, and then specify deviations from the happy path within your context blocks.

Unfortunately, I can't seem to do this with Jest. I've tried the following:

beforeEach(() => {
  let input = 'foo';
  beforeEach(() => {
    setupSomeThing(input);
  });

  describe('when input is bar', () => {
    input = 'bar';
    it('does something different', () => {

    });
  });

  describe('when input is baz', () => {
    input = 'baz';
    it('does something different', () => {

    });
  });
});

Because jest executes every describe block before running any specific describe block, input is always 'baz'. Does anyone know a work around, or a way to get the rspec behavior?

Thanks in advance!

Update

You can get similar behavior (albeit without lazy evaluation) using beforeAll.

beforeEach(() => {
  let input = 'foo';
  beforeEach(() => {
    setupSomeThing(input);
  });

  describe('when input is bar', () => {
    beforeAll(() => {
     input = 'bar';
    });

    it('does something different', () => {

    });
  });

  describe('when input is baz', () => {
    beforeAll(() => {
     input = 'baz';
    });        

    it('does something different', () => {

    });
  });
});
like image 801
Noah Avatar asked May 23 '17 17:05

Noah


2 Answers

The best solutions I've found have been libraries like

https://github.com/stalniy/bdd-lazy-var

and

https://github.com/tatyshev/given2

If you don't want to introduce a dependency, you can get similar behavior (albeit without lazy evaluation) by doing something like this:

beforeEach(() => {
  let input = 'foo';
  beforeEach(() => {
    setupSomeThing(input);
  });

  describe('when input is bar', () => {
    beforeAll(() => {
     input = 'bar';
    });

    it('does something different', () => {

    });
  });

  describe('when input is baz', () => {
    beforeAll(() => {
     input = 'baz';
    });        

    it('does something different', () => {

    });
  });
});
like image 143
Noah Avatar answered Nov 16 '22 14:11

Noah


Use a beforeAll to set variables in a parent beforeEach.

Building on @Noah's own answer, I thought I would share our final solution with using this as well.

describe( "Logging in", () => { 
  beforeEach( () => login( this.password ) ); // Steps are same for all so re-use but use different password.

  describe( "when password is incorrect", () => {
    beforeAll( () => this.password = "INCORRECT PASSWORD" );

    it( "doesn't log in.", () => {
      // Your assertion(s).
    } );
  } );

  describe( "when password is correct", () => {
    beforeAll( () => this.password = "CORRECT PASSWORD" );

    it( "logs in successfully.", () => {
      // Your assertion(s).
    } );
  } );
} );
like image 24
Joshua Pinter Avatar answered Nov 16 '22 15:11

Joshua Pinter