JavaScript Unit Test by MinHo Kim (Dexter Developer Guide)
Contents 1. Why 2. How 3. What 4. BDD / TDD 5. Assertion: Chai 6. Test Double: Sinon 7. Test Framework: Mocha 8. Test Runner: Karma 9. All in One for Dexter WEB
Why Multi-purpose for one function Hard to analyze, debug, and test One change impact others Hard to change and improve code Only manual test and eye-check Hard to deploy Slow Development
How Create Small Function by TDD/BDD One Purpose for one Function Run Test Automatically with Tools Detect Impact from one Change Continuously Integration and Delivery Reduce Human Errors on Test Fast and Safe Development
5 Build/Run : Gulp 3. What Test Runner(Karma) Test Framework(Mocha) Assertion(Chai) Test Double(Sinon) Node
BDD / TDD Requirements and Features Write a Failing Acceptance Test Write a Failing Acceptance Test Write a Failing Unit Test Write a Failing Unit Test Make the Test Pass Make the Test Pass Refactor divide BDD TDD Pass User Developer Detailed Design or Codes
BDD / TDD Write a Failing Acceptance Test Write a Failing Acceptance Test Write a Failing Unit Test Write a Failing Unit Test Refactor divide BDD TDD Pass User As an administrator of Dexter, I want to find a User by ID NoFeatures 1 Manage User Info 2 Search User Info By ID NoUser Stories 1 Find User By ID 2… Make the Test Pass Make the Test Pass
Assertion: Chai support node and browser support BDD/TDD Style support any JS Test Fwk. support Should, Expect, Assert style v2.1.0 Should ExpectAssert refer to npm install –g chai
Assertion: Chai refer to Should & Expect (1)
Assertion: Chai refer to Should & Expect (2)
Assertion: Chai refer to Assert
Test Double: Sinon Test Double Spy StubMockFake Timer Stunt Object for Test Subject refer to an example: examples in this document: npm install sinon –save-dev
Test Double: Sinon Sinon Spy a fake method(= spy) recode args, return value, exceptions and ‘this’ value refer to TypesExamples Creation var spy = sinon.spy() var spy = sinon.spy(myFunction); var spy = sinon.spy(object, “method”); special args spy.withArgs(aSpecialArg); spy.withArgs(aSpecialArg).called spy.calledWith(arg1, arg2, …); spy.calledWithExactly(…); spy.calledWithMatch(…); Calling var spyCall = spy.getCall(n); spy.thisValues/args/exceptions/returnValues Call Count spy.callCount, spy.called, spy.calledOnce, / Twice / Thrice Order calledBefore(anotherSpy); calledAfter(anotherSpy); Excepton spy.threw(); spy.threw(“TypeError”); Return spy.returned(obj); Spy APIs
Test Double: Sinon Sinon Stub a function with pre-programmed behavior (= predefined behavior) can be anonymous and wrap existing function refer to TypesExamples Creation var stub = sinon.stub(); var stub = sinon.stub(obj, “method”); var stub = sinon.stub(o, “m”, func); special args stub.withArgs(arg1, …).returns(v); Calling stub.onCall(n).returns(v);.onFirstCall,.onSecondCall() Return stub.returns(obj);.returnsThis(); Yield stub.yields(arg1, …); stub.yieldsOn(context, arg1, …); stub.yieldsTo(property, arg1, …); Excepton stub.throws(); stub.throws(obj); Return stub.withArgs(arg1, …).throws(e); Stub APIs
Test Double: Sinon Sinon Mock a fake method(= spy) and a pre-programmed behavior(= stub) pre-programmed expectations(mock!) especially orders or call-times of a function need call verify() refer to StepsExamples Creation var mock = sinon.mock(obj); Expectation var exp = mock.expects(“method”); Calling mock.method(); Restoring mock.restore() Verifying mock.verify(); // has.restore() TypesAPIs Calling- times.atLeast(number).atMost(number).once().twice().thrice().exactly(number).never() argument.withArgs(a1, …).withExactArgs(…) Mock APIs Expectation APIs
Test Double: Sinon Sinon Fake Timer a synchronous implementation of setTimeout() clock object : use for passing time refer to StepsExamples Creation var clock = sinon.useFakeTimers(); or sinon.useFakeTImers(now); Passing Time clock.tick(ms); restore clock.restore(); Fake Timer APIs source:
Mocha Mocha is a feature-rich JavaScript test framework running on Node.js and the browser, making asynchronous testing simple and fun.Node.js Initial release (2011/10/22 ) last Release: v2.1.0 (2014/11/23) refer to a guide:
Mocha $ npm install –g mocha $ mkdir test $ vim test/test.js $ mocha
Test Framework: Mocha Command Line Options usage: mocha [debug] [options] [files] short options full options description -h --helpoutput usage information -V --versionoutput the version number -A --async-onlyforce all tests to take a callback (async) -d --debugenable node’s debugger -g --grep only run tests matching -i --invertinverts –grep -s --slow “slow” test threshold in milliseconds [75] -t --timeout set test-case timeout in milliseconds [2000] -w --watchwatch files for changes --check-leakscheck for global variable leaks --full-tracedisplay the full stack trace --harmonyeanble all harmony features(except typeof) --harmony-*collections, generators, proxies, arrow_functions, classes, _proxies,_shipping --no-timeoutsdisables timeouts --recursiveinclude sub directories
20 describe (‘feature/user story description’, function() { }); describe (‘feature/user story description’, function() { }); 7. Test Framework: Mocha Basic Structure – BDD style describe (‘feature/user story description’, function() { }); describe (‘feature/user story description’, function() { }); it(‘should do something’, function([done]) { }); it(‘should do something’, function([done]) { }); assertion statements: assert/should/expect test doubles: spy/stub/mock/fake assertion statements: assert/should/expect test doubles: spy/stub/mock/fake it(‘should do something’, function([done]) { }); it(‘should do something’, function([done]) { }); assert.equal(expected value, real value); var stub = sinon.stub(); assert.equal(expected value, real value); var stub = sinon.stub(); before(function(){ // run before all tests }); before(function(){ // run before all tests }); after(function(){ // run after all tests }); after(function(){ // run after all tests }); beforeEach(function(){ // run before each tests }); beforeEach(function(){ // run before each tests }); afterEach(function(){ // run after each tests }); afterEach(function(){ // run after each tests });
21 suite (‘a set of sub-suites’, function() { }); suite (‘a set of sub-suites’, function() { }); 7. Test Framework: Mocha Basic Structure – TDD style suite (‘a set of test-cases’, function() { }); suite (‘a set of test-cases’, function() { }); test(‘should do something’, function([done]) { }); test(‘should do something’, function([done]) { }); assertion statements: assert/should/expect test doubles: spy/stub/mock/fake assertion statements: assert/should/expect test doubles: spy/stub/mock/fake test(‘should do something’, function([done]) { }); test(‘should do something’, function([done]) { }); assert.equal(expected value, real value); var stub = sinon.stub(); assert.equal(expected value, real value); var stub = sinon.stub(); suiteSetup(function(){ // run before all tests }); suiteSetup(function(){ // run before all tests }); suiteTeardown( function(){ // run after all tests }); suiteTeardown( function(){ // run after all tests }); setup(function(){ // run before each tests }); setup(function(){ // run before each tests }); teardown(function(){ // run after each tests }); teardown(function(){ // run after each tests });
Test Framework: Mocha Report Types HTMLDOC Min Json Progress List Landing Strip Nyan refer to
Test Framework: Mocha Synchronous Code Asynchronous Code refer to
Test Framework: Mocha only() & skip() refer to
Test Framework: Mocha Dynamically Generating Tests refer to
Test Framework: Mocha Timeout refer to
Test Runner: Karma refer to Run Test with Mocha, QUnit, etc. Front-end Test support IE, Chrome, Firefox Use with AngularJS $ npm install –g karma an example: $ npm install –g karma $ karma init // create karma.conf.js file // create test codes $ karma start // start karma server $ karma run // execute tests
Test Runner: Karma an example: karma-mocha $ npm install karma --save-dev
All in one for Dexter WEB front-end create test file in the dexter-server project: dexter-server/test/front-end/test-AngularControllerName.js write test code: 1 ①write a description with angular app name ②load your angular app ③declare variables for controller and scope for angular ④inject and create the scope and the controller variables ⑤write test codes with using the controller and the scope variables ⑥run test codes with gulp or karma $ gulp watch-test $ karma start $ karma run $ gulp test-front-end
All in one for Dexter WEB back-end create test file in the dexter-server project: dexter-server/test/back-end/test-ModuleName.js write test code: ①create a describe and it function for tests ②explain what you test ③use ‘done’, if you test asynchronously ④assert logics ⑤run test codes with gulp $ gulp watch-test $ gulp test-back-end
All in one for Dexter WEB Demo & Pair Programming