본문 바로가기

study

[TEST CODE with mocha, should, sinon] stub mock spy before beforeEach after afterEach

반응형

mocha

- 테스트 주도 개발을 할 수 있는 대표적인 JavaScript 테스트 프레임워크 

should.js

- mocha에서 사용할 수 있는 assertion모듈

- node.js에서 사용할 수 있는 표현적인, 가독성 높은 단언문(assertion) 라이브러리 

- 테스트 프레임워크에 의존적이지 않음 

sinon 

- standalone test spies, stubs and mocks for js

- works with any unit testing framework

 

 


 

 

 

Test doubles ⊃ stubs mocks spies 

 

Test doubles ?

-> We usually don't want anything external (such as database calls) to influence the outcome of tests.

Test doubles can replace troublesome parts of the code and make tests easier to write.

 

 

 

Spies

Record information about function calls, including:

- number of time called , 불려지는 횟수

- arguments, 인자값 

- return value, 값 반환

- this value, 이 값 

- exceptions thrown (if any), 예외 던지기

 

Spies are stubs that also record some information based on how they were called. One form of this might be an email service that records how many messages it was sent.

 

There are two types of spies:

1. Anonymous functions

2. Ones that wrap methods in our code 

 

 

 

 

 

Stubs

Come with all the functionality of spies, but they replace the target function.

They have methods that can add custom behaviors including:

- returning a specific value

- throwing a specified exception

- automatically invoking callbacks with provided argumetns

- defining behavior on nth call to stub 

 

Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. 

 

Some common use cases include: 

- Replacing AJAX calls to prevent database calls

- Triggering different code paths depending on function output

- Testing output when a particular exceptions is thrown 

 

 

 

 

 

Mocks 

Similar to stubs, but can be used to replace whole objects and alter their behavior. 

 

Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.

 

They are mostly used when you need to stub out more than one function from a single object. If you only need to replace a single function, a stub is easier to use.

 

Beware!!! 

Mocks make it easy to make your tests overly specific and 'brittle'. 

 

 

정리 

spy 
1. function call 및 정보를 기록한다. 
* 정보 : 불린 횟수, 인자값, 리턴값, 이값(this value), 예외던지기 등  


stub
1. spy의 기능을 지니고 있지만 특정 타겟 함수를 대체할 수 있다. 
2. 테스트동안 canned answers만을 제공한다. ( 테스트 외부 소스와 연결되지 않는다. )
ex. some method 가 제 3자 API에 의존하고 있다면 테스트할때마다 요청보내는건 좋지 않으므로 이런 경우 stub이 유용하다. 


mocks 
1. stub과 비슷하지만 하나의 전체 객체들을 대체할 때 사용된다.
2. 하나의 객체로부터 두 개 이상의 함수를 stub out할 때 mocks이 주로 사용된다.
( 함수 한 개일 때는 stub이 낫다.) 

 

 

 


 

 

 

Mocha ( for Node.js ) 

provides 4 hooks 

 

before

- is run once before all the tests in a describe 

- is meant to set the stage for a group of tests.

- If you need to setup a mock object or data structure and this object or structure can be reused by all the tests in a single describe, you can use before to set it up and after to tear it down. 

beforeEach

- is run before each test in a describe

- is for each individual tests

- beforeEach, afterEach -> These ensure test isolation : each test starts from a known state and does not depend on the presence or absence of a previous test to succeed. 

after

- is run once after all the tests in a describe

afterEach 

- is run after each test in a describe

 

 

before, after는 Suite단위 (BDD 경우 describe로 작성하는 단위) 로 실행된다. 각 Suite를 실행하기 전 before를 실행하고 Suite 실행이 끝나면 after가 실행된다. 

beforeEach와 afterEach는 각 테스트 단위로 (BDD 경우 it으로 작성하는) 실행된다. 

 

흐름 이해하기

