From 8b924370990e0f2b0b7214e3c8b17c5c86655a51 Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 13 Sep 2021 22:58:40 -0400 Subject: [PATCH 01/27] Update UnitTestingApp.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @WildHog Suggested the following changes: - Added additional attributes to generate statistics and counting tests, failed and passed tests - Added an attribute to control the level of information to send to the console (`_levelInfo`). It allows to generates only summary information - Reset testing counters - `printSummary`: Prints the summary of all previous tests executed so far - `printSubHeader`: To allow the second level of printing information - `assertEquals`: To be able to test a function and to compare with the expected result. This function allows to automatically creation message for failing tests comparing the result with the expected result. So it is more friendly because you don´t need to provide `message` input argument (optional) I modified some existing methods to include the new attributes, such as `assert`, and `printHeader`. For `printHeader` I changed the behavior, so it only prints information when the test is enabled, it doesn't make sense to print information when the test is not enabled. If you accept the above changes I can help you to update the documentation. Please let me know and thanks for this contribution, I tried several options for my case, and it is really an elegant approach and very simple. I am still learning JS, I didn´t quite understand why you are using Weakmap for `_enable`and `_runningInGas`so I didn´t take that road for creating the new attributes. I am going to upload to this fork a corresponding `TestingTemplateChanges` file so you can run and see the changes I made. Please let me know, Thanks, David --- UnitTestingApp.js | 133 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 22 deletions(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index 3b2e36f..3b913ff 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -8,19 +8,27 @@ * Class for running unit tests */ - let UnitTestingApp = (function () { +let UnitTestingApp = (function () { + _nTests = 0; // Total number of tests executed + _nFailTests = 0; // Total test failed + _nPassTests = 0; // Total test passed + _levelInfo = 1; // Level of information to show in the output (0-summary, 1-trace and test result information) const _enabled = new WeakMap(); const _runningInGas = new WeakMap(); class UnitTestingApp { constructor() { if (UnitTestingApp.instance) return UnitTestingApp.instance; - + _enabled.set(this, false); _runningInGas.set(this, false); + this._nTests = 0; + this._nFailTests = 0; + this._nPassTests = 0; + this._levelInfo = 1; UnitTestingApp.instance = this; - + return UnitTestingApp.instance; } @@ -31,9 +39,9 @@ disable() { _enabled.set(this, false); } - + get isEnabled() { - return _enabled.get(this); + return _enabled.get(this); } get isInGas() { @@ -52,24 +60,88 @@ if (console.clear) console.clear(); } + getLevelInfo() { + return this._levelInfo; + } + + setLevelInfo(value) { + this._levelInfo = value; + } + + resetTestCounters() { + this._nFailTests = 0; + this._nPassTests = 0; + this._nTests = 0; + } + /** * Tests whether conditions pass or not * @param {Boolean | Function} condition - The condition to check - * @param {String} message - the message to display in the onsole + * @param {String} message - the message to display in the console (based on _levelInfo value) * @return {void} */ assert(condition, message) { - if(!_enabled.get(this)) return; - if(this.isInGas !== this.runningInGas) return; + if (!_enabled.get(this)) return; + if (this.isInGas !== this.runningInGas) return; + this._nTests++; try { if ("function" === typeof condition) condition = condition(); - if (condition) console.log(`✔ PASSED: ${message}`); - else console.log(`❌ FAILED: ${message}`); - } catch(err) { - console.log(`❌ FAILED: ${message} (${err})`); + if (condition) { + this._nPassTests++; + if (this._levelInfo > 0) { console.log(`✔ PASSED: ${message}`) }; + } + else { + this._nFailTests++; + if (this._levelInfo > 0) { console.log(`❌ FAILED: ${message}`) }; + } + + } catch (err) { + this._nFailTests++; + if (this._levelInfo > 0) { console.log(`❌ FAILED: ${message} (${err})`) }; } } + /** + * Tests whether fun result is equal to expected result or not + * @param {Function} fun - The function to evaluate + * @param {String} expectedResult - The expected result to validate + * @param {String} message - If present, then used as message to display in the console (based on _levelInfo value). + * If message is not present, then in case the result is not equal, it shows the missmatch + * In the form of: "result != expectedResult". If the result is valid and message is not + * provided, then it only indicates the test passed. + * @return {void} + */ + assertEquals(fun, expectedResult, message = null) { + if (!_enabled.get(this)) return; + if (this.isInGas !== this.runningInGas) return; + this._nTests++; + let msg, result; + + try { + if ("function" === typeof fun) { + result = fun(); + } + let condition = expectedResult == result; + if (condition) { + this._nPassTests++; + msg = (message == null) ? "" : ": " + message; + if (this._levelInfo >= 1) { console.log(`✔ PASSED${msg}`) }; + } + else { + this._nFailTests++; + msg = (message == null) ? result + " != " + expectedResult : message; + if (this._levelInfo >= 1) { console.log(`❌ FAILED: ${msg}`) }; + } + + } catch (err) { + this._nFailTests++; + this.errorMsg = result + " != " + expectedResult; + msg = (message == null) ? result + " != " + expectedResult : message; + if (this._levelInfo >= 1) { console.log(`❌ FAILED(err): ${msg} (${err})`) }; + } + } + + /** * Tests functions that throw error messages * @param {Function} callback - the function that you expect to return the error message @@ -78,8 +150,8 @@ * @return {void} */ catchErr(callback, errorMessage, message) { - if(!_enabled.get(this)) return; - if(this.isInGas !== this.runningInGas) return; + if (!_enabled.get(this)) return; + if (this.isInGas !== this.runningInGas) return; let error; let isCaught = false; try { @@ -98,21 +170,38 @@ * @returns {Boolean} */ is2dArray(array, message) { - if(!_enabled.get(this)) return; - if(this.isInGas !== this.runningInGas) return; + if (!_enabled.get(this)) return; + if (this.isInGas !== this.runningInGas) return; try { if (typeof array === 'function') array = array(); this.assert(Array.isArray(array) && Array.isArray(array[0]), message); - } catch(err) { + } catch (err) { this.assert(false, `${message}: ${err}`); } } printHeader(text) { - if(this.isInGas !== this.runningInGas) return; - console.log('*********************'); - console.log('* ' + text); - console.log('*********************'); + if (!_enabled.get(this)) return; + if (this.isInGas !== this.runningInGas) return; + if (_levelInfo > 0) { + console.log('*********************'); + console.log('* ' + text) + console.log('*********************'); + } + } + + printSubHeader(text) { + if (!_enabled.get(this)) return; + if (this._levelInfo > 0) { console.log('** ' + text) }; + } + + printSummary() { + if (!_enabled.get(this)) return; + if (this.isInGas !== this.runningInGas) return; + if (this._levelInfo > 0) { + console.log('TOTAL TESTS= ' + this._nTests + ', ❌ FAILED=' + this._nFailTests + ', ✔ PASSED=' + this._nPassTests); + } + console.log((this._nFailTests == 0) ? "ALL TESTS ✔ PASSED" : "❌ Some Tests Failed"); } /** @@ -127,4 +216,4 @@ return UnitTestingApp; })(); -if (typeof module !== "undefined") module.exports = UnitTestingApp; \ No newline at end of file +if (typeof module !== "undefined") module.exports = UnitTestingApp; From 1249a548e3ca52bdeefbbf1c4f2184bbcd4674fe Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 13 Sep 2021 23:00:24 -0400 Subject: [PATCH 02/27] Create TestingTemplateChanges Added the file for visualizing and testing the changes added. --- TestingTemplateChanges | 90 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 TestingTemplateChanges diff --git a/TestingTemplateChanges b/TestingTemplateChanges new file mode 100644 index 0000000..7a7a31a --- /dev/null +++ b/TestingTemplateChanges @@ -0,0 +1,90 @@ +// jshint esversion: 8 +if (typeof require !== 'undefined') { + UnitTestingApp = require('./UnitTestingApp.js'); +} + +// Function to test +function addTwoValues (a, b) { + return a + b; +} + +/***************** + * TESTS + * Taking the sources files from: https://github.com/WildH0g/UnitTestingApp + *****************/ + +/** + * Runs the tests; insert online and offline tests where specified by comments + * @returns {void} + */ +function runTests() { + const test = new UnitTestingApp(); + test.enable(); + test.clearConsole(); + + test.runInGas(false); + test.printHeader('Testing addTwoValues online'); + + + /************************ + * Run Local Tests Here + ************************/ + + test.runInGas(true); + test.printHeader('Testing addTwoValues local'); + // Testing new method: assertEquals, printSummary and printSubHeader using addTwoValue function with default levelInfo + test.printSubHeader('Using default Level Info value (1)'); + test.assertEquals(() => addTwoValues(1,2),3); + test.assertEquals(() => addTwoValues(1,2),4); + test.assertEquals(() => addTwoValues(1,2),4, "message input argument specified"); + test.printSummary(); + + // Testing the same tests just printing summary information + test.printSubHeader('Testing the output setting levelInfo =0'); + test.setLevelInfo(0); // 0-Only summary result, 1-Detail results + console.log('LevelInfo =' + test.getLevelInfo()); + test.assertEquals(() => addTwoValues(1,2),3); + test.assertEquals(() => addTwoValues(1,2),4); + test.assertEquals(() => addTwoValues(1,2),4, "test expected to fail"); + test.printSummary(); + console.log("Reset counters"); + test.resetTestCounters(); + test.printSummary(); + + // Testing assert function work as expected + test.printSubHeader('Testing with assert, with default level Info'); + test.setLevelInfo(1); + test.assert(() => addTwoValues(1,2) == 3, "Valid case"); + test.assert(() => addTwoValues(1,2) == 4, "Invalid case"); + test.printSummary(); + test.printSubHeader('Testing with assert, with printing just summary information only: one test passed, one failed'); + console.log("Reset counters"); + test.resetTestCounters(); + test.assert(() => addTwoValues(1,2) == 3, "Valid case"); + test.assert(() => addTwoValues(1,2) == 4, "Invalid case"); + test.printSummary(); + // Note under levelInfo = 0, only printSummary is able to print, all other print methods don't print information + // therefore we use directly console.log + console.log('Testing with assert, with printing just summary information only: one test passed'); + console.log("Reset counters"); + test.resetTestCounters(); + test.assert(() => addTwoValues(1,2) == 3, "Valid case"); + test.printSummary(); + + + /************************ + * Run Online Tests Here + ************************/ + +} + +/** + * If we're running locally, execute the tests. In GAS environment, runTests() needs to be executed manually + */ +(function () { + /** + * @param {Boolean} - if true, were're in the GAS environment, otherwise we're running locally + */ + const IS_GAS_ENV = typeof ScriptApp !== 'undefined'; + if (!IS_GAS_ENV) runTests(); +})(); From a3627f25dcf55d496af89e3f978e49b5d716f2ce Mon Sep 17 00:00:00 2001 From: dlealv Date: Tue, 21 Sep 2021 23:41:06 -0400 Subject: [PATCH 03/27] Update README.md Updated the REAMDE file, based on the minor modification added. --- README.md | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6710813..669df98 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,78 @@ test.runInGas(true); Then we have the actual built-in testing methods, `assert()`, `catchErr()` and `is2dArray()`. +### Control level of information to log out + +The function `setLevelInfo(value)` controls the level information to be reported through the console. If `value`is equal to `0`, it runs in silent mode, i.e. no information will be reported. The only exception from print-family functions is `printSummary()` that under this mode just logs out a single summary line. For example if no errors found, the output will be: + +```ALL TESTS ✔ PASSED``` + +if at least one test failed then it logs out the following information: + +```❌ Some Tests Failed``` + +This setup is usufull for large tests where we just want to add some incremental test and to have just a minimal output about the overall testing result. + +Here is how the level of information can be specified for silent mode: + +```javascript +test.setLevelInfo(0); +``` +If value is `1`(default value) it means trace information will log out the result per each test, indicating if the test fails or passed. Depending on the specific testing function it will log out different information, for example, let´s says we want to test the following function: + +```javascript +function addTwoValues (a, b) { + return a + b; +} +``` + +then invoking the following assert function: + +```javascript +test.assertEquals(() => addTwoValues(1,2),3); // where expected result is 3 +``` + +will return + +``` +✔ PASSED +``` + +on contrary, if we invoke the function with a wrong expected result: + +```javascript +test.assertEquals(() => addTwoValues(1,2),4); // where expected result is 4 +``` + +will return + +``` +❌ FAILED: 3 != 4 +``` +If we invoke `assertEquals` including its optional input argument `message`, then the output information will be on each case the information specificied by this input argument + +```javascript +test.assertEquals(() => addTwoValues(1,2),3, "Expected result: 1+2 = 3"); +test.assertEquals(() => addTwoValues(1,2),4, "Wrong result because 1+2 should be equal to 3"); +``` + +The output will be: + +``` +✔ PASSED: Expected result: 1+2 = 3 +❌ FAILED: Wrong result because 1+2 should be equal to 3 +``` + +If we are using `assert` that has `message`as input argument, the message will be printed as testing result. + +In case `printSummary()` is invoked with level info is equal to `1`, it logs out an additional line providing statistics about testing results: + +``` +TOTAL TESTS= 1, ❌ FAILED=0, ✔ PASSED=1 +ALL TESTS ✔ PASSED +``` +Indicating that `1`test passed and `0` test failed + ### assert(condition, message) `assert()` is the main method of the class. The first argument that it takes is the condition and it checks whether the condition is truthy or falsy. The condition can either be a boolean value or a function that returns such a condition, function being the preferred approach for error catching reasons. If the condition is truthy, it logs out a “PASSED” message, otherwise it logs out a “FAILED” message. If you pass in a function that throws an error, the method will catch the error and log out a “FAILED” message. For example: @@ -61,6 +133,16 @@ test.assert(() => num % 2 === 0, `Number ${num} is even`’); test.assert(() => num > 10, `Number ${num} is bigger than 10`); // logs out FAILED: Number 2 is bigger than 10 ``` +### assertEquals(fun, expectedResult, message) +`assertEquals`is another assert function, that helps to verify the result with respect the expected value. The first argument is the function we would like to evaluate, the second argument is the expected result. If returned value of the `fun`is not equal to `expectedResult` it fails, otherwise passed. Here is how to validate javascript standard `max()`function: + +```javascript +test.assertEquals(() => Math.max(1,2),2); # Expected result is 2 (equal to actual result) +test.assertEquals(() => Math.max(1,2),1); # Expected result is 1 (different than actual result) +test.assertEquals(() => Math.max(1,2),2, "Correct result the max of 1,2 is 2"); +``` +The last case specifies the message to log out as testing result regardless of the test result, the same message will be log out. + ### catchErr(condition, expectedErrorMessage, message) @@ -94,20 +176,49 @@ test.is2dArray(values, ‘values is an array of arrays’); // logs out PASSED Then there are a couple of helper methods, `printHeader()` and `clearConsole()`. -### printHeader(headerStr) +### Print* Functions -Just helps with readability by printing a header in the console like this: +**Note**: The level of information shows by print-family functions wil depend on the level of information the user specified via `setLevelInfo(vaue)`, or if no value was specified it assumes the level of infomration is `1`. See section: *Control level of information to log out* for more information. + +The `printHeader()` function just helps with readability by printing a header in the console like this. It can be used for printing for example the title of the testing set. Here the expected result under `1` level of information: ```javascript test.printHeader(‘Offline tests’); -/* +``` Logs out the following: +``` ********************* * Offline tests ********************* -*/ +``` +There also a second print header function: `printSubHeader(text)`, usefull to log out a sub header as a single line with prefix `**`. Here the output under level of information equal to `1`: + +```javascript +test.printSubHeader(‘Testing valid cases...’); ``` +logs out: +``` +** Testing valid cases... +``` +There is a third print function: `printSummary()`, that logs out a summary of testing results (depending on the level of information we want to show) + +```javascript +test.printSummary(); +``` +If we ran 20 tests, where there is one failed test, under level of information equal to `1`, the result will be: +``` +TOTAL TESTS= 20, ❌ FAILED=1, ✔ PASSED=19 +ALL TESTS ✔ PASSED +``` +Similarly if the level of information is equal to `0`, the output will be: +``` +ALL TESTS ✔ PASSED +``` + +### resetTestCounters() +The `resetTestCounters()` is usefull for reseting testing counters (`_nTests`, `_nFailTests`, `_nPassTests`, attributes of the class `UnitTestingApp`), the function `printSummary()` will log out information depending on the overall testing results based on such attributes. We can use this function to reset testing counters after a running a set of tests, so we can print a summary information per set of tests using `printSummary()`. + ### clearConsole() @@ -183,4 +294,4 @@ function runTests() { ## Current Version -0.1.0 +0.1.1 From 862e0b6992a6c0971985d1417236cd6551b2f829 Mon Sep 17 00:00:00 2001 From: dlealv Date: Wed, 22 Sep 2021 23:42:31 -0400 Subject: [PATCH 04/27] Delete TestingTemplateChanges Creating a new version and move it to Example --- TestingTemplateChanges | 90 ------------------------------------------ 1 file changed, 90 deletions(-) delete mode 100644 TestingTemplateChanges diff --git a/TestingTemplateChanges b/TestingTemplateChanges deleted file mode 100644 index 7a7a31a..0000000 --- a/TestingTemplateChanges +++ /dev/null @@ -1,90 +0,0 @@ -// jshint esversion: 8 -if (typeof require !== 'undefined') { - UnitTestingApp = require('./UnitTestingApp.js'); -} - -// Function to test -function addTwoValues (a, b) { - return a + b; -} - -/***************** - * TESTS - * Taking the sources files from: https://github.com/WildH0g/UnitTestingApp - *****************/ - -/** - * Runs the tests; insert online and offline tests where specified by comments - * @returns {void} - */ -function runTests() { - const test = new UnitTestingApp(); - test.enable(); - test.clearConsole(); - - test.runInGas(false); - test.printHeader('Testing addTwoValues online'); - - - /************************ - * Run Local Tests Here - ************************/ - - test.runInGas(true); - test.printHeader('Testing addTwoValues local'); - // Testing new method: assertEquals, printSummary and printSubHeader using addTwoValue function with default levelInfo - test.printSubHeader('Using default Level Info value (1)'); - test.assertEquals(() => addTwoValues(1,2),3); - test.assertEquals(() => addTwoValues(1,2),4); - test.assertEquals(() => addTwoValues(1,2),4, "message input argument specified"); - test.printSummary(); - - // Testing the same tests just printing summary information - test.printSubHeader('Testing the output setting levelInfo =0'); - test.setLevelInfo(0); // 0-Only summary result, 1-Detail results - console.log('LevelInfo =' + test.getLevelInfo()); - test.assertEquals(() => addTwoValues(1,2),3); - test.assertEquals(() => addTwoValues(1,2),4); - test.assertEquals(() => addTwoValues(1,2),4, "test expected to fail"); - test.printSummary(); - console.log("Reset counters"); - test.resetTestCounters(); - test.printSummary(); - - // Testing assert function work as expected - test.printSubHeader('Testing with assert, with default level Info'); - test.setLevelInfo(1); - test.assert(() => addTwoValues(1,2) == 3, "Valid case"); - test.assert(() => addTwoValues(1,2) == 4, "Invalid case"); - test.printSummary(); - test.printSubHeader('Testing with assert, with printing just summary information only: one test passed, one failed'); - console.log("Reset counters"); - test.resetTestCounters(); - test.assert(() => addTwoValues(1,2) == 3, "Valid case"); - test.assert(() => addTwoValues(1,2) == 4, "Invalid case"); - test.printSummary(); - // Note under levelInfo = 0, only printSummary is able to print, all other print methods don't print information - // therefore we use directly console.log - console.log('Testing with assert, with printing just summary information only: one test passed'); - console.log("Reset counters"); - test.resetTestCounters(); - test.assert(() => addTwoValues(1,2) == 3, "Valid case"); - test.printSummary(); - - - /************************ - * Run Online Tests Here - ************************/ - -} - -/** - * If we're running locally, execute the tests. In GAS environment, runTests() needs to be executed manually - */ -(function () { - /** - * @param {Boolean} - if true, were're in the GAS environment, otherwise we're running locally - */ - const IS_GAS_ENV = typeof ScriptApp !== 'undefined'; - if (!IS_GAS_ENV) runTests(); -})(); From 75fed20e8c97d7d608510bbdf3636b1a3307e9a0 Mon Sep 17 00:00:00 2001 From: dlealv Date: Wed, 22 Sep 2021 23:50:25 -0400 Subject: [PATCH 05/27] Create TestForVersion_0.1.1.js Testing sample for the new changes in version 0.1.1 --- Example/TestForVersion_0.1.1.js | 96 +++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 Example/TestForVersion_0.1.1.js diff --git a/Example/TestForVersion_0.1.1.js b/Example/TestForVersion_0.1.1.js new file mode 100644 index 0000000..aabde85 --- /dev/null +++ b/Example/TestForVersion_0.1.1.js @@ -0,0 +1,96 @@ +/* +Testing script for testing the new changes introduced in UnitTestingApp script on version 0.1.1 +*/ + +// jshint esversion: 8 +if (typeof require !== 'undefined') { + UnitTestingApp = require('./UnitTestingApp.js'); +} + +// Function to test +function addTwoValues (a, b) { + return a + b; +} + +/***************** + * TESTS + * Taking the sources files from: https://github.com/WildH0g/UnitTestingApp + *****************/ + +/** + * Runs the tests; insert online and offline tests where specified by comments + * @returns {void} + */ +function runTests() { + const test = new UnitTestingApp(); + test.enable(); + test.clearConsole(); + test.runInGas(true); + + /************************ + * Run Local Tests Here + ************************/ + + test.printHeader("Testing addTwoValues using assertEquals"); + // Testing new method: assertEquals, printSummary and printSubHeader using addTwoValue function with default levelInfo + // We haven´t set levelInfo, so we are using the default value: 1 + test.printSubHeader("Using default Level Info value (1) for 'addTwoValues' function"); + test.assertEquals(() => addTwoValues(1,2),3); // PASS + test.assertEquals(() => addTwoValues(2,2),4, "Valid case: 2 + 2 = 4"); // PASS with message + test.assertEquals(() => addTwoValues(1,2),4); // FAIL + test.assertEquals(() => addTwoValues(1,2),4, "Expected to fail, because 1+2 != 4"); + test.printSummary(); // It should print final result and statistics (two lines) + + // Testing the same tests but in silent mode (LevelInfo = 0) + test.printSubHeader("Testing the output setting levelInfo = 0"); // It logs out, because we haven't changed LevelInfo yet + test.setLevelInfo(0); // 0-Only summary result, 1-Detail results + // Because LevelInfo = 0 we use the console for traceability purpose + console.log("LevelInfo = " + test.getLevelInfo()); + test.assertEquals(() => addTwoValues(1,2),3); + test.assertEquals(() => addTwoValues(2,2),4, "Valid case: 2 + 2 = 4"); + test.assertEquals(() => addTwoValues(1,2),4); + test.assertEquals(() => addTwoValues(1,2),4, "Expected to fail, because 1+2 != 4"); + test.printSummary(); // Only summary result: Some test failed + console.log("Reset counters"); + test.resetTestCounters(); + console.log("Shows only the summary line, the counters are reseted so no tests, therefore all test passed"); + test.printSummary(); //Shows only summary line + + // Testing the existing assert function work as expected + test.printHeader("Testing addTwoValues using assert"); + test.printSubHeader('Default level Info'); + test.setLevelInfo(1); + test.printSubHeader("LevelInfo = " + test.getLevelInfo()); + test.assert(() => addTwoValues(1,2) == 3, "Valid case: 1 + 2 = 3"); + test.assert(() => addTwoValues(1,2) == 4, "Invalid case: 1 + 2 != 4"); + test.printSubHeader("Expected to log out two lines: statistics and summary"); + test.printSummary(); + + test.printSubHeader("Testing under silent mode: one test passed, one failed"); + test.setLevelInfo(0); + console.log("LevelInfo = " + test.getLevelInfo()); + test.assert(() => addTwoValues(1,2) == 3, "Valid case: 1 + 2 = 3"); + test.assert(() => addTwoValues(1,2) == 4, "Invalid case: 1 + 2 != 4"); + console.log("Expected to see some test failed"); + test.printSummary(); + + // Reset the counters. For testing all test passed under silent mode + console.log("Testing the case all test passed with silent mode"); + console.log("Reseting the counters"); + test.resetTestCounters(); + console.log('Testing with assert, under silent mode: one test executed and passed'); + test.assert(() => addTwoValues(1,2) == 3, "Valid case"); + console.log("Printing the summary line only: all test passed"); + test.printSummary(); + console.log("Changing the level info to 1"); + test.setLevelInfo(1); + test.printSubHeader("Showing now printSummary with two lines for the previous set of tests"); + test.printSummary(); + + test.printHeader("Testing enable = false, the print-family functions print no information"); + test.disable(); + test.printHeader("No expected output"); + test.printSubHeader("No expected output"); + test.printSummary(); + +} From fa69a38c4900147e81a44c0792f1e64e8c59151f Mon Sep 17 00:00:00 2001 From: dlealv Date: Thu, 23 Sep 2021 00:11:39 -0400 Subject: [PATCH 06/27] Update README.md Minor changes, spelling, reviewed some sentences. --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 669df98..f5595d9 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ if(test.isEnabled) { test.disable(); // tests will not run below this line ``` +**Note:** If tests are disable, print-family function will no produce any output regardless of the level of information we have configured to show. See section: *Control Level of Information to log out* for more information. ### Choosing the Environment with runInGas(Boolean) @@ -48,7 +49,7 @@ test.runInGas(true); Then we have the actual built-in testing methods, `assert()`, `catchErr()` and `is2dArray()`. -### Control level of information to log out +### Control Level of Information to log out The function `setLevelInfo(value)` controls the level information to be reported through the console. If `value`is equal to `0`, it runs in silent mode, i.e. no information will be reported. The only exception from print-family functions is `printSummary()` that under this mode just logs out a single summary line. For example if no errors found, the output will be: @@ -65,7 +66,7 @@ Here is how the level of information can be specified for silent mode: ```javascript test.setLevelInfo(0); ``` -If value is `1`(default value) it means trace information will log out the result per each test, indicating if the test fails or passed. Depending on the specific testing function it will log out different information, for example, let´s says we want to test the following function: +If value is `1`(default value) it means trace information will log out the result per each test, indicating if the test fails or passed. Depending on the specific testing function it will log out different information, for example, let's says we want to test the following custom function: ```javascript function addTwoValues (a, b) { @@ -96,7 +97,8 @@ will return ``` ❌ FAILED: 3 != 4 ``` -If we invoke `assertEquals` including its optional input argument `message`, then the output information will be on each case the information specificied by this input argument + +As you can see there is no need to specify the message, it shows where is the problem. If we invoke `assertEquals` including its optional input argument `message`, then the output information will be on each case the information specificied by this input argument ```javascript test.assertEquals(() => addTwoValues(1,2),3, "Expected result: 1+2 = 3"); @@ -110,15 +112,15 @@ The output will be: ❌ FAILED: Wrong result because 1+2 should be equal to 3 ``` -If we are using `assert` that has `message`as input argument, the message will be printed as testing result. +If we are using `assert` that has a mandatory `message`as input argument, the message will be printed as testing result. -In case `printSummary()` is invoked with level info is equal to `1`, it logs out an additional line providing statistics about testing results: +When using `printSummary()` with level info is equal to `1`, it logs out an additional line providing statistics about testing results: ``` TOTAL TESTS= 1, ❌ FAILED=0, ✔ PASSED=1 ALL TESTS ✔ PASSED ``` -Indicating that `1`test passed and `0` test failed +Indicating that `1`test passed and `0` test failed and the total tests executed ### assert(condition, message) @@ -139,7 +141,7 @@ test.assert(() => num > 10, `Number ${num} is bigger than 10`); ```javascript test.assertEquals(() => Math.max(1,2),2); # Expected result is 2 (equal to actual result) test.assertEquals(() => Math.max(1,2),1); # Expected result is 1 (different than actual result) -test.assertEquals(() => Math.max(1,2),2, "Correct result the max of 1,2 is 2"); +test.assertEquals(() => Math.max(1,2),2, "Correct result the max of 1,2 is 2"); # Using option input argument message ``` The last case specifies the message to log out as testing result regardless of the test result, the same message will be log out. @@ -176,9 +178,9 @@ test.is2dArray(values, ‘values is an array of arrays’); // logs out PASSED Then there are a couple of helper methods, `printHeader()` and `clearConsole()`. -### Print* Functions +### Print-family Functions -**Note**: The level of information shows by print-family functions wil depend on the level of information the user specified via `setLevelInfo(vaue)`, or if no value was specified it assumes the level of infomration is `1`. See section: *Control level of information to log out* for more information. +**Note**: The level of information shows by print-family functions wil depend on the level of information the user specified via `setLevelInfo(vaue)`, or if no value was specified it assumes the level of infomration is `1`. See section: *Control Level of Information to log out* for more information. The `printHeader()` function just helps with readability by printing a header in the console like this. It can be used for printing for example the title of the testing set. Here the expected result under `1` level of information: From 91bddea08b887e047aac571dac1f7de8d424926a Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 4 Oct 2021 12:54:44 -0400 Subject: [PATCH 07/27] Update UnitTestingApp.js Added the changes I mentioned in my last post. I included also how to treat undefined input argument for `assertEquals` only, waiting for your feedback --- UnitTestingApp.js | 84 +++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index 3b913ff..fe46609 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -10,12 +10,12 @@ let UnitTestingApp = (function () { - _nTests = 0; // Total number of tests executed - _nFailTests = 0; // Total test failed - _nPassTests = 0; // Total test passed - _levelInfo = 1; // Level of information to show in the output (0-summary, 1-trace and test result information) const _enabled = new WeakMap(); const _runningInGas = new WeakMap(); + const _nTests = new WeakMap(); // Total number of tests executed + const _nFailTests = new WeakMap(); // Total test failed + const _nPassTests = new WeakMap(); // Total test passed + const _levelInfo = new WeakMap(); // Level of information to show in the output (0-summary, 1-trace and test result information) class UnitTestingApp { constructor() { @@ -23,10 +23,10 @@ let UnitTestingApp = (function () { _enabled.set(this, false); _runningInGas.set(this, false); - this._nTests = 0; - this._nFailTests = 0; - this._nPassTests = 0; - this._levelInfo = 1; + _levelInfo.set(this, 1); + _nTests.set(this, 0); + _nFailTests.set(this, 0); + _nPassTests.set(this, 0); UnitTestingApp.instance = this; return UnitTestingApp.instance; @@ -61,17 +61,17 @@ let UnitTestingApp = (function () { } getLevelInfo() { - return this._levelInfo; + return _levelInfo.get(this); } setLevelInfo(value) { - this._levelInfo = value; + _levelInfo.set(this, value); } resetTestCounters() { - this._nFailTests = 0; - this._nPassTests = 0; - this._nTests = 0; + _nTests.set(this, 0); + _nFailTests.set(this, 0); + _nPassTests.set(this, 0); } /** @@ -83,27 +83,27 @@ let UnitTestingApp = (function () { assert(condition, message) { if (!_enabled.get(this)) return; if (this.isInGas !== this.runningInGas) return; - this._nTests++; + _nTests.set(this, _nTests.get(this) + 1); try { if ("function" === typeof condition) condition = condition(); if (condition) { - this._nPassTests++; - if (this._levelInfo > 0) { console.log(`✔ PASSED: ${message}`) }; + _nPassTests.set(this, _nPassTests.get(this) + 1); + if (this.getLevelInfo() > 0) { console.log(`✔ PASSED: ${message}`) }; } else { - this._nFailTests++; - if (this._levelInfo > 0) { console.log(`❌ FAILED: ${message}`) }; + _nFailTests.set(this, _nFailTests.get(this) + 1); + if (this.getLevelInfo() > 0) { console.log(`❌ FAILED: ${message}`) }; } } catch (err) { - this._nFailTests++; - if (this._levelInfo > 0) { console.log(`❌ FAILED: ${message} (${err})`) }; + _nFailTests.set(this, _nFailTests.get(this) + 1); + if (this.getLevelInfo() > 0) { console.log(`❌ FAILED: ${message} (${err})`) }; } } /** * Tests whether fun result is equal to expected result or not - * @param {Function} fun - The function to evaluate + * @param {Boolean | Function} Condition or fun - to check * @param {String} expectedResult - The expected result to validate * @param {String} message - If present, then used as message to display in the console (based on _levelInfo value). * If message is not present, then in case the result is not equal, it shows the missmatch @@ -114,30 +114,35 @@ let UnitTestingApp = (function () { assertEquals(fun, expectedResult, message = null) { if (!_enabled.get(this)) return; if (this.isInGas !== this.runningInGas) return; - this._nTests++; + _nTests.set(this, _nTests.get(this) + 1); let msg, result; try { - if ("function" === typeof fun) { - result = fun(); - } - let condition = expectedResult == result; + // Checking preconditions + if(fun === undefined) throw new Error("Provide 'fun' input argument"); + if(expectedResult === undefined) throw new Error("Provide 'expectedResult' input argument"); + + ("function" === typeof fun) ? result = fun() : result = fun; + let condition = expectedResult === result; if (condition) { - this._nPassTests++; + _nPassTests.set(this, _nPassTests.get(this) + 1); msg = (message == null) ? "" : ": " + message; - if (this._levelInfo >= 1) { console.log(`✔ PASSED${msg}`) }; + if (this.getLevelInfo() >= 1) console.log(`✔ PASSED${msg}`); } else { - this._nFailTests++; + _nFailTests.set(this, _nFailTests.get(this) + 1); msg = (message == null) ? result + " != " + expectedResult : message; - if (this._levelInfo >= 1) { console.log(`❌ FAILED: ${msg}`) }; + if (this.getLevelInfo() >= 1) console.log(`❌ FAILED: ${msg}`); } } catch (err) { - this._nFailTests++; - this.errorMsg = result + " != " + expectedResult; - msg = (message == null) ? result + " != " + expectedResult : message; - if (this._levelInfo >= 1) { console.log(`❌ FAILED(err): ${msg} (${err})`) }; + _nFailTests.set(this, _nFailTests.get(this) + 1); + if ((fun === undefined) || (expectedResult === undefined)) { + msg =""; + } else { + msg = (message == null) ? result + " != " + expectedResult : message; + } + if (this.getLevelInfo() >= 1) console.log(`❌ FAILED(err): ${msg} (${err})`); } } @@ -183,7 +188,7 @@ let UnitTestingApp = (function () { printHeader(text) { if (!_enabled.get(this)) return; if (this.isInGas !== this.runningInGas) return; - if (_levelInfo > 0) { + if (this.getLevelInfo() > 0) { console.log('*********************'); console.log('* ' + text) console.log('*********************'); @@ -192,16 +197,17 @@ let UnitTestingApp = (function () { printSubHeader(text) { if (!_enabled.get(this)) return; - if (this._levelInfo > 0) { console.log('** ' + text) }; + if (this.getLevelInfo() > 0) { console.log('** ' + text) }; } printSummary() { if (!_enabled.get(this)) return; if (this.isInGas !== this.runningInGas) return; - if (this._levelInfo > 0) { - console.log('TOTAL TESTS= ' + this._nTests + ', ❌ FAILED=' + this._nFailTests + ', ✔ PASSED=' + this._nPassTests); + if (this.getLevelInfo() > 0) { + console.log('TOTAL TESTS= ' + _nTests.get(this) + ', ❌ FAILED=' + + _nFailTests.get(this) + ', ✔ PASSED=' + _nPassTests.get(this)); } - console.log((this._nFailTests == 0) ? "ALL TESTS ✔ PASSED" : "❌ Some Tests Failed"); + console.log((_nFailTests.get(this) == 0) ? "ALL TESTS ✔ PASSED" : "❌ Some Tests Failed"); } /** From fdb788e3666e86906f19d92b92c8bfb824fa0a56 Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 4 Oct 2021 13:41:00 -0400 Subject: [PATCH 08/27] Update TestForVersion_0.1.1.js Updated the file, with additional tests, including when input argument is a condition (not a funcion) and when input argument are `undefined` --- Example/TestForVersion_0.1.1.js | 38 ++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/Example/TestForVersion_0.1.1.js b/Example/TestForVersion_0.1.1.js index aabde85..394a438 100644 --- a/Example/TestForVersion_0.1.1.js +++ b/Example/TestForVersion_0.1.1.js @@ -35,14 +35,28 @@ function runTests() { // Testing new method: assertEquals, printSummary and printSubHeader using addTwoValue function with default levelInfo // We haven´t set levelInfo, so we are using the default value: 1 test.printSubHeader("Using default Level Info value (1) for 'addTwoValues' function"); + test.printSubHeader("Test 1 passes, Test 2 passes with message, Test 3 fails, Test 4 fails with custom message"); test.assertEquals(() => addTwoValues(1,2),3); // PASS test.assertEquals(() => addTwoValues(2,2),4, "Valid case: 2 + 2 = 4"); // PASS with message test.assertEquals(() => addTwoValues(1,2),4); // FAIL test.assertEquals(() => addTwoValues(1,2),4, "Expected to fail, because 1+2 != 4"); test.printSummary(); // It should print final result and statistics (two lines) + test.printSubHeader("Testing when fun is boolean"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.printSubHeader("Test-1 Pass, Test-2 Fail"); + test.assertEquals(1 + 2 == 3, true); + test.assertEquals(1 + 1 == 3, true); + test.printSummary(); + test.printSubHeader("Testing undefined input arguments"); + test.assertEquals(); + test.assertEquals(1+1); + test.printSubHeader("Expected to see 4-Tests, 1-Pass and 3-Fail"); + test.printSummary(); + // Testing the same tests but in silent mode (LevelInfo = 0) - test.printSubHeader("Testing the output setting levelInfo = 0"); // It logs out, because we haven't changed LevelInfo yet + test.printSubHeader("Testing the same tests with setting levelInfo = 0"); // It logs out, because we haven't changed LevelInfo yet test.setLevelInfo(0); // 0-Only summary result, 1-Detail results // Because LevelInfo = 0 we use the console for traceability purpose console.log("LevelInfo = " + test.getLevelInfo()); @@ -51,21 +65,39 @@ function runTests() { test.assertEquals(() => addTwoValues(1,2),4); test.assertEquals(() => addTwoValues(1,2),4, "Expected to fail, because 1+2 != 4"); test.printSummary(); // Only summary result: Some test failed - console.log("Reset counters"); + test.printSubHeader("Reset counters"); test.resetTestCounters(); console.log("Shows only the summary line, the counters are reseted so no tests, therefore all test passed"); test.printSummary(); //Shows only summary line // Testing the existing assert function work as expected - test.printHeader("Testing addTwoValues using assert"); test.printSubHeader('Default level Info'); test.setLevelInfo(1); + test.printHeader("Testing addTwoValues using assert"); test.printSubHeader("LevelInfo = " + test.getLevelInfo()); + test.printSubHeader("Test-1 Pass, Test-2 Fail"); test.assert(() => addTwoValues(1,2) == 3, "Valid case: 1 + 2 = 3"); test.assert(() => addTwoValues(1,2) == 4, "Invalid case: 1 + 2 != 4"); test.printSubHeader("Expected to log out two lines: statistics and summary"); test.printSummary(); + test.printSubHeader("Testing when condition is boolean"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.printSubHeader("Test-1 Pass, Test-2 Fail"); + test.assert(1 + 2 == 3, "Valid case: 1 + 2 = 3"); + test.assert(1 + 1 == 3, "Invalid case: 1 + 1 != 3"); + test.printSummary(); + + test.printSubHeader("Testing undefined input arguments"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.printSubHeader("Expected to see 3-Tests. Test-1 Pass, Test-2 Pass and Test-3 Fail"); + test.assert(); + test.assert(1+1==2); + test.assert(1+1==3); + test.printSummary(); + test.printSubHeader("Testing under silent mode: one test passed, one failed"); test.setLevelInfo(0); console.log("LevelInfo = " + test.getLevelInfo()); From f8edc326892f9a71d0482f7f331d578c67fe712e Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 4 Oct 2021 16:29:15 -0400 Subject: [PATCH 09/27] Update UnitTestingApp.js Removed unnecessary curly bracket under assert. --- UnitTestingApp.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index fe46609..41bc947 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -88,16 +88,16 @@ let UnitTestingApp = (function () { if ("function" === typeof condition) condition = condition(); if (condition) { _nPassTests.set(this, _nPassTests.get(this) + 1); - if (this.getLevelInfo() > 0) { console.log(`✔ PASSED: ${message}`) }; + if (this.getLevelInfo() > 0) console.log(`✔ PASSED: ${message}`); } else { _nFailTests.set(this, _nFailTests.get(this) + 1); - if (this.getLevelInfo() > 0) { console.log(`❌ FAILED: ${message}`) }; + if (this.getLevelInfo() > 0) console.log(`❌ FAILED: ${message}`); } } catch (err) { _nFailTests.set(this, _nFailTests.get(this) + 1); - if (this.getLevelInfo() > 0) { console.log(`❌ FAILED: ${message} (${err})`) }; + if (this.getLevelInfo() > 0) console.log(`❌ FAILED: ${message} (${err})`); } } From 59dfd7dc095fb45740edf798668fa1c9549bd71c Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 4 Oct 2021 17:11:43 -0400 Subject: [PATCH 10/27] Update UnitTestingApp.js Simplified the catch block for assertEquals. --- UnitTestingApp.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index 41bc947..1032047 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -137,11 +137,7 @@ let UnitTestingApp = (function () { } catch (err) { _nFailTests.set(this, _nFailTests.get(this) + 1); - if ((fun === undefined) || (expectedResult === undefined)) { - msg =""; - } else { - msg = (message == null) ? result + " != " + expectedResult : message; - } + msg = (msg === undefined) ? "" : ((message == null) ? result + " != " + expectedResult : message); if (this.getLevelInfo() >= 1) console.log(`❌ FAILED(err): ${msg} (${err})`); } } From 128c5044b1c646844118334029a7b0a4c99bc040 Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 4 Oct 2021 22:27:20 -0400 Subject: [PATCH 11/27] Update UnitTestingApp.js Removing curly bracket from printSubHeader, printSummary. Added documentation for resetTestCounters and printSummary, --- UnitTestingApp.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index 1032047..0f044c9 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -68,6 +68,10 @@ let UnitTestingApp = (function () { _levelInfo.set(this, value); } + /** + * Reset statistics counters + * @return {void} + */ resetTestCounters() { _nTests.set(this, 0); _nFailTests.set(this, 0); @@ -119,8 +123,8 @@ let UnitTestingApp = (function () { try { // Checking preconditions - if(fun === undefined) throw new Error("Provide 'fun' input argument"); - if(expectedResult === undefined) throw new Error("Provide 'expectedResult' input argument"); + if (fun === undefined) throw new Error("Provide 'fun' input argument"); + if (expectedResult === undefined) throw new Error("Provide 'expectedResult' input argument"); ("function" === typeof fun) ? result = fun() : result = fun; let condition = expectedResult === result; @@ -193,16 +197,21 @@ let UnitTestingApp = (function () { printSubHeader(text) { if (!_enabled.get(this)) return; - if (this.getLevelInfo() > 0) { console.log('** ' + text) }; + if (this.getLevelInfo() > 0) console.log('** ' + text); } + /** + * Logs out testing summary, If _levelInfo is > 0, informs about total tests, number of failed tests and passed tests + * and in a second line indicating all test passed if no test failed otherwise indicating some test failed. + * If _levelInfo <= 0, logs out only the content of the second line. + * @return {void} + */ printSummary() { if (!_enabled.get(this)) return; if (this.isInGas !== this.runningInGas) return; - if (this.getLevelInfo() > 0) { - console.log('TOTAL TESTS= ' + _nTests.get(this) + ', ❌ FAILED=' - + _nFailTests.get(this) + ', ✔ PASSED=' + _nPassTests.get(this)); - } + let msg = "TOTAL TESTS=%d, ❌ FAILED=%d, ✔ PASSED=%d"; + if (this.getLevelInfo() > 0) console.log(Utilities.formatString(msg, _nTests.get(this), + _nFailTests.get(this), _nPassTests.get(this))); console.log((_nFailTests.get(this) == 0) ? "ALL TESTS ✔ PASSED" : "❌ Some Tests Failed"); } From f87a9391583e175072d63bdab250bf9d54c101c0 Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 4 Oct 2021 23:16:15 -0400 Subject: [PATCH 12/27] Update README.md Minor corrections. --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f5595d9..91454c3 100644 --- a/README.md +++ b/README.md @@ -51,13 +51,13 @@ Then we have the actual built-in testing methods, `assert()`, `catchErr()` and ` ### Control Level of Information to log out -The function `setLevelInfo(value)` controls the level information to be reported through the console. If `value`is equal to `0`, it runs in silent mode, i.e. no information will be reported. The only exception from print-family functions is `printSummary()` that under this mode just logs out a single summary line. For example if no errors found, the output will be: +The function `setLevelInfo(value)` controls the level information to be reported through the console. If `value`is less or equal than `0`, it runs in silent mode, i.e. no information will be reported. The only exception from print-family functions is `printSummary()` that under this mode just logs out a single summary line. For example if no errors found, the output will be: ```ALL TESTS ✔ PASSED``` if at least one test failed then it logs out the following information: -```❌ Some Tests Failed``` +```❌ Some Tests FAILED``` This setup is usufull for large tests where we just want to add some incremental test and to have just a minimal output about the overall testing result. @@ -66,7 +66,7 @@ Here is how the level of information can be specified for silent mode: ```javascript test.setLevelInfo(0); ``` -If value is `1`(default value) it means trace information will log out the result per each test, indicating if the test fails or passed. Depending on the specific testing function it will log out different information, for example, let's says we want to test the following custom function: +If the value is `1`(default value) or greater, it means trace information will log out the result per each test, indicating if the test failed or passed. Depending on the specific testing function it will log out different information, for example, let's says we want to test the following custom function: ```javascript function addTwoValues (a, b) { @@ -98,7 +98,7 @@ will return ❌ FAILED: 3 != 4 ``` -As you can see there is no need to specify the message, it shows where is the problem. If we invoke `assertEquals` including its optional input argument `message`, then the output information will be on each case the information specificied by this input argument +As you can see, there is no need to specify the message, it shows where is the problem. If we invoke `assertEquals` including its optional input argument `message`, then the output information will be on each case the information specificied by this input argument ```javascript test.assertEquals(() => addTwoValues(1,2),3, "Expected result: 1+2 = 3"); @@ -114,7 +114,7 @@ The output will be: If we are using `assert` that has a mandatory `message`as input argument, the message will be printed as testing result. -When using `printSummary()` with level info is equal to `1`, it logs out an additional line providing statistics about testing results: +When using `printSummary()` with level info is equal or greater than `1`, it logs out an additional line providing statistics about testing results: ``` TOTAL TESTS= 1, ❌ FAILED=0, ✔ PASSED=1 @@ -136,14 +136,14 @@ test.assert(() => num > 10, `Number ${num} is bigger than 10`); // logs out FAILED: Number 2 is bigger than 10 ``` ### assertEquals(fun, expectedResult, message) -`assertEquals`is another assert function, that helps to verify the result with respect the expected value. The first argument is the function we would like to evaluate, the second argument is the expected result. If returned value of the `fun`is not equal to `expectedResult` it fails, otherwise passed. Here is how to validate javascript standard `max()`function: +`assertEquals`is another assert function, that helps to verify the result with respect the expected value. The first argument is the function or condition we would like to evaluate, the second argument is the expected result. If returned value of the `fun`is not equal to `expectedResult` it fails, otherwise passed. Here is how to validate javascript standard `max()`function: ```javascript test.assertEquals(() => Math.max(1,2),2); # Expected result is 2 (equal to actual result) test.assertEquals(() => Math.max(1,2),1); # Expected result is 1 (different than actual result) -test.assertEquals(() => Math.max(1,2),2, "Correct result the max of 1,2 is 2"); # Using option input argument message +test.assertEquals(() => Math.max(1,2),2, "Correct result the max of 1,2 is 2"); # Using optional input argument message ``` -The last case specifies the message to log out as testing result regardless of the test result, the same message will be log out. +The last case specifies the message to log out regardless of the test result, i.e. the same message will be log out whether it failed or passed. ### catchErr(condition, expectedErrorMessage, message) @@ -211,11 +211,11 @@ test.printSummary(); If we ran 20 tests, where there is one failed test, under level of information equal to `1`, the result will be: ``` TOTAL TESTS= 20, ❌ FAILED=1, ✔ PASSED=19 -ALL TESTS ✔ PASSED +❌ Some Tests FAILED ``` Similarly if the level of information is equal to `0`, the output will be: ``` -ALL TESTS ✔ PASSED +❌ Some Tests FAILED ``` ### resetTestCounters() From 6fabb5def4785f04429bc298856b559738c27d3f Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 4 Oct 2021 23:17:43 -0400 Subject: [PATCH 13/27] Update UnitTestingApp.js Changed in printSummary the output "Some Tests Failed" to "Some Tests FAILED". --- UnitTestingApp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index 0f044c9..e752516 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -212,7 +212,7 @@ let UnitTestingApp = (function () { let msg = "TOTAL TESTS=%d, ❌ FAILED=%d, ✔ PASSED=%d"; if (this.getLevelInfo() > 0) console.log(Utilities.formatString(msg, _nTests.get(this), _nFailTests.get(this), _nPassTests.get(this))); - console.log((_nFailTests.get(this) == 0) ? "ALL TESTS ✔ PASSED" : "❌ Some Tests Failed"); + console.log((_nFailTests.get(this) == 0) ? "ALL TESTS ✔ PASSED" : "❌ Some Tests FAILED"); } /** From 6deff219dfa55f50499ce91acb8958762169bba4 Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 11 Oct 2021 15:37:51 -0400 Subject: [PATCH 14/27] Update UnitTestingApp.js Removed the condition for throwing an error in case no mandatory input arguments are provided. --- UnitTestingApp.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index e752516..4151bf1 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -122,10 +122,6 @@ let UnitTestingApp = (function () { let msg, result; try { - // Checking preconditions - if (fun === undefined) throw new Error("Provide 'fun' input argument"); - if (expectedResult === undefined) throw new Error("Provide 'expectedResult' input argument"); - ("function" === typeof fun) ? result = fun() : result = fun; let condition = expectedResult === result; if (condition) { @@ -141,7 +137,7 @@ let UnitTestingApp = (function () { } catch (err) { _nFailTests.set(this, _nFailTests.get(this) + 1); - msg = (msg === undefined) ? "" : ((message == null) ? result + " != " + expectedResult : message); + (message == null) ? result + " != " + expectedResult : message; if (this.getLevelInfo() >= 1) console.log(`❌ FAILED(err): ${msg} (${err})`); } } From 8f7c69a0c37bec555e1e622e3014e2de333202f7 Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 11 Oct 2021 15:39:03 -0400 Subject: [PATCH 15/27] Update TestForVersion_0.1.1.js Added more tests for undefined input values --- Example/TestForVersion_0.1.1.js | 69 ++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/Example/TestForVersion_0.1.1.js b/Example/TestForVersion_0.1.1.js index 394a438..bdcb0f2 100644 --- a/Example/TestForVersion_0.1.1.js +++ b/Example/TestForVersion_0.1.1.js @@ -8,7 +8,7 @@ if (typeof require !== 'undefined') { } // Function to test -function addTwoValues (a, b) { +function addTwoValues(a, b) { return a + b; } @@ -26,7 +26,7 @@ function runTests() { test.enable(); test.clearConsole(); test.runInGas(true); - + /************************ * Run Local Tests Here ************************/ @@ -35,23 +35,26 @@ function runTests() { // Testing new method: assertEquals, printSummary and printSubHeader using addTwoValue function with default levelInfo // We haven´t set levelInfo, so we are using the default value: 1 test.printSubHeader("Using default Level Info value (1) for 'addTwoValues' function"); - test.printSubHeader("Test 1 passes, Test 2 passes with message, Test 3 fails, Test 4 fails with custom message"); - test.assertEquals(() => addTwoValues(1,2),3); // PASS - test.assertEquals(() => addTwoValues(2,2),4, "Valid case: 2 + 2 = 4"); // PASS with message - test.assertEquals(() => addTwoValues(1,2),4); // FAIL - test.assertEquals(() => addTwoValues(1,2),4, "Expected to fail, because 1+2 != 4"); + test.printSubHeader("Expected: Test 1 passes, Test 2 passes with message, Test 3 fails, Test 4 fails with custom message"); + test.assertEquals(() => addTwoValues(1, 2), 3); // PASS + test.assertEquals(() => addTwoValues(2, 2), 4, "Valid case: 2 + 2 = 4"); // PASS with message + test.assertEquals(() => addTwoValues(1, 2), 4); // FAIL + test.assertEquals(() => addTwoValues(1, 2), 4, "Expected to fail, because 1+2 != 4"); test.printSummary(); // It should print final result and statistics (two lines) test.printSubHeader("Testing when fun is boolean"); - test.printSubHeader("Reset counters"); + test.printSubHeader("Reset counters"); test.resetTestCounters(); - test.printSubHeader("Test-1 Pass, Test-2 Fail"); + test.printSubHeader("Expected Test-1 Pass, Test-2 Fail"); test.assertEquals(1 + 2 == 3, true); test.assertEquals(1 + 1 == 3, true); test.printSummary(); test.printSubHeader("Testing undefined input arguments"); - test.assertEquals(); - test.assertEquals(1+1); - test.printSubHeader("Expected to see 4-Tests, 1-Pass and 3-Fail"); + test.printSubHeader("Expected: Test-3 Pass, Test-4 Fail, Test-5 Pass, Test-6 Fail"); + test.assertEquals(); // PASS both are undefined + test.assertEquals(1 + 1); // FAIL, because the expectedValue is undefined + test.assertEquals(1 + 1, 2, undefined); // Pass but message is undefined + test.assertEquals(1 + 1, 3, undefined); // Fail with undefined message (default is null, so the message doesn't show) + test.printSubHeader("Expected: 6-Tests, 3-Pass and 3-Fail"); test.printSummary(); @@ -60,10 +63,10 @@ function runTests() { test.setLevelInfo(0); // 0-Only summary result, 1-Detail results // Because LevelInfo = 0 we use the console for traceability purpose console.log("LevelInfo = " + test.getLevelInfo()); - test.assertEquals(() => addTwoValues(1,2),3); - test.assertEquals(() => addTwoValues(2,2),4, "Valid case: 2 + 2 = 4"); - test.assertEquals(() => addTwoValues(1,2),4); - test.assertEquals(() => addTwoValues(1,2),4, "Expected to fail, because 1+2 != 4"); + test.assertEquals(() => addTwoValues(1, 2), 3); + test.assertEquals(() => addTwoValues(2, 2), 4, "Valid case: 2 + 2 = 4"); + test.assertEquals(() => addTwoValues(1, 2), 4); + test.assertEquals(() => addTwoValues(1, 2), 4, "Expected to fail, because 1+2 != 4"); test.printSummary(); // Only summary result: Some test failed test.printSubHeader("Reset counters"); test.resetTestCounters(); @@ -72,12 +75,12 @@ function runTests() { // Testing the existing assert function work as expected test.printSubHeader('Default level Info'); - test.setLevelInfo(1); + test.setLevelInfo(1); test.printHeader("Testing addTwoValues using assert"); test.printSubHeader("LevelInfo = " + test.getLevelInfo()); test.printSubHeader("Test-1 Pass, Test-2 Fail"); - test.assert(() => addTwoValues(1,2) == 3, "Valid case: 1 + 2 = 3"); - test.assert(() => addTwoValues(1,2) == 4, "Invalid case: 1 + 2 != 4"); + test.assert(() => addTwoValues(1, 2) == 3, "Valid case: 1 + 2 = 3"); + test.assert(() => addTwoValues(1, 2) == 4, "Invalid case: 1 + 2 != 4"); test.printSubHeader("Expected to log out two lines: statistics and summary"); test.printSummary(); @@ -90,32 +93,34 @@ function runTests() { test.printSummary(); test.printSubHeader("Testing undefined input arguments"); - test.printSubHeader("Reset counters"); - test.resetTestCounters(); - test.printSubHeader("Expected to see 3-Tests. Test-1 Pass, Test-2 Pass and Test-3 Fail"); - test.assert(); - test.assert(1+1==2); - test.assert(1+1==3); + test.printSubHeader("Expected: Test-3 Fail (no arguments), Test-4 Pass (with undefined message), Test-5 Pass(with undefined message), Test-6 Pass(with undefined message)"); + test.assert(); // Expected to fail, not a valid condition + test.assert(undefined == undefined); // Pass + test.assert(1 + 1 == 2); + test.assert(1 + 1 == 3); + test.printSubHeader("Expected: 6-Tests, Fail=3, Pass=3"); test.printSummary(); test.printSubHeader("Testing under silent mode: one test passed, one failed"); - test.setLevelInfo(0); + test.setLevelInfo(0); console.log("LevelInfo = " + test.getLevelInfo()); - test.assert(() => addTwoValues(1,2) == 3, "Valid case: 1 + 2 = 3"); - test.assert(() => addTwoValues(1,2) == 4, "Invalid case: 1 + 2 != 4"); + console.log("Reseting the counters"); + test.resetTestCounters(); + test.assert(() => addTwoValues(1, 2) == 3, "Valid case: 1 + 2 = 3"); + test.assert(() => addTwoValues(1, 2) == 4, "Invalid case: 1 + 2 != 4"); console.log("Expected to see some test failed"); test.printSummary(); // Reset the counters. For testing all test passed under silent mode console.log("Testing the case all test passed with silent mode"); console.log("Reseting the counters"); - test.resetTestCounters(); - console.log('Testing with assert, under silent mode: one test executed and passed'); - test.assert(() => addTwoValues(1,2) == 3, "Valid case"); + test.resetTestCounters(); + console.log('Testing with assert, under silent mode: one test executed and passed'); + test.assert(() => addTwoValues(1, 2) == 3, "Valid case"); console.log("Printing the summary line only: all test passed"); test.printSummary(); console.log("Changing the level info to 1"); - test.setLevelInfo(1); + test.setLevelInfo(1); test.printSubHeader("Showing now printSummary with two lines for the previous set of tests"); test.printSummary(); From 69a6d2f686d688f21d80857474c361dc43ebc38c Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 11 Oct 2021 16:11:47 -0400 Subject: [PATCH 16/27] Update README.md Adding a comment about `=>` arrow function in `catchErr section. --- README.md | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 91454c3..dacf2f4 100644 --- a/README.md +++ b/README.md @@ -152,15 +152,28 @@ The goal of this method is to test whether your callback function (`callback`) c ```javascript function square(number) { - if (typeof number !== ‘number’) throw new Error(‘Argument must be a number’); + if ("number" != typeof number) throw new Error("Argument must be a number"); return number * number; } test.catchErr( - () => square(‘a string’), // we’re passing a string here to test that our function throws an error - ‘Argument must be a number’, // this is the error message we are expecting - ‘We caught the type error correctly’ -); + () => square("a string"), // we’re passing a string here to test that our function throws an error + "Argument must be a number", // this is the error message we are expecting + "We caught the type error correctly" + ); +``` +**Note:** Even Though you can invoke both asserts functions (`assert`, `assertEquals`) not using the `=>` (arrow function), for example: +```javascript +test.assertEquals(square(2), 4); +test.assert(square(2)==4, "Valid case sqrt(2) is equal to 4"); +``` +We don't recommend invoking the functions like this because `catchErr()` function will require arrow function invokation. The following code will produce an execution error on the first line of the body of `sqrt` function. +```javascript +test.catchErr( + square("a string"), // we’re passing a string here to test that our function throws an error + "Argument must be a number", // this is the error message we are expecting + "We caught the type error correctly" + ); ``` ### is2dArray(Array) From b7a5256498086b305eab1b86daddd2eee40e0945 Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 11 Oct 2021 23:01:29 -0400 Subject: [PATCH 17/27] Update UnitTestingApp.js In catchErr the local variable error is never used, removed the declaration and the line error = err, because then the variable error is not used. --- UnitTestingApp.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index 4151bf1..9018cbf 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -153,12 +153,10 @@ let UnitTestingApp = (function () { catchErr(callback, errorMessage, message) { if (!_enabled.get(this)) return; if (this.isInGas !== this.runningInGas) return; - let error; let isCaught = false; try { callback(); } catch (err) { - error = err; isCaught = new RegExp(errorMessage).test(err); } finally { this.assert(isCaught, message); From 62e86bee88a47187d3ed0b6354020a3eecf605f1 Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 11 Oct 2021 23:05:18 -0400 Subject: [PATCH 18/27] Update TestForVersion_0.1.1.js Added specific tests for testing catchErr, considering both scenarios: 1) When the error message matches and 2) When the error message doesn't match --- Example/TestForVersion_0.1.1.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Example/TestForVersion_0.1.1.js b/Example/TestForVersion_0.1.1.js index bdcb0f2..44d9373 100644 --- a/Example/TestForVersion_0.1.1.js +++ b/Example/TestForVersion_0.1.1.js @@ -12,6 +12,12 @@ function addTwoValues(a, b) { return a + b; } +// Function to test in case an error is thrown +function addTwoValuesSafe(a, b) { + if(("number" != typeof a) || ("number" != typeof b)) throw new Error("Input argument is not a valid number"); + return addTwoValues(a,b); +} + /***************** * TESTS * Taking the sources files from: https://github.com/WildH0g/UnitTestingApp @@ -57,12 +63,30 @@ function runTests() { test.printSubHeader("Expected: 6-Tests, 3-Pass and 3-Fail"); test.printSummary(); + test.printSubHeader("Testing catching errors"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.printSubHeader("Expected to fail, becuase the expected error doesn´t match"); + test.catchErr( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + "Argument must be a number", // this is the error message we are expecting + "We caught the error, but with the wrong error message" + ); + test.printSubHeader("Expected to pass, because the expected error matches"); + test.catchErr( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + "Input argument is not a valid number", // this is the error message we are expecting + "We caught the error message correctly" + ); + test.printSummary(); // Testing the same tests but in silent mode (LevelInfo = 0) test.printSubHeader("Testing the same tests with setting levelInfo = 0"); // It logs out, because we haven't changed LevelInfo yet test.setLevelInfo(0); // 0-Only summary result, 1-Detail results // Because LevelInfo = 0 we use the console for traceability purpose console.log("LevelInfo = " + test.getLevelInfo()); + console.log("Reset counters"); + test.resetTestCounters(); test.assertEquals(() => addTwoValues(1, 2), 3); test.assertEquals(() => addTwoValues(2, 2), 4, "Valid case: 2 + 2 = 4"); test.assertEquals(() => addTwoValues(1, 2), 4); From 06d2a5b3208b5a027b8049202f3921fb07e54d23 Mon Sep 17 00:00:00 2001 From: dlealv Date: Mon, 11 Oct 2021 23:08:35 -0400 Subject: [PATCH 19/27] Update README.md minor corrections --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dacf2f4..80a645e 100644 --- a/README.md +++ b/README.md @@ -162,12 +162,12 @@ test.catchErr( "We caught the type error correctly" ); ``` -**Note:** Even Though you can invoke both asserts functions (`assert`, `assertEquals`) not using the `=>` (arrow function), for example: +**Note:** Even though you can invoke both asserts functions (`assert`, `assertEquals`) not using the `=>` (arrow function), for example: ```javascript test.assertEquals(square(2), 4); test.assert(square(2)==4, "Valid case sqrt(2) is equal to 4"); ``` -We don't recommend invoking the functions like this because `catchErr()` function will require arrow function invokation. The following code will produce an execution error on the first line of the body of `sqrt` function. +We don't recommend invoking like this, because `catchErr()` function will require arrow function invokation. The following code will produce an execution error on the first line of the body of `sqrt` function. ```javascript test.catchErr( square("a string"), // we’re passing a string here to test that our function throws an error From 0e9f25b7c589154dc1a7c180aae0a4edeff393cc Mon Sep 17 00:00:00 2001 From: dlealv Date: Tue, 12 Oct 2021 20:22:08 -0400 Subject: [PATCH 20/27] Update UnitTestingApp.js In is2dArray inverted the typeof checking as it is in other functions of the library. --- UnitTestingApp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index 9018cbf..33853ec 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -172,7 +172,7 @@ let UnitTestingApp = (function () { if (!_enabled.get(this)) return; if (this.isInGas !== this.runningInGas) return; try { - if (typeof array === 'function') array = array(); + if ('function' === typeof array) array = array(); this.assert(Array.isArray(array) && Array.isArray(array[0]), message); } catch (err) { this.assert(false, `${message}: ${err}`); From f5de05f4dbe8741087bed6a4b0b833897db62987 Mon Sep 17 00:00:00 2001 From: dlealv Date: Sat, 23 Oct 2021 17:23:03 -0400 Subject: [PATCH 21/27] Update UnitTestingApp.js Added a minor improvement for `catchErr` to consider the input argument `message`optional, because the message can be built automatically if the user doesn't provide it. --- UnitTestingApp.js | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index 33853ec..d1deb57 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -147,10 +147,11 @@ let UnitTestingApp = (function () { * Tests functions that throw error messages * @param {Function} callback - the function that you expect to return the error message * @param {String} errorMessage - the error message you are expecting - * @param {String} message - the message to display in the console + * @param {String} message - the message to display in the console. If null (default) a message is built as follow in case of fail + * Wrong error message: 'Caugh error message' != errorMessage * @return {void} */ - catchErr(callback, errorMessage, message) { + catchErr(callback, errorMessage, message=null) { if (!_enabled.get(this)) return; if (this.isInGas !== this.runningInGas) return; let isCaught = false; @@ -158,6 +159,36 @@ let UnitTestingApp = (function () { callback(); } catch (err) { isCaught = new RegExp(errorMessage).test(err); + message = (!isCaught && (message == null)) ? `Wrong error message: '${errorMessage}' != '${err.message}'` : message; + } finally { + this.assert(isCaught, message); + } + } + + /** + * Tests functions that throw error (message and type of error) + * @param {Function} callback - the function that you expect to return the error message + * @param {Type} errorType - the error type you are expecting + * @param {String} errorMessage - the error message you are expecting + * @param {String} message - the message to display in the console. If null (default value), in case of fail + * It builds a predefined message as follow. In case of wrong error type: + * Wrong error type: 'errorType' != 'CaughErrorType' + *. In case of wrong error message: Wrong error message: 'Caugh error message' != 'errorMessage' + * @return {void} + */ + catchErrType(callback, errorType, errorMessage, message = null) { + if (!_enabled.get(this)) return; + if (this.isInGas !== this.runningInGas) return; + let isCaught = false; + try { + callback(); + } catch (err) { + if (err instanceof errorType) { + isCaught = new RegExp(errorMessage).test(err); + message = (!isCaught && (message == null)) ? `Wrong error message: '${errorMessage}' != '${err.message}'` : message; + } else { + message = (message == null) ? `Wrong error type: '${errorType.name}' != '${err.name}'` : message; + } } finally { this.assert(isCaught, message); } From 96eef6eb9a8b982061c5f93697eed0fd1037fe17 Mon Sep 17 00:00:00 2001 From: dlealv Date: Sat, 23 Oct 2021 17:24:51 -0400 Subject: [PATCH 22/27] Update TestForVersion_0.1.1.js Added the corresponding tests for `catchErrType` and `catchErr`. --- Example/TestForVersion_0.1.1.js | 70 ++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/Example/TestForVersion_0.1.1.js b/Example/TestForVersion_0.1.1.js index 44d9373..1d85e8f 100644 --- a/Example/TestForVersion_0.1.1.js +++ b/Example/TestForVersion_0.1.1.js @@ -14,8 +14,8 @@ function addTwoValues(a, b) { // Function to test in case an error is thrown function addTwoValuesSafe(a, b) { - if(("number" != typeof a) || ("number" != typeof b)) throw new Error("Input argument is not a valid number"); - return addTwoValues(a,b); + if (("number" != typeof a) || ("number" != typeof b)) throw new Error("Input argument is not a valid number"); + return addTwoValues(a, b); } /***************** @@ -37,6 +37,9 @@ function runTests() { * Run Local Tests Here ************************/ + let t ="test"; + console.log(`this is '${t}'`); + test.printHeader("Testing addTwoValues using assertEquals"); // Testing new method: assertEquals, printSummary and printSubHeader using addTwoValue function with default levelInfo // We haven´t set levelInfo, so we are using the default value: 1 @@ -63,23 +66,78 @@ function runTests() { test.printSubHeader("Expected: 6-Tests, 3-Pass and 3-Fail"); test.printSummary(); - test.printSubHeader("Testing catching errors"); + test.printSubHeader("Testing catching errors using catchErr"); test.printSubHeader("Reset counters"); test.resetTestCounters(); + + test.printSubHeader("Expected: error type and message correct, using user message"); + test.catchErr( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + "Input argument is not a valid number", // this is the error message we are expecting + "We caught the error message correctly" + ); + test.printSubHeader("Expected to fail, becuase the expected error doesn´t match"); test.catchErr( () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error - "Argument must be a number", // this is the error message we are expecting + "Wrong error message", // this is the error message we are expecting "We caught the error, but with the wrong error message" ); - test.printSubHeader("Expected to pass, because the expected error matches"); + test.printSubHeader("Expected: error type and message correct, using default message"); test.catchErr( () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + "Wrong error message" // this is the error message we are expecting (wrong) + ); + + test.printSummary(); + + test.printSubHeader("Testing catching ErrType.."); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + + test.printSubHeader("Expected: Error type and message correct"); + test.catchErrType( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + Error, // This is the error type we are expecting "Input argument is not a valid number", // this is the error message we are expecting - "We caught the error message correctly" + "We caught the error type and error message correctly" ); + + test.printSubHeader("Expected: Error type correct, but error message wrong, with user message"); + test.catchErrType( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + Error, // This is the error type we are expecting + "Wrong error message", // this is the error message we are expecting + "We caught the error type correctly, but wrong error message" + ); + + test.printSubHeader("Expected: Error type correct, but error message wrong, with default message"); + test.catchErrType( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + Error, // This is the error type we are expecting + "Wrong error message" // this is the error message we are expecting + ); + + test.printSubHeader("Expected: Error type incorrect, using user message"); + test.catchErrType( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + TypeError, // This is the error type we are expecting + "Wrong error type", // this is the error message we are expecting + "We caught incorrect error type" + ); + + test.printSubHeader("Expected: Error type incorrect, using default message"); + test.catchErrType( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + TypeError, // This is the error type we are expecting + "Wrong error type" // this is the error message we are expecting + ); + test.printSummary(); + + + // Testing the same tests but in silent mode (LevelInfo = 0) test.printSubHeader("Testing the same tests with setting levelInfo = 0"); // It logs out, because we haven't changed LevelInfo yet test.setLevelInfo(0); // 0-Only summary result, 1-Detail results From be19779c6a1cc05ba45f7488a77b9b5e64bf5b51 Mon Sep 17 00:00:00 2001 From: dlealv Date: Sun, 24 Oct 2021 16:23:13 -0400 Subject: [PATCH 23/27] Update UnitTestingApp.js Corrected my previous version. Now`catchErr` extending its signature using default input arguments, can also test error message and error type. Reviewed documentation. Added minor changes to `assert` to be used consistently for the message to print out in case the test passes with an empty message. We use this function for sending messages to `catchErr` so both functions behave the same under this scenario. For `assertEquals` now we wrap some message with quotes (') when it makes sense. --- UnitTestingApp.js | 90 +++++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index d1deb57..dfd405a 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -13,9 +13,9 @@ let UnitTestingApp = (function () { const _enabled = new WeakMap(); const _runningInGas = new WeakMap(); const _nTests = new WeakMap(); // Total number of tests executed - const _nFailTests = new WeakMap(); // Total test failed - const _nPassTests = new WeakMap(); // Total test passed - const _levelInfo = new WeakMap(); // Level of information to show in the output (0-summary, 1-trace and test result information) + const _nFailTests = new WeakMap(); // Total tests failed + const _nPassTests = new WeakMap(); // Total tests passed + const _levelInfo = new WeakMap(); // Level of information to show in the console (0-summary, 1-trace and test result information) class UnitTestingApp { constructor() { @@ -81,7 +81,7 @@ let UnitTestingApp = (function () { /** * Tests whether conditions pass or not * @param {Boolean | Function} condition - The condition to check - * @param {String} message - the message to display in the console (based on _levelInfo value) + * @param {String} message - the message to display in the console (based on _levelInfo value). * @return {void} */ assert(condition, message) { @@ -92,7 +92,8 @@ let UnitTestingApp = (function () { if ("function" === typeof condition) condition = condition(); if (condition) { _nPassTests.set(this, _nPassTests.get(this) + 1); - if (this.getLevelInfo() > 0) console.log(`✔ PASSED: ${message}`); + let msg = (message =="") ? "" : ": " + message; // remove ":" if empty message + if (this.getLevelInfo() > 0) console.log(`✔ PASSED${msg}`); } else { _nFailTests.set(this, _nFailTests.get(this) + 1); @@ -109,10 +110,11 @@ let UnitTestingApp = (function () { * Tests whether fun result is equal to expected result or not * @param {Boolean | Function} Condition or fun - to check * @param {String} expectedResult - The expected result to validate - * @param {String} message - If present, then used as message to display in the console (based on _levelInfo value). - * If message is not present, then in case the result is not equal, it shows the missmatch - * In the form of: "result != expectedResult". If the result is valid and message is not - * provided, then it only indicates the test passed. + * @param {String} message - If present, then used as message to display to console (based on _levelInfo value). + * If message is not provided (default), then in case the result is not equal to expectedResult, + * it shows the missmatch in the form of: + * "'result' != 'expectedResult'" (numbers or booleans are not wrapped in quotes (')) + * If the result is valid and message is not provided, then it only indicates the test passed. * @return {void} */ assertEquals(fun, expectedResult, message = null) { @@ -120,7 +122,8 @@ let UnitTestingApp = (function () { if (this.isInGas !== this.runningInGas) return; _nTests.set(this, _nTests.get(this) + 1); let msg, result; - + //wraps in quotes (') any type except numbers or boolean + function q(v){return (('number' === typeof v) || ('boolean' === typeof v)) ? v : "'" + v + "'"}; try { ("function" === typeof fun) ? result = fun() : result = fun; let condition = expectedResult === result; @@ -131,66 +134,55 @@ let UnitTestingApp = (function () { } else { _nFailTests.set(this, _nFailTests.get(this) + 1); - msg = (message == null) ? result + " != " + expectedResult : message; + msg = (message == null) ? q(result) + " != " + q(expectedResult) : message; if (this.getLevelInfo() >= 1) console.log(`❌ FAILED: ${msg}`); } } catch (err) { _nFailTests.set(this, _nFailTests.get(this) + 1); - (message == null) ? result + " != " + expectedResult : message; + (message == null) ? q(result) + " != " + q(expectedResult) : message; if (this.getLevelInfo() >= 1) console.log(`❌ FAILED(err): ${msg} (${err})`); } } - - /** - * Tests functions that throw error messages - * @param {Function} callback - the function that you expect to return the error message - * @param {String} errorMessage - the error message you are expecting - * @param {String} message - the message to display in the console. If null (default) a message is built as follow in case of fail - * Wrong error message: 'Caugh error message' != errorMessage - * @return {void} - */ - catchErr(callback, errorMessage, message=null) { - if (!_enabled.get(this)) return; - if (this.isInGas !== this.runningInGas) return; - let isCaught = false; - try { - callback(); - } catch (err) { - isCaught = new RegExp(errorMessage).test(err); - message = (!isCaught && (message == null)) ? `Wrong error message: '${errorMessage}' != '${err.message}'` : message; - } finally { - this.assert(isCaught, message); - } - } - /** - * Tests functions that throw error (message and type of error) + * Tests functions that throw error, validating message and/or type of error. If no error thrown, then the test fails. * @param {Function} callback - the function that you expect to return the error message - * @param {Type} errorType - the error type you are expecting * @param {String} errorMessage - the error message you are expecting - * @param {String} message - the message to display in the console. If null (default value), in case of fail - * It builds a predefined message as follow. In case of wrong error type: - * Wrong error type: 'errorType' != 'CaughErrorType' - *. In case of wrong error message: Wrong error message: 'Caugh error message' != 'errorMessage' + * @param {String} message - the message to display to console (based on _levelInfo attribute value). + * If null (default value), in case error is cautgh, it builds a predefined message as follow: + * In case of wrong error type: "Wrong error type: 'CaughErrorType' != 'errorType'" + * In case of wrong error message: "Wrong error message: 'Caugh error message' != 'errorMessage'" + * In case both errorType and errorMessage are wrong: + * "Wrong error type: 'CaughErrorType' != 'errorType' and Wrong error message: 'Caugh error message' != 'errorMessage'" + * If no error was caught, then the message will be: "No error thrown" + * @param {Type} errorType - the error type you are expecting * @return {void} */ - catchErrType(callback, errorType, errorMessage, message = null) { + catchErr(callback, errorMessage, message = null, errorType = null) { if (!_enabled.get(this)) return; if (this.isInGas !== this.runningInGas) return; - let isCaught = false; + let isCaughtErrorType = true, isCaughtErrorMessage = false; + + // Identify correct input argument by its expected type + if ((message != null) && ("string" != typeof message)) {// invoked: catchErr(callback,string, null, Error) + errorType = message; + message = null; + } + try { callback(); } catch (err) { - if (err instanceof errorType) { - isCaught = new RegExp(errorMessage).test(err); - message = (!isCaught && (message == null)) ? `Wrong error message: '${errorMessage}' != '${err.message}'` : message; - } else { - message = (message == null) ? `Wrong error type: '${errorType.name}' != '${err.name}'` : message; + if (errorType != null) isCaughtErrorType = err instanceof errorType; + isCaughtErrorMessage = new RegExp(errorMessage).test(err); + if (message == null) { + !isCaughtErrorType ? message = `Wrong error type: '${err.name}' != '${errorType.name}'` : message =""; + if (!isCaughtErrorMessage) message += (!isCaughtErrorType ? " and wrong " : "Wrong ") + + `error message: '${errorMessage}' != '${err.message}'`; } } finally { - this.assert(isCaught, message); + if (message == null) message = "No error thrown"; + this.assert(isCaughtErrorType && isCaughtErrorMessage, message); } } From 05bbf5809fdc59e1f7b65fe8e5dbeb2626956095 Mon Sep 17 00:00:00 2001 From: dlealv Date: Sun, 24 Oct 2021 16:24:38 -0400 Subject: [PATCH 24/27] Update TestForVersion_0.1.1.js Added additional tests for testing the new changes. --- Example/TestForVersion_0.1.1.js | 231 ++++++++++++++++++++++++++------ 1 file changed, 188 insertions(+), 43 deletions(-) diff --git a/Example/TestForVersion_0.1.1.js b/Example/TestForVersion_0.1.1.js index 1d85e8f..6de96b0 100644 --- a/Example/TestForVersion_0.1.1.js +++ b/Example/TestForVersion_0.1.1.js @@ -37,109 +37,250 @@ function runTests() { * Run Local Tests Here ************************/ - let t ="test"; - console.log(`this is '${t}'`); - test.printHeader("Testing addTwoValues using assertEquals"); // Testing new method: assertEquals, printSummary and printSubHeader using addTwoValue function with default levelInfo // We haven´t set levelInfo, so we are using the default value: 1 test.printSubHeader("Using default Level Info value (1) for 'addTwoValues' function"); - test.printSubHeader("Expected: Test 1 passes, Test 2 passes with message, Test 3 fails, Test 4 fails with custom message"); + test.printSubHeader("Expected: Test 1 pass, Test 2 pass with user message, Test 3 fail default message, Test 4 fails with user message"); test.assertEquals(() => addTwoValues(1, 2), 3); // PASS test.assertEquals(() => addTwoValues(2, 2), 4, "Valid case: 2 + 2 = 4"); // PASS with message test.assertEquals(() => addTwoValues(1, 2), 4); // FAIL test.assertEquals(() => addTwoValues(1, 2), 4, "Expected to fail, because 1+2 != 4"); + test.printSubHeader("Expected: 4-Tests, 2-Tests fail, 2-Tests Pass"); test.printSummary(); // It should print final result and statistics (two lines) + test.printSubHeader("Testing when fun is boolean"); test.printSubHeader("Reset counters"); test.resetTestCounters(); - test.printSubHeader("Expected Test-1 Pass, Test-2 Fail"); - test.assertEquals(1 + 2 == 3, true); - test.assertEquals(1 + 1 == 3, true); + + test.printSubHeader("Expected Test-1 pass with default message, Test-2 pass with user message, Test-3 fail with default message, Test-4 fail with user message"); + test.assertEquals(1 + 2 == 3, true); // Pass + test.assertEquals(1 + 2 == 3, true, "Valid Case"); + test.assertEquals(1 + 1 == 3, true); // FAIL + test.assertEquals(1 + 1 == 3, true, "Invalid Case"); + test.printSubHeader("Expected: 4-Tests, 2-Tests fail, 2-Tests Pass"); test.printSummary(); - test.printSubHeader("Testing undefined input arguments"); - test.printSubHeader("Expected: Test-3 Pass, Test-4 Fail, Test-5 Pass, Test-6 Fail"); + + test.printSubHeader("testing using strings"); + test.printSubHeader("Test-5 pass with user message, Test-6 pass with default message, Test-7 pass with default message, Test-8 fail with default message, Test-9 fail with deafult message"); + test.assertEquals("world" == "world", true, "Expected to pass 'world' = 'world'"); // Pass with user message + test.assertEquals("world" == "world", true); // Pass + test.assertEquals("world" != "World", true, "Expected to pass 'world' != 'World'"); // Pass with user message + test.assertEquals("world" == "World", true); // Fail with default message + test.assertEquals("world" != "world", true); // Fail with default message + test.printSubHeader("Expected: 9-Tests, 4-Tests fail, 5-Tests Pass"); + test.printSummary(); + + + test.printSubHeader("Testing undefined input arguments using default message or undefined message"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.printSubHeader("Expected: Test-1 pass, Test-2 fail, Test-3 pass, Test-4 fail"); test.assertEquals(); // PASS both are undefined test.assertEquals(1 + 1); // FAIL, because the expectedValue is undefined test.assertEquals(1 + 1, 2, undefined); // Pass but message is undefined - test.assertEquals(1 + 1, 3, undefined); // Fail with undefined message (default is null, so the message doesn't show) - test.printSubHeader("Expected: 6-Tests, 3-Pass and 3-Fail"); + test.assertEquals(1 + 1, 3, undefined); // Fail with undefined message (treated as null, so it shows default message + test.printSubHeader("Expected: 4-Tests, 2-Tests fail, 2-Tests pass"); test.printSummary(); - test.printSubHeader("Testing catching errors using catchErr"); + test.printHeader("Testing catching errors using catchErr"); test.printSubHeader("Reset counters"); test.resetTestCounters(); - test.printSubHeader("Expected: error type and message correct, using user message"); + test.printSubHeader("Testing backward compatibility"); + + test.printSubHeader("Expected to pass: throw an error and error message is correct, user provided the message"); test.catchErr( () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error - "Input argument is not a valid number", // this is the error message we are expecting - "We caught the error message correctly" + "Input argument is not a valid number", // this is the error message we are expecting (correct) + "We caught the error message correctly" // This is the user message to log out + ); + + test.printSubHeader("Expected to pass: throw an error and error message is correct, using default message"); + test.catchErr( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + "Input argument is not a valid number", // this is the error message we are expecting (correct) ); - test.printSubHeader("Expected to fail, becuase the expected error doesn´t match"); + test.printSubHeader("Expected to fail: wrong error message, user provided the message"); test.catchErr( () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error "Wrong error message", // this is the error message we are expecting "We caught the error, but with the wrong error message" ); - test.printSubHeader("Expected: error type and message correct, using default message"); + + test.printSubHeader("Expected to fail: wrong error message, using default message"); test.catchErr( () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error "Wrong error message" // this is the error message we are expecting (wrong) ); + test.printSubHeader("Expected: 4-Tests, 2-Tests fail, 2-Pass"); test.printSummary(); - test.printSubHeader("Testing catching ErrType.."); + test.printSubHeader("Testing error type via errorType optional input argument"); test.printSubHeader("Reset counters"); test.resetTestCounters(); - test.printSubHeader("Expected: Error type and message correct"); - test.catchErrType( + test.printSubHeader("Expected to pass: error type and error message are correct, user provided the message"); + test.catchErr( () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error - Error, // This is the error type we are expecting "Input argument is not a valid number", // this is the error message we are expecting - "We caught the error type and error message correctly" + "We caught the error type and error message correctly", + Error // This is the error type we are expecting ); - test.printSubHeader("Expected: Error type correct, but error message wrong, with user message"); - test.catchErrType( + test.printSubHeader("Expected to pass: error type and error message are correct, using default message"); + test.catchErr( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + "Input argument is not a valid number", // this is the error message we are expecting + Error // This is the error type we are expecting + ); + + test.printSubHeader("Expected to fail: error type correct, but wrong error message, user provided the message"); + test.catchErr( () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error - Error, // This is the error type we are expecting "Wrong error message", // this is the error message we are expecting - "We caught the error type correctly, but wrong error message" + "We caught the error type correctly, but wrong error message", + Error // This is the error type we are expecting ); - test.printSubHeader("Expected: Error type correct, but error message wrong, with default message"); - test.catchErrType( + test.printSubHeader("Expected to fail: error type correct, but wrong error message using default message"); + test.catchErr( () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error - Error, // This is the error type we are expecting - "Wrong error message" // this is the error message we are expecting + "Wrong error message", // this is the error message we are expecting + Error // This is the error type we are expecting ); - test.printSubHeader("Expected: Error type incorrect, using user message"); - test.catchErrType( + test.printSubHeader("Expected to fail: wrong error type, error message correct, user provided the message"); + test.catchErr( () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error - TypeError, // This is the error type we are expecting - "Wrong error type", // this is the error message we are expecting - "We caught incorrect error type" + "Input argument is not a valid number", // this is the error message we are expecting + "We caught incorrect error type, but the error message is correct", + TypeError // This is the error type we are expecting ); - test.printSubHeader("Expected: Error type incorrect, using default message"); - test.catchErrType( + test.printSubHeader("Expected to fail: wrong error type, error message correct, using default message"); + test.catchErr( () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error - TypeError, // This is the error type we are expecting - "Wrong error type" // this is the error message we are expecting + "Input argument is not a valid number", // this is the error message we are expecting + TypeError // This is the error type we are expecting ); + test.printSubHeader("Expected to fail: wrong error type and error message, user provided the message"); + test.catchErr( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + "Wrong error message", // this is the error message we are expecting + "We caught incorrect error type and message", + TypeError // This is the error type we are expecting + ); + + test.printSubHeader("Expected to fail: wrong error type and error message, using default message"); + test.catchErr( + () => addTwoValuesSafe("a", "b"), // we’re passing a string here to test that our function throws an error + "Wrong error message", // this is the error message we are expecting + TypeError // This is the error type we are expecting + ); + + test.printSubHeader("Expected: 8-Tests 6-Tests fail, 2-Pass"); test.printSummary(); + test.printSubHeader("Testing no error should be thrown"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + + // Not catching error + test.printSubHeader("Expected to fail: no error should be thrown, correct error message, user provided the message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "Input argument is not a valid number", // this is the error message we are expecting + "No error should be thrown" // this is the error message we are expecting + ); + + test.printSubHeader("Expected to fail: no error should be thrown, correct error message using defeault message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "Input argument is not a valid number" // this is the error message we are expecting + ); + + test.printSubHeader("Expected to fail: no error should be thrown, wrong error message, user provided the message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "No error", // this is the error message we are expecting + "No error should be thrown" // this is the error message we are expecting + ); + + test.printSubHeader("Expected to fail: no error should be thrown, using default message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "No error", // this is the error message we are expecting + ); + test.printSubHeader("Expected to fail: no error should be thrown, error type and message are correct, user provided the message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "Input argument is not a valid number", // this is the error message we are expecting + "No error should be thrown", // this is the error message we are expecting + Error + ); +test.printSubHeader("Expected to fail: no error should be thrown, error type and message are correct, using default message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "Input argument is not a valid number", // this is the error message we are expecting + Error + ); - // Testing the same tests but in silent mode (LevelInfo = 0) - test.printSubHeader("Testing the same tests with setting levelInfo = 0"); // It logs out, because we haven't changed LevelInfo yet + test.printSubHeader("Expected to fail: no error should be thrown, error type correct and wrong error message, user provided the message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "No error", // this is the error message we are expecting + "No error should be thrown", // this is the error message we are expecting + Error + ); + + test.printSubHeader("Expected to fail: no error should be thrown, error type correct and wrong error message, using default message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "No error", // this is the error message we are expecting + Error + ); + + test.printSubHeader("Expected to fail: no error should be thrown, providing wrong errorType and correct error message, user provided the message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "Input argument is not a valid number", // this is the error message we are expecting + "No error should be thrown", // this is the error message we are expecting + TypeError + ); + + test.printSubHeader("Expected to fail: no error should be thrown, providing wrong errorType and correct error message, using default message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "Input argument is not a valid number", // this is the error message we are expecting + TypeError + ); + + test.printSubHeader("Expected to fail: no error should be thrown, wrong error type an message, user provided the message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "No error", // this is the error message we are expecting + "No error should be thrown", // this is the error message we are expecting + TypeError + ); + + test.printSubHeader("Expected to fail: no error should be thrown, providing wrong errorType and wrong error message using default message"); + test.catchErr( + () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) + "No error", // this is the error message we are expecting + TypeError + ); + + test.printSubHeader("Expected: 12-Tests, 12-Tests fail, 0-Pass"); + test.printSummary(); + + // Testing similar basic tests but in silent mode (LevelInfo = 0) + test.printHeader("Testing the same assertEquals tests with setting levelInfo = 0"); // It logs out, because we haven't changed LevelInfo yet test.setLevelInfo(0); // 0-Only summary result, 1-Detail results // Because LevelInfo = 0 we use the console for traceability purpose console.log("LevelInfo = " + test.getLevelInfo()); @@ -156,8 +297,9 @@ function runTests() { test.printSummary(); //Shows only summary line // Testing the existing assert function work as expected - test.printSubHeader('Default level Info'); + test.printSubHeader('Setting default level Info'); test.setLevelInfo(1); + test.printHeader("Testing addTwoValues using assert"); test.printSubHeader("LevelInfo = " + test.getLevelInfo()); test.printSubHeader("Test-1 Pass, Test-2 Fail"); @@ -169,9 +311,11 @@ function runTests() { test.printSubHeader("Testing when condition is boolean"); test.printSubHeader("Reset counters"); test.resetTestCounters(); - test.printSubHeader("Test-1 Pass, Test-2 Fail"); + test.printSubHeader("Test-1 Pass, Test-2 pass (empty string), Test-3 fail"); test.assert(1 + 2 == 3, "Valid case: 1 + 2 = 3"); + test.assert(1 + 2 == 3, ""); // testing empty message test.assert(1 + 1 == 3, "Invalid case: 1 + 1 != 3"); + test.printSubHeader("3-Tests, 1-fail, 2 pass"); test.printSummary(); test.printSubHeader("Testing undefined input arguments"); @@ -201,6 +345,7 @@ function runTests() { test.assert(() => addTwoValues(1, 2) == 3, "Valid case"); console.log("Printing the summary line only: all test passed"); test.printSummary(); + console.log("Changing the level info to 1"); test.setLevelInfo(1); test.printSubHeader("Showing now printSummary with two lines for the previous set of tests"); From 69112d3a77509bebafc74796555cc1194b4c0baa Mon Sep 17 00:00:00 2001 From: dlealv Date: Sat, 30 Oct 2021 20:36:27 -0400 Subject: [PATCH 25/27] Update README.md Documented the new changes will be released today --- README.md | 217 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 177 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 80a645e..d9254bc 100644 --- a/README.md +++ b/README.md @@ -47,11 +47,11 @@ test.runInGas(true); // switch to online tests in Google Apps Script environment ``` -Then we have the actual built-in testing methods, `assert()`, `catchErr()` and `is2dArray()`. +Then we have the actual built-in testing methods, `assert()`, `catchErr()` and `is2dArray()`, etc.. ### Control Level of Information to log out -The function `setLevelInfo(value)` controls the level information to be reported through the console. If `value`is less or equal than `0`, it runs in silent mode, i.e. no information will be reported. The only exception from print-family functions is `printSummary()` that under this mode just logs out a single summary line. For example if no errors found, the output will be: +The function attribute `levelInfo` controls the level information to be reported through the console. If `value`is less or equal than `0`, it runs in silent mode, i.e. no information will shown in the console. The only exception from print-family functions is `printSummary()` it logs out a single summary line. For example if all tests passed, the output will be: ```ALL TESTS ✔ PASSED``` @@ -64,7 +64,7 @@ This setup is usufull for large tests where we just want to add some incremental Here is how the level of information can be specified for silent mode: ```javascript -test.setLevelInfo(0); +test.levelInfo = 0; ``` If the value is `1`(default value) or greater, it means trace information will log out the result per each test, indicating if the test failed or passed. Depending on the specific testing function it will log out different information, for example, let's says we want to test the following custom function: @@ -74,81 +74,120 @@ function addTwoValues (a, b) { } ``` -then invoking the following assert function: +then invoking the following assert functions: ```javascript -test.assertEquals(() => addTwoValues(1,2),3); // where expected result is 3 +test.assert(() => addTwoValues(1,2)==3); // expected result is 3 +test.assertEquals(() => addTwoValues(1,2),3); // expected result is 3 ``` will return ``` -✔ PASSED +✔ PASSED: Input argument 'condition' passed +✔ PASSED: 3 === 3 ``` -on contrary, if we invoke the function with a wrong expected result: +on contrary, if we invoke the functions with a wrong expected result: ```javascript -test.assertEquals(() => addTwoValues(1,2),4); // where expected result is 4 +test.assertEquals(() => addTwoValues(1,2),4); // expected result is 4 +test.assertEquals(() => addTwoValues(1,2),4); // expected result is 4 ``` will return ``` +❌ FAILED: Input argument 'condition' failed ❌ FAILED: 3 != 4 ``` +In case of fail when the user doesn't provide the input argument `message`, `assertEquals` indicates the specific mismatch. -As you can see, there is no need to specify the message, it shows where is the problem. If we invoke `assertEquals` including its optional input argument `message`, then the output information will be on each case the information specificied by this input argument +If we invoke `assertEquals` or `assert` including its optional input argument `message`, the user has more control of the information that goes to the console: ```javascript -test.assertEquals(() => addTwoValues(1,2),3, "Expected result: 1+2 = 3"); -test.assertEquals(() => addTwoValues(1,2),4, "Wrong result because 1+2 should be equal to 3"); +test.assert(() => addTwoValues(1,2)==3, "Expected result: 1 + 2 = 3"); +test.assert(() => addTwoValues(1,2)==4, "Wrong result because 1 + 2 should be equal to 3"); +test.assertEquals(() => addTwoValues(1,2),3, "Expected result: 1 + 2 = 3"); +test.assertEquals(() => addTwoValues(1,2),4, "Wrong result because 1 + 2 should be equal to 3"); ``` The output will be: ``` -✔ PASSED: Expected result: 1+2 = 3 +✔ PASSED: Expected result: 1 + 2 = 3 +❌ FAILED: Wrong result because 1 + 2 should be equal to 3 +✔ PASSED: Expected result: 1 + 2 = 3 ❌ FAILED: Wrong result because 1+2 should be equal to 3 ``` -If we are using `assert` that has a mandatory `message`as input argument, the message will be printed as testing result. - -When using `printSummary()` with level info is equal or greater than `1`, it logs out an additional line providing statistics about testing results: +If we invoke `printSummary()` with `levelInfo` is equal or greater than `1`, it logs out an additional line providing statistics about testing results: ``` -TOTAL TESTS= 1, ❌ FAILED=0, ✔ PASSED=1 -ALL TESTS ✔ PASSED +TOTAL TESTS= 4, ❌ FAILED=2, ✔ PASSED=2 +❌ Some Tests FAILED ``` -Indicating that `1`test passed and `0` test failed and the total tests executed +Indicating that `2`test passed and `2` test failed and the total tests executed -### assert(condition, message) +### assert(condition, message = null) -`assert()` is the main method of the class. The first argument that it takes is the condition and it checks whether the condition is truthy or falsy. The condition can either be a boolean value or a function that returns such a condition, function being the preferred approach for error catching reasons. If the condition is truthy, it logs out a “PASSED” message, otherwise it logs out a “FAILED” message. If you pass in a function that throws an error, the method will catch the error and log out a “FAILED” message. For example: +`assert()` is the main method of the class. The first argument that it takes is the condition and it checks whether the condition is truthy or falsy. The condition can either be a boolean value or a function that returns such a condition, function being the preferred approach for error catching reasons. If the condition is truthy, it logs out a “PASSED” message, otherwise it logs out a “FAILED” message. If you pass in a function that throws an error, the method will catch the error and log out a “ERROR” message and it counts as a fail test. For example: ```javascript const num = 6; - test.assert(() => num % 2 === 0, `Number ${num} is even`’); -// logs out PASSED: Number 2 is even - test.assert(() => num > 10, `Number ${num} is bigger than 10`); -// logs out FAILED: Number 2 is bigger than 10 ``` -### assertEquals(fun, expectedResult, message) -`assertEquals`is another assert function, that helps to verify the result with respect the expected value. The first argument is the function or condition we would like to evaluate, the second argument is the expected result. If returned value of the `fun`is not equal to `expectedResult` it fails, otherwise passed. Here is how to validate javascript standard `max()`function: +logs out: +``` +✔ PASSED: Number 6 is even +❌ FAILED: Number 6 is bigger than 10 +``` + +Let's say we have the following function that throws an error: + +```javascript +function addTwoValues(a, b) { + if (("number" != typeof a) || ("number" != typeof b)) + throw new Error("Input argument is not a valid number"); + return a+b; +} +``` +then when testing `addTwoValues`: + +```javascript +test.assert(() => addTwoValues("a", "b") === 0, "Expected an error was thrown"); +``` + +will show an error message in the console as follow. For errors the library uses `console.error` so it will appear with light red background + +``` +❌ ERROR: Expected and error was thrown (Error: Input argument is not a valid number) +``` + +**Note:** If input argument `message` is not provided a default built-in message is provided indicating the `condition` passed or failed. + +### assertEquals(condition, expectedResult, message = null) +Similar to `assert` function, handy for checking against specific expected result, `assert` function when input argument `message` is not provided, there is not way to verify the condition againts expected result, with `assertEquals` the buil-in default message helps to confirm the match or to identify the mismatch. Here is how to validate javascript standard `max()`function: + ```javascript -test.assertEquals(() => Math.max(1,2),2); # Expected result is 2 (equal to actual result) -test.assertEquals(() => Math.max(1,2),1); # Expected result is 1 (different than actual result) -test.assertEquals(() => Math.max(1,2),2, "Correct result the max of 1,2 is 2"); # Using optional input argument message +test.assertEquals(() => Math.max(1,2),2); // Pass +test.assertEquals(() => Math.max(1,2),1); // Fail +test.assertEquals(() => Math.max(1,2),2, "Correct result the max of 1,2 is 2"); // Pass +``` ``` -The last case specifies the message to log out regardless of the test result, i.e. the same message will be log out whether it failed or passed. +✔ PASSED: 2 === 2 +❌ FAILED: 2 != 1 +✔ PASSED: Correct result the max of 1,2 is 2 +``` +As in `assert` function in case an error is thrown an error message will be generated. +**Note:** Basic types values such as `number`, `boolean`, `null` or `undefined` are not wrapped in quotes ('), all other types are wrapped. -### catchErr(condition, expectedErrorMessage, message) +### catchErr(callback, errorMessage, message = null, errorType = null) -The goal of this method is to test whether your callback function (`callback`) catches the correct error. What you want to do is to make sure that the callback actually throws an error and then the `tests` method will check if it’s the correct one. Then finally it will use `assert()` to log out the corresponding message. For example, let’s say you have a function that returns the square value of a number you pass as the argument, and you want to throw an error if the argument isn’t a number, then you can test the error this way: +The goal of this method is to test whether your callback function (`callback`) catches the correct error message and/or error type. What you want to do is to make sure that the callback actually throws an error and then if it’s the correct one. Then finally it will use `assert()` to log out the corresponding message. For example, let’s say you have a function that returns the square value of a number you pass as the argument, and you want to throw an error if the argument isn’t a number, then you can test the error this way: ```javascript function square(number) { @@ -160,14 +199,93 @@ test.catchErr( () => square("a string"), // we’re passing a string here to test that our function throws an error "Argument must be a number", // this is the error message we are expecting "We caught the type error correctly" - ); +); +``` +If message is not provided a bult-in message is provided as follow for previous example: +```javascript +test.catchErr( + () => square("a string"), // we’re passing a string here to test that our function throws an error + "Argument must be a number" // this is the error message we are expecting +); +``` +``` +✔ PASSED: Error message is correct +``` + +Similar for fail case: +```javascript +test.catchErr( + () => square("a string"), // we’re passing a string here to test that our function throws an error + "Wrong error message" // this is the error message we are expecting +); +``` +``` +❌ FAILED: Wrong error message: 'Argument must be a number' != 'Wrong error message' +``` +When input argument `errorType` is provided it also checks the error type caught is the same as `errorType`. For example: + +```javascript +// Correct error type and error message +test.catchErr( + () => square("a string"), // we’re passing a string here to test that our function throws an error + "Argument must be a number", // this is the error message we are expecting + Error // This is the error type we are expecting +); +// Wrong error type and correct error message +test.catchErr( + () => square("a string"), // we’re passing a string here to test that our function throws an error + "Argument must be a number", // this is the error message we are expecting + TypeError // This is the error type we are expecting +); +// Correct error type and wrong error message +test.catchErr( + () => square("a string"), // we’re passing a string here to test that our function throws an error + "Wrong error message", // this is the error message we are expecting + Error // This is the error type we are expecting +); +// Wrong error type and error message +test.catchErr( + () => square("a string"), // we’re passing a string here to test that our function throws an error + "Wrong error message", // this is the error message we are expecting + TypeError // This is the error type we are expecting +); + +``` + +it will produce the following output: + +``` +✔ PASSED: Error type and error message are correct +❌ FAILED: Wrong error type: 'Error' != 'TypeError' +❌ FAILED: Wrong error message: 'Argument must be a number' != 'Wrong error message' +❌ FAILED: Wrong error type: 'Error' != 'TypeError' and wrong error message: 'Argument must be a number' != 'Wrong error message' ``` + +If we pass a valid value, no error is thrown: + +```javascript +test.catchErr( + () => square(2), // we’re passing a valid value + "Argument must be a number", // this is the error message we are expecting + Error +); +``` + +the test fails with the following information + +``` +❌ FAILED: No error thrown +``` + **Note:** Even though you can invoke both asserts functions (`assert`, `assertEquals`) not using the `=>` (arrow function), for example: + ```javascript test.assertEquals(square(2), 4); test.assert(square(2)==4, "Valid case sqrt(2) is equal to 4"); ``` -We don't recommend invoking like this, because `catchErr()` function will require arrow function invokation. The following code will produce an execution error on the first line of the body of `sqrt` function. + +We don't recommend to do it, because `catchErr()` function will require arrow function invokation. The following code will produce an execution error on the first line of the body of `sqrt` function. + ```javascript test.catchErr( square("a string"), // we’re passing a string here to test that our function throws an error @@ -176,7 +294,7 @@ test.catchErr( ); ``` -### is2dArray(Array) +### is2dArray(Array, message = null) This method runs a test to check whether the argument is a 2D array. This is useful for testing spreadsheet values before inserting them into a spreadsheet: @@ -187,24 +305,29 @@ const values = [ ]; test.is2dArray(values, ‘values is an array of arrays’); // logs out PASSED + ``` +If input argument `message` is not provided a built-in message is generated indicated the test passed or failed. Then there are a couple of helper methods, `printHeader()` and `clearConsole()`. ### Print-family Functions -**Note**: The level of information shows by print-family functions wil depend on the level of information the user specified via `setLevelInfo(vaue)`, or if no value was specified it assumes the level of infomration is `1`. See section: *Control Level of Information to log out* for more information. +**Note**: The level of information shown by print-family functions will depend on the level of information the user specified via `levelInfo`, or if no value was specified it assumes the level of information is `1`. See section: *Control Level of Information to log out* for more information. The `printHeader()` function just helps with readability by printing a header in the console like this. It can be used for printing for example the title of the testing set. Here the expected result under `1` level of information: + ```javascript test.printHeader(‘Offline tests’); ``` + Logs out the following: + ``` -********************* +*************** * Offline tests -********************* +*************** ``` There also a second print header function: `printSubHeader(text)`, usefull to log out a sub header as a single line with prefix `**`. Here the output under level of information equal to `1`: @@ -212,27 +335,41 @@ There also a second print header function: `printSubHeader(text)`, usefull to lo ```javascript test.printSubHeader(‘Testing valid cases...’); ``` + logs out: + ``` ** Testing valid cases... ``` + There is a third print function: `printSummary()`, that logs out a summary of testing results (depending on the level of information we want to show) ```javascript test.printSummary(); ``` + If we ran 20 tests, where there is one failed test, under level of information equal to `1`, the result will be: + ``` TOTAL TESTS= 20, ❌ FAILED=1, ✔ PASSED=19 ❌ Some Tests FAILED ``` -Similarly if the level of information is equal to `0`, the output will be: + +Similarly if the `levelInfo` lower or equial than `0`, the output will be: + ``` ❌ Some Tests FAILED ``` +on contrary if all tests passed, will show: + +``` +ALL TESTS ✔ PASSED +``` + + ### resetTestCounters() -The `resetTestCounters()` is usefull for reseting testing counters (`_nTests`, `_nFailTests`, `_nPassTests`, attributes of the class `UnitTestingApp`), the function `printSummary()` will log out information depending on the overall testing results based on such attributes. We can use this function to reset testing counters after a running a set of tests, so we can print a summary information per set of tests using `printSummary()`. +The `resetTestCounters()` is usefull for reseting testing counters (`_nTests`, `_nFailTests`, `_nPassTests`, private attributes of the class `UnitTestingApp`), the function `printSummary()` will log out information depending on the overall testing results based on such attributes. We can use this function to reset testing counters after a running a set of tests, so we can print a summary information per set of tests using `printSummary()`. ### clearConsole() From 4fcc7ab9af2d661d9728a6c095a5b32bdc622801 Mon Sep 17 00:00:00 2001 From: dlealv Date: Sat, 30 Oct 2021 20:50:19 -0400 Subject: [PATCH 26/27] Update UnitTestingApp.js Summary of the changes: 1. This version has consistent behaviour for the messages via (input argument `message`). When the user doesn't provide a message a default built-in message will be provided in both cases: PASS and FAIL 2. Improved documentation 3. Improved printSummary() 4. Considered Error label in case of Error and counted as a failure and using `console.error()` in instead of `console.log()` 5. Using setter and getter for attribute `levelInfo` 6. Added a safeguard for setting `levelInfo` to ensure numbers only. I considered throwing an error 7. Encapusalated the check if the test is not active in a private function: `stopIfNotActive_` in order to have a cleaner code and easier to maintain --- UnitTestingApp.js | 202 +++++++++++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 84 deletions(-) diff --git a/UnitTestingApp.js b/UnitTestingApp.js index dfd405a..ce35434 100644 --- a/UnitTestingApp.js +++ b/UnitTestingApp.js @@ -5,11 +5,14 @@ ************************/ /** - * Class for running unit tests + * Class for running unit tests. For more information check the following links: + * https://github.com/WildH0g/UnitTestingApp + * https://medium.com/geekculture/taking-away-the-pain-from-unit-testing-in-google-apps-script-98f2feee281d */ let UnitTestingApp = (function () { - + // Using WeakMap to keep attributes private, idea taken from here: + // https://chrisrng.svbtle.com/using-weakmap-for-private-properties const _enabled = new WeakMap(); const _runningInGas = new WeakMap(); const _nTests = new WeakMap(); // Total number of tests executed @@ -52,6 +55,15 @@ let UnitTestingApp = (function () { return _runningInGas.get(this); } + get levelInfo() { + return _levelInfo.get(this); + } + + set levelInfo(value) { + if ("number" !== typeof value) throw new TypeError("Input argument value should be a number"); + _levelInfo.set(this, value); + } + runInGas(bool = true) { _runningInGas.set(this, bool); } @@ -60,16 +72,14 @@ let UnitTestingApp = (function () { if (console.clear) console.clear(); } - getLevelInfo() { - return _levelInfo.get(this); - } - - setLevelInfo(value) { - _levelInfo.set(this, value); + stopIfNotActive_() {// Helper function (not vissible for users of the library) + if (!_enabled.get(this)) return true; + if (this.isInGas !== this.runningInGas) return true; + return false; } /** - * Reset statistics counters + * Reset statistics counters: Number of tests, test passed and test failed * @return {void} */ resetTestCounters() { @@ -79,91 +89,96 @@ let UnitTestingApp = (function () { } /** - * Tests whether conditions pass or not + * Tests whether conditions pass or not. If other attributes such as enable, runningInGas indicate + * the test is not active, no test is carried out. * @param {Boolean | Function} condition - The condition to check - * @param {String} message - the message to display in the console (based on _levelInfo value). + * @param {String} message - the message to display in the console (if attribute levelInfo >=1). + * if value is not provided (default) it builds a default message indicating whether the test + * failed os passed, or some error occurred. * @return {void} */ - assert(condition, message) { - if (!_enabled.get(this)) return; - if (this.isInGas !== this.runningInGas) return; + assert(condition, message = null) { + if (this.stopIfNotActive_()) return; _nTests.set(this, _nTests.get(this) + 1); try { if ("function" === typeof condition) condition = condition(); if (condition) { _nPassTests.set(this, _nPassTests.get(this) + 1); - let msg = (message =="") ? "" : ": " + message; // remove ":" if empty message - if (this.getLevelInfo() > 0) console.log(`✔ PASSED${msg}`); - } - else { + message = (message == null) ? "Input argument 'condition' passed" : message; + if (this.levelInfo >= 1) console.log(`✔ PASSED: ${message}`); + } else { + message = (message == null) ? "Input argument 'condition' failed" : message; _nFailTests.set(this, _nFailTests.get(this) + 1); - if (this.getLevelInfo() > 0) console.log(`❌ FAILED: ${message}`); + if (this.levelInfo >= 1) console.log(`❌ FAILED: ${message}`); } - } catch (err) { + message = (message == null) ? "Something was wrong" : message; _nFailTests.set(this, _nFailTests.get(this) + 1); - if (this.getLevelInfo() > 0) console.log(`❌ FAILED: ${message} (${err})`); + if (this.levelInfo >= 1) console.error(`❌ ERROR: ${message} (${err})`); } } /** - * Tests whether fun result is equal to expected result or not + * Tests whether condition result is strictly equal (===) to expected result or not. + * If other attributes such as enable, runningInGas + * indicate the test is not active no test is carried out. * @param {Boolean | Function} Condition or fun - to check * @param {String} expectedResult - The expected result to validate - * @param {String} message - If present, then used as message to display to console (based on _levelInfo value). - * If message is not provided (default), then in case the result is not equal to expectedResult, - * it shows the missmatch in the form of: - * "'result' != 'expectedResult'" (numbers or booleans are not wrapped in quotes (')) - * If the result is valid and message is not provided, then it only indicates the test passed. + * @param {String} message - If present, then used as message to display to console (if attribute levelInfo >= 1). + * If message is not provided (default), if test failed, i.e. result is not equal to expectedResult, + * it shows the missmatch in the form of: + * "'result' != 'expectedResult'" (numbers or booleans are not wrapped in quotes (')) + * If the test passed, the message will be: + * "'result' === 'expectedResult'" (numbers or booleans are not wrapped in quotes (')) + * If some error occured, then: "Something was wrong" * @return {void} */ - assertEquals(fun, expectedResult, message = null) { - if (!_enabled.get(this)) return; - if (this.isInGas !== this.runningInGas) return; + assertEquals(condition, expectedResult, message = null) { + if (this.stopIfNotActive_()) return; _nTests.set(this, _nTests.get(this) + 1); - let msg, result; - //wraps in quotes (') any type except numbers or boolean - function q(v){return (('number' === typeof v) || ('boolean' === typeof v)) ? v : "'" + v + "'"}; + + // wraps in quotes (') any type except numbers, booleans, null or undefined + function q(v) {return ('number' === typeof v) || ('boolean' === typeof v) || !v ? v: `'${v}'`} try { - ("function" === typeof fun) ? result = fun() : result = fun; - let condition = expectedResult === result; - if (condition) { + if ("function" === typeof condition) condition = condition(); + let result = condition === expectedResult; + if (result) { _nPassTests.set(this, _nPassTests.get(this) + 1); - msg = (message == null) ? "" : ": " + message; - if (this.getLevelInfo() >= 1) console.log(`✔ PASSED${msg}`); - } - else { + message = (message == null) ? q(condition) + " === " + q(expectedResult) : message; + if (this.levelInfo >= 1) console.log(`✔ PASSED: ${message}`); + } else { _nFailTests.set(this, _nFailTests.get(this) + 1); - msg = (message == null) ? q(result) + " != " + q(expectedResult) : message; - if (this.getLevelInfo() >= 1) console.log(`❌ FAILED: ${msg}`); + message = (message == null) ? q(condition) + " != " + q(expectedResult) : message; + if (this.levelInfo >= 1) console.log(`❌ FAILED: ${message}`); } - } catch (err) { _nFailTests.set(this, _nFailTests.get(this) + 1); - (message == null) ? q(result) + " != " + q(expectedResult) : message; - if (this.getLevelInfo() >= 1) console.log(`❌ FAILED(err): ${msg} (${err})`); + message = (message == null) ? "Something was wrong" : message; + if (this.levelInfo >= 1) console.error(`❌ ERROR: ${message} (${err})`); } } /** - * Tests functions that throw error, validating message and/or type of error. If no error thrown, then the test fails. - * @param {Function} callback - the function that you expect to return the error message - * @param {String} errorMessage - the error message you are expecting - * @param {String} message - the message to display to console (based on _levelInfo attribute value). - * If null (default value), in case error is cautgh, it builds a predefined message as follow: - * In case of wrong error type: "Wrong error type: 'CaughErrorType' != 'errorType'" - * In case of wrong error message: "Wrong error message: 'Caugh error message' != 'errorMessage'" - * In case both errorType and errorMessage are wrong: - * "Wrong error type: 'CaughErrorType' != 'errorType' and Wrong error message: 'Caugh error message' != 'errorMessage'" - * If no error was caught, then the message will be: "No error thrown" - * @param {Type} errorType - the error type you are expecting - * @return {void} - */ + * Tests functions that throw error, validating message and/or type of error. If no error thrown, then the test fails. + * If other attributes such as enable, runningInGas indicate the test is not active, no test is carried out. + * @param {Function} callback - the function that you expect to return the error message + * @param {String} errorMessage - the error message you are expecting + * @param {String} message - the message to display to console (if attribute levelInfo >= 1). + * If null (default value), in case error is cautgh, it builds a predefined message as follow: + * In case of wrong error type: "Wrong error type: 'CaughErrorType' != 'errorType'" + * In case of wrong error message: "Wrong error message: 'Caugh error message' != 'errorMessage'" + * In case both errorType and errorMessage are wrong: + * "Wrong error type: 'CaughErrorType' != 'errorType' and wrong error message: 'Caugh error message' != 'errorMessage'" + *. In case error type and error message are correct, then: + * "Error type and error message are correct" + * If no error was caught, then the message will be: "No error thrown" and it is considered the test failed. + * @param {Type} errorType - the error type you are expecting. If null (default) the error type is not tested. + * @return {void} + */ catchErr(callback, errorMessage, message = null, errorType = null) { - if (!_enabled.get(this)) return; - if (this.isInGas !== this.runningInGas) return; - let isCaughtErrorType = true, isCaughtErrorMessage = false; - + if (this.stopIfNotActive_()) return; + let isCaughtErrorMessage = false, isCaughtErrorType = true // Error type is optional so default result is true + // Identify correct input argument by its expected type if ((message != null) && ("string" != typeof message)) {// invoked: catchErr(callback,string, null, Error) errorType = message; @@ -175,11 +190,15 @@ let UnitTestingApp = (function () { } catch (err) { if (errorType != null) isCaughtErrorType = err instanceof errorType; isCaughtErrorMessage = new RegExp(errorMessage).test(err); - if (message == null) { - !isCaughtErrorType ? message = `Wrong error type: '${err.name}' != '${errorType.name}'` : message =""; - if (!isCaughtErrorMessage) message += (!isCaughtErrorType ? " and wrong " : "Wrong ") - + `error message: '${errorMessage}' != '${err.message}'`; + if (message == null) {// Building default message in case of fail + if(!isCaughtErrorType) message = `Wrong error type: '${err.name}' != '${errorType.name}'`; + if (!isCaughtErrorMessage){ + let msg = `error message: '${err.message}' != '${errorMessage}'`; + message = (isCaughtErrorType) ? `Wrong ${msg}` : `${message} and wrong ${msg}`; + } } + // In case it didn't fail (message is still null), building default message + if(message == null) message = (errorType == null) ? "Error message is correct" : "Error type and error message are correct"; } finally { if (message == null) message = "No error thrown"; this.assert(isCaughtErrorType && isCaughtErrorMessage, message); @@ -187,47 +206,62 @@ let UnitTestingApp = (function () { } /** - * Tests whether an the argument is a 2d array + * Tests whether an the argument is a 2d array. If other attributes such as enable, runningInGas + * indicate the test is not active no test is carried out. * @param {*[][]} array - any 2d-array + * @param {String} message - The message to log out. If message is not provided a default + * message will be provided. * @returns {Boolean} */ - is2dArray(array, message) { - if (!_enabled.get(this)) return; - if (this.isInGas !== this.runningInGas) return; + is2dArray(array, message = null) { + if (this.stopIfNotActive_()) return; try { if ('function' === typeof array) array = array(); - this.assert(Array.isArray(array) && Array.isArray(array[0]), message); + let isArray = Array.isArray(array) && Array.isArray(array[0]); + if (message == null) message = "Input argument array is " + (isArray ? "2D array" : "not a 2D array"); + this.assert(isArray, message); } catch (err) { + if (message == null) message = "Something was wrong"; this.assert(false, `${message}: ${err}`); } } + /** + * Logs out using header format (3 lines). It logs out to the console if attribute levelInfo >= 1. + * If other attributes such as enable, runningInGas indicate the test is not active no information is loged out. + */ printHeader(text) { - if (!_enabled.get(this)) return; - if (this.isInGas !== this.runningInGas) return; - if (this.getLevelInfo() > 0) { - console.log('*********************'); + if (this.stopIfNotActive_()) return; + if (this.levelInfo >= 1) { + let len = ("string" === typeof text) ? text.length + 2 : 20; + if(len > 80) len = 80; + console.log("*".repeat(len)); console.log('* ' + text) - console.log('*********************'); + console.log("*".repeat(len)); } } + /** + * Logs out using sub header format (1 line). It logs out to the console if attribute levelInfo >= 1. + * If other attributes such as enable, runningInGas indicate the test is not active no information is loged out. + */ printSubHeader(text) { - if (!_enabled.get(this)) return; - if (this.getLevelInfo() > 0) console.log('** ' + text); + if (this.stopIfNotActive_()) return; + if (this.levelInfo >= 1) console.log('** ' + text); } /** - * Logs out testing summary, If _levelInfo is > 0, informs about total tests, number of failed tests and passed tests - * and in a second line indicating all test passed if no test failed otherwise indicating some test failed. - * If _levelInfo <= 0, logs out only the content of the second line. + * Logs out testing summary, If levelInfo is >= 1, then provides test statistics, informaing about total tests, + * number of failed tests and passed tests and in a second line summary line indicating all test passed if no test failed + * otherwise indicating some test failed. + * If levelInfo < 1, logs out only the content of the second line (summary line). + * If other attributes such as enable, runningInGas indicate the test is not active no information is loged out. * @return {void} */ printSummary() { - if (!_enabled.get(this)) return; - if (this.isInGas !== this.runningInGas) return; + if (this.stopIfNotActive_()) return; let msg = "TOTAL TESTS=%d, ❌ FAILED=%d, ✔ PASSED=%d"; - if (this.getLevelInfo() > 0) console.log(Utilities.formatString(msg, _nTests.get(this), + if (this.levelInfo >= 1) console.log(Utilities.formatString(msg, _nTests.get(this), _nFailTests.get(this), _nPassTests.get(this))); console.log((_nFailTests.get(this) == 0) ? "ALL TESTS ✔ PASSED" : "❌ Some Tests FAILED"); } From cc0df114faa2c0224eeda8fd18a3ce937445660a Mon Sep 17 00:00:00 2001 From: dlealv Date: Sat, 30 Oct 2021 20:52:29 -0400 Subject: [PATCH 27/27] Update TestForVersion_0.1.1.js Added the corresponding tests and verification for the version uploaded today. --- Example/TestForVersion_0.1.1.js | 251 +++++++++++++++++++++++--------- 1 file changed, 181 insertions(+), 70 deletions(-) diff --git a/Example/TestForVersion_0.1.1.js b/Example/TestForVersion_0.1.1.js index 6de96b0..f6ea437 100644 --- a/Example/TestForVersion_0.1.1.js +++ b/Example/TestForVersion_0.1.1.js @@ -38,44 +38,60 @@ function runTests() { ************************/ test.printHeader("Testing addTwoValues using assertEquals"); - // Testing new method: assertEquals, printSummary and printSubHeader using addTwoValue function with default levelInfo // We haven´t set levelInfo, so we are using the default value: 1 test.printSubHeader("Using default Level Info value (1) for 'addTwoValues' function"); - test.printSubHeader("Expected: Test 1 pass, Test 2 pass with user message, Test 3 fail default message, Test 4 fails with user message"); - test.assertEquals(() => addTwoValues(1, 2), 3); // PASS - test.assertEquals(() => addTwoValues(2, 2), 4, "Valid case: 2 + 2 = 4"); // PASS with message - test.assertEquals(() => addTwoValues(1, 2), 4); // FAIL - test.assertEquals(() => addTwoValues(1, 2), 4, "Expected to fail, because 1+2 != 4"); - test.printSubHeader("Expected: 4-Tests, 2-Tests fail, 2-Tests Pass"); - test.printSummary(); // It should print final result and statistics (two lines) + test.printSubHeader("Expected: Test 1 pass user message, Test 2 pass with default, Test 3 fail user message, Test 4 fails with default message"); + test.assertEquals(() => addTwoValues(2, 2), 4, "Valid case: 2 + 2 = 4"); // Pass + test.assertEquals(() => addTwoValues(1, 2), 3); // Pass + test.assertEquals(() => addTwoValues(1, 2), 4, "Expected to fail, because 1 + 2 != 4"); // Fail + test.assertEquals(() => addTwoValues(1, 2), 4); // Fail + test.printSubHeader("Expected: 4-Test, 2-Tests fail, 2-Tests Pass"); + test.printSummary(); // It should print final result and statistics (two lines); - test.printSubHeader("Testing when fun is boolean"); + test.printSubHeader("Testing when condition is boolean"); test.printSubHeader("Reset counters"); test.resetTestCounters(); - - test.printSubHeader("Expected Test-1 pass with default message, Test-2 pass with user message, Test-3 fail with default message, Test-4 fail with user message"); + test.printSubHeader("Expected Test-1 pass with user message, Test-2 pass with default message, Test-3 fail with user message, Test-4 fail with default message"); + test.assertEquals(1 + 2 == 3, true, "Valid Case"); // Pass test.assertEquals(1 + 2 == 3, true); // Pass - test.assertEquals(1 + 2 == 3, true, "Valid Case"); - test.assertEquals(1 + 1 == 3, true); // FAIL test.assertEquals(1 + 1 == 3, true, "Invalid Case"); + test.assertEquals(1 + 1 == 3, true); // Fail test.printSubHeader("Expected: 4-Tests, 2-Tests fail, 2-Tests Pass"); test.printSummary(); - test.printSubHeader("testing using strings"); - test.printSubHeader("Test-5 pass with user message, Test-6 pass with default message, Test-7 pass with default message, Test-8 fail with default message, Test-9 fail with deafult message"); - test.assertEquals("world" == "world", true, "Expected to pass 'world' = 'world'"); // Pass with user message + test.printSubHeader("Testing using strings conditions and validating the result with boolean condition"); + test.printSubHeader("Test-5 pass with user message, Test-6 pass with default message, Test-7 pass with user message, Test-8 fail with default message, Test-9 fail with deafult message"); + test.assertEquals("world" == "world", true, "Expected to pass 'world' = 'world'"); // Pass test.assertEquals("world" == "world", true); // Pass - test.assertEquals("world" != "World", true, "Expected to pass 'world' != 'World'"); // Pass with user message - test.assertEquals("world" == "World", true); // Fail with default message - test.assertEquals("world" != "world", true); // Fail with default message + test.assertEquals("world" != "World", true, "Expected to pass 'world' != 'World'"); // Pass + test.assertEquals("world" == "World", true); // Fail + test.assertEquals("world" != "world", true); // Fail test.printSubHeader("Expected: 9-Tests, 4-Tests fail, 5-Tests Pass"); test.printSummary(); + test.printSubHeader("Testing using empty message"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.assertEquals(1 + 2 == 3, true, ""); // Pass + test.assertEquals(1 + 1 == 3, true, ""); // Fail + test.printSubHeader("Expected: 2-Tests, 1-Test fail, 1-Test Pass"); + test.printSummary(); + + test.printSubHeader("Testing using strings and validating with string value"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.printSubHeader("Test-1 pass with user message, Test-2 pass with default message, Test-3 fail with user message, Test-4 fail with default message") + test.assertEquals("world", "world", "Expected to pass 'world' = 'world'"); // Pass + test.assertEquals("world", "world"); // Pass + test.assertEquals("world", "World", "Expected to fail 'world' != 'World'"); // Fail + test.assertEquals("world", "World"); // Fail + test.printSubHeader("Expected: 4-Tests, 2-Tests fail, 2-Tests Pass"); + test.printSummary(); test.printSubHeader("Testing undefined input arguments using default message or undefined message"); test.printSubHeader("Reset counters"); test.resetTestCounters(); - test.printSubHeader("Expected: Test-1 pass, Test-2 fail, Test-3 pass, Test-4 fail"); + test.printSubHeader("Expected: Test-1 pass (no arguments), Test-2 fail (only condition), Test-3 pass with undefined message, Test-4 fail with undefined message"); test.assertEquals(); // PASS both are undefined test.assertEquals(1 + 1); // FAIL, because the expectedValue is undefined test.assertEquals(1 + 1, 2, undefined); // Pass but message is undefined @@ -83,6 +99,120 @@ function runTests() { test.printSubHeader("Expected: 4-Tests, 2-Tests fail, 2-Tests pass"); test.printSummary(); + test.printSubHeader("Testing an unexpected error occurred"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.printSubHeader("Expected: unexpected error with user message"); + test.assertEquals( + () => addTwoValuesSafe("a", "b"), // an unexpected error will thrown + "a + b", + "Expected: unexpected error" + ); + test.printSubHeader("Expected: unexpected error with default message"); + test.assertEquals( + () => addTwoValuesSafe("a", "b"), // an unexpected error will thrown + "a + b" + ); + + // Testing the existing assert function work as expected for backward compatibility + test.printHeader("Testing addTwoValues using assert"); + test.printSubHeader("Test-1 Pass with user message, Test-2 pass with default message, Test-3 fail with user message, Test-4 fail with default message"); + test.assert(() => addTwoValues(1, 2) == 3, "Valid case: 1 + 2 = 3"); // Pass + test.assert(() => addTwoValues(1, 2) == 3); // Pass + test.assert(() => addTwoValues(1, 2) == 4, "Invalid case: 1 + 2 != 4"); // Fail + test.assert(() => addTwoValues(1, 2) == 4); // Fail + test.printSubHeader("4-Tests, 2-Tests Fail, 2-Tests Pass"); + test.printSummary(); + + test.printSubHeader("Testing when condition is boolean"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.printSubHeader("Test-1 Pass with user message, Test-2 pass with default message, Test-3 fail user message, Test-4 fail with default message"); + test.assert(1 + 2 == 3, "Valid case: 1 + 2 = 3"); // Pass + test.assert(1 + 2 == 3); // Pass + test.assert(1 + 1 == 3, "Invalid case: 1 + 1 != 3"); // Fail + test.assert(1 + 1 == 3); // Fail + test.printSubHeader("4-Tests, 2-fail, 2-Tests pass"); + test.printSummary(); + + + test.printSubHeader("Testing using strings conditions and validating the result with boolean condition"); + test.printSubHeader("Test-5 pass with user message, Test-6 pass with default message, Test-7 pass with user message, Test-8 fail with user message, Test-9 fail with deafult message, Test-10 fail with default message"); + test.assert("world" == "world", "Expected to pass 'world' = 'world'"); // Pass + test.assert("world" == "world"); // Pass + test.assert("world" != "World", "Expected to pass 'world' != 'World'"); // Pass + test.assert("world" == "World", "Expected to fail 'word' != 'World"); // Fail + test.assert("world" == "World"); // Fail + test.assert("world" != "world"); // Fail + + test.printSubHeader("Expected: 10-Tests, 5-Tests fail, 5-Tests Pass"); + test.printSummary(); + + test.printSubHeader("Testing using empty message"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.assert(1 + 2 == 3, ""); // Pass + test.assert(1 + 1 == 3, ""); // Fail + test.printSubHeader("Expected: 2-Tests, 1-Test fail, 1-Test Pass"); + test.printSummary(); + + test.printSubHeader("Testing undefined input arguments"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.printSubHeader("Expected: Test-1 Fail (no arguments), Test-2 Pass with (undefined message), Test-3 Pass(with undefined message), Test-4 Fail (with undefined message)"); + test.assert(); // Fail, not a valid condition + test.assert(undefined == undefined); // Pass + test.assert(1 + 1 == 2); // Pass + test.assert(1 + 1 == 3); // Fail + test.printSubHeader("Expected: 4-Tests, Fail=2, Pass=2"); + test.printSummary(); + + test.printSubHeader("Testing an unexpected error occured"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.printSubHeader("Expected: unexpected error with user message"); + test.assert( + () => addTwoValuesSafe("a", "b"), // unexpected error was thrown + "Expected: unexpected error" + ); + test.printSubHeader("Expected: unexpected error with default message"); + test.assert( + () => addTwoValuesSafe("a", "b") // unexpected error was thrown + ); + + test.printSubHeader("Testing set levelInfo"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + test.assert(() => test.levelInfo = 1.1, "Valid case: 1.1 is a number"); + test.catchErr( + () => test.levelInfo = "non number", // Expected to throw a TypeError + "Input argument value should be a number", // this is the error message we are expecting + "Throw an error because value is a string. Expected error type and message to be correct", + TypeError + ); + test.printSubHeader("2-Tests, 0-fail, 2-pass"); + test.printSummary(); + + test.printHeader("Testing is2Array"); + test.printSubHeader("Reset counters"); + test.resetTestCounters(); + + const array2D = [ + ["a1", "a2"], + ["b1", "b2"] + ]; + const array = ["a1", "a2"]; + test.is2dArray(array2D, "Expected to pass, values is an array of arrays"); + test.is2dArray(null, "Expected to fail, it is null input value"); + test.is2dArray(array, "Expected to fail, it is a single array"); + test.is2dArray([array], "Expected to pass, it is a 2D array"); + test.is2dArray([array]); // PASS + test.is2dArray(array); // FAIL + test.is2dArray(addTwoValuesSafe(1, 2)); // FAIL + test.is2dArray(() => addTwoValuesSafe("a", "b")); // FAIL + test.printSubHeader("8-Tests, 5-fail, 3-pass"); + test.printSummary(); + test.printHeader("Testing catching errors using catchErr"); test.printSubHeader("Reset counters"); test.resetTestCounters(); @@ -224,7 +354,7 @@ function runTests() { Error ); -test.printSubHeader("Expected to fail: no error should be thrown, error type and message are correct, using default message"); + test.printSubHeader("Expected to fail: no error should be thrown, error type and message are correct, using default message"); test.catchErr( () => addTwoValuesSafe(1, 2), // we are passing valid values (no error thrown) "Input argument is not a valid number", // this is the error message we are expecting @@ -279,61 +409,32 @@ test.printSubHeader("Expected to fail: no error should be thrown, error type and test.printSubHeader("Expected: 12-Tests, 12-Tests fail, 0-Pass"); test.printSummary(); - // Testing similar basic tests but in silent mode (LevelInfo = 0) - test.printHeader("Testing the same assertEquals tests with setting levelInfo = 0"); // It logs out, because we haven't changed LevelInfo yet - test.setLevelInfo(0); // 0-Only summary result, 1-Detail results - // Because LevelInfo = 0 we use the console for traceability purpose - console.log("LevelInfo = " + test.getLevelInfo()); + // Testing similar basic tests but in silent mode (levelInfo = 0) + test.printHeader("Testing assert and assertEquals under silent mode (levelInfo = 0)"); // It logs out, because we haven't changed levelInfo yet + test.levelInfo = 0; // 0-Only summary result, 1-Detail results + // Because levelInfo = 0 we use the console for traceability purpose + console.log("levelInfo = " + test.levelInfo); console.log("Reset counters"); test.resetTestCounters(); - test.assertEquals(() => addTwoValues(1, 2), 3); - test.assertEquals(() => addTwoValues(2, 2), 4, "Valid case: 2 + 2 = 4"); - test.assertEquals(() => addTwoValues(1, 2), 4); - test.assertEquals(() => addTwoValues(1, 2), 4, "Expected to fail, because 1+2 != 4"); + console.log("Testing assertEquals"); + console.log("********************"); + test.assertEquals(() => addTwoValues(2, 2), 4, "Valid case: 2 + 2 = 4"); // Pass + test.assertEquals(() => addTwoValues(1, 2), 3); // Pass + test.assertEquals(() => addTwoValues(1, 2), 4, "Expected to fail, because 1+2 != 4"); // Fail + test.assertEquals(() => addTwoValues(1, 2), 4); // Fail + console.log("Expected: Some tests failed (one line)"); test.printSummary(); // Only summary result: Some test failed - test.printSubHeader("Reset counters"); + console.log("Reset counters"); test.resetTestCounters(); console.log("Shows only the summary line, the counters are reseted so no tests, therefore all test passed"); test.printSummary(); //Shows only summary line - // Testing the existing assert function work as expected - test.printSubHeader('Setting default level Info'); - test.setLevelInfo(1); - - test.printHeader("Testing addTwoValues using assert"); - test.printSubHeader("LevelInfo = " + test.getLevelInfo()); - test.printSubHeader("Test-1 Pass, Test-2 Fail"); - test.assert(() => addTwoValues(1, 2) == 3, "Valid case: 1 + 2 = 3"); - test.assert(() => addTwoValues(1, 2) == 4, "Invalid case: 1 + 2 != 4"); - test.printSubHeader("Expected to log out two lines: statistics and summary"); - test.printSummary(); - - test.printSubHeader("Testing when condition is boolean"); - test.printSubHeader("Reset counters"); - test.resetTestCounters(); - test.printSubHeader("Test-1 Pass, Test-2 pass (empty string), Test-3 fail"); - test.assert(1 + 2 == 3, "Valid case: 1 + 2 = 3"); - test.assert(1 + 2 == 3, ""); // testing empty message - test.assert(1 + 1 == 3, "Invalid case: 1 + 1 != 3"); - test.printSubHeader("3-Tests, 1-fail, 2 pass"); - test.printSummary(); - - test.printSubHeader("Testing undefined input arguments"); - test.printSubHeader("Expected: Test-3 Fail (no arguments), Test-4 Pass (with undefined message), Test-5 Pass(with undefined message), Test-6 Pass(with undefined message)"); - test.assert(); // Expected to fail, not a valid condition - test.assert(undefined == undefined); // Pass - test.assert(1 + 1 == 2); - test.assert(1 + 1 == 3); - test.printSubHeader("Expected: 6-Tests, Fail=3, Pass=3"); - test.printSummary(); - - test.printSubHeader("Testing under silent mode: one test passed, one failed"); - test.setLevelInfo(0); - console.log("LevelInfo = " + test.getLevelInfo()); - console.log("Reseting the counters"); + console.log("Testing assert"); + console.log("**************"); + console.log("Reset counters"); test.resetTestCounters(); - test.assert(() => addTwoValues(1, 2) == 3, "Valid case: 1 + 2 = 3"); - test.assert(() => addTwoValues(1, 2) == 4, "Invalid case: 1 + 2 != 4"); + test.assert(() => addTwoValues(1, 2) == 3, "Valid case: 1 + 2 = 3"); // Pass + test.assert(() => addTwoValues(1, 2) == 4, "Invalid case: 1 + 2 != 4"); // Fail console.log("Expected to see some test failed"); test.printSummary(); @@ -347,9 +448,19 @@ test.printSubHeader("Expected to fail: no error should be thrown, error type and test.printSummary(); console.log("Changing the level info to 1"); - test.setLevelInfo(1); - test.printSubHeader("Showing now printSummary with two lines for the previous set of tests"); + test.levelInfo = 1; + test.printSubHeader("Showing now printSummary with two lines from the previous set of tests"); + test.printSummary(); + test.printSubHeader("Set levelInfo to 1.1"); + test.levelInfo = 1.1; + test.printSubHeader("Showing now printSummary with two lines from the previous set of tests"); test.printSummary(); + test.printSubHeader("Set levelInfo to 0.1"); + test.levelInfo = 0.1; + console.log("Showing now printSummary with the summary line only from the previous set of tests"); + test.printSummary(); + console.log("Changing the level info to 1"); + test.levelInfo = 1; test.printHeader("Testing enable = false, the print-family functions print no information"); test.disable();