( 출처 :  stackoverflow: https://stackoverflow.com/questions/21418580/what-is-the-difference-between-before-and-beforeeach )

describe("top", function () {
    before(function () {
        console.log("top before");
    });
    after(function () {
        console.log("top after");
    });
    beforeEach(function () {
        console.log("top beforeEach");
    });
    afterEach(function () {
        console.log("top afterEach");
    });
    it("test1", function () {
        console.log("top test1");
    });
    describe("sublevel", function() {
        before(function () {
            console.log("sublevel before");
        });
        after(function () {
            console.log("sublevel after");
        });
        beforeEach(function () {
            console.log("sublevel beforeEach");
        });
        afterEach(function () {
            console.log("sublevel afterEach");
        });
        it("test1", function () {
            console.log("sublevel test1");
        });
        it("test2", function () {
            console.log("sublevel test2");
        });
    });
    it("test2", function () {
        console.log("top test2");
    });
});

위의 테스트 코드를 실행 시 아래와 같이 실행된다.

top before

top beforeEach
top test1
top afterEach

top beforeEach
top test2
top afterEach 

sublevel before

top beforeEach
sublevel beforeEach
sublevel test1
sublevel afterEach
top afterEach

top beforeEach
sublevel beforeEach
sublevel test2
sublevel afterEach
top afterEach

sublevel after
top after 

When Mocha executes a test, all the before and the beforeEach hooks that were set in the describe that contains it, and all the ancestors of that describe apply to the test. sublevel before executes before top beforeEach because it is a before hook. And with after and afterEach, the same logic applies but the order is reversed. 

 

 

 


 

 

Junit ( for JAVA ) 

BeforeAll  ( =  BeforeClass )

- is called once and only once.

- is best used for initializing code.

- is used when you are certain that the tests don't make any changes to those conditions. ( Otherwise -> beforeEach) 

 

BeforeEach  ( = Before )

- is called before each individual test. ( it will run before every test, so it can reset the conditions for the next one. ) 

- is used when the tests do make changes to those conditions. 

- Unless the initialization is slow or computationally expensive, it may be safest to default to using beforeEach as it reduces the opportunity for human error, i.e. not realizing that one test is changing the setup for the next one. 

 

    beforeAll((done) => {
      // Mocking method from within Box file
      transferBoxPlanSpy = jest.spyOn(Box, 'transferPlanFromBox').mockImplementation(() => Promise.resolve());

      // Pulling data from MongoDB
      User.findOne({ user_name: 'testsurgeon1' }, (err, user) => {
        user.addMGSPermission();
        user.save(done);
      });
    });

    beforeEach(() => {
      planData2 = {
        user_name: 'hello1',
        laterality: 'right',
        plan_id: 'testplan42',
        order_number: '856-hd-02-l',
        file_id: '123456sbyuidbefui',
      };
    });

The sample above is a good example of using both in combination 

the slow network call is put in beforeAll, so it only has to happen once; and the data object (which is presumably modified by the tests is reset each time in beforeEach. 

 

 

Q : Do I even need to use beforeAll() ? Wouldn't just placing it inside the describe work as well ? 

-> No. describe is just a group related tests togeter, it's not a substitute for beforeAll(). 

 

Describe 

is a group related tests. 

 

 


 

 

References: 

https://www.youtube.com/watch?v=Qlmv7nox5pM 

https://stackoverflow.com/questions/21418580/what-is-the-difference-between-before-and-beforeeach

 

What is the difference between `before()` and `beforeEach()`?

What specifically is the difference between Mocha's before() and beforeEach()? (Same question for after() and afterEach().) I assume before() runs once per describe() block, and beforeEach() runs ...

stackoverflow.com

https://blog.outsider.ne.kr/774

 

should.js : node.js에서 사용할 수 있는 BDD 스타일의 Assertion 모듈 :: Outsider's Dev Story

몇일 전에 mocha 테스트 프레임워크를 소개한 관계로 mocha에서 사용할 수 있는 assetion 모듈인 should.js를 정리해 봅니다. mocha 글에서도 썼듯이 mocha는 꼭 should.js를 써야하는 것은 아니지만 사용법도

blog.outsider.ne.kr

 

 

반응형