6 Classes + Testing

Classes + Testing

Objectives

  1. ECMAScript modules (ESM).
  2. Classes, constructors, and inheritance.
  3. Fields, methods, and encapsulation.
  4. Static fields and methods.
  5. Abstract classes and methods?
  6. JavaScript Object Notation (JSON) for serialization and deserialization.
  7. Unit testing using test runners (opens in a new tab).

1 Modules

Build a simple banking application that manages accounts and provides various operations such as depositing to, withdrawing from, adding, removing, and retrieving accounts.

  1. Initialize your directory as a package using bun init, then (optionally) set the module format to ESM in package.json: { "type": "module" }.

  2. Create a Bank module, bank.js, where you declare an array, accounts, of object literals to store the following accounts:

    idbalancetype
    08a45dd424502.30Savings
    a9e24658414,100.00Current
    7160dca60134,420.55Current
    2efde49d9d61,023.69Savings
  3. Implement and export the following functions in the Bank module:

    1. add(account) adds either a Savings or Current account to the array of accounts and returns its identifier.

      The identifier is generated using the Nano ID (opens in a new tab) (nanoid (opens in a new tab)) package.

    2. getAll() return all accounts.

    3. get(id) returns an account by identifier.

    4. remove(id) deletes an account by identifier.

    5. deposit(id, amount) adds the amount to the account balance.

    6. withdraw(id, amount) subtracts the amount from the account balance, if it has sufficient balance.

    7. totalBalance() returns the total balance for all accounts.

    8. deductFee(fee) deducts a monthly fee from the balance of all Current accounts.

    9. distributeBenefit(percentage) increases the balance of all Savings accounts by a benefit percentage.

    10. toJSON() returns the array of accounts as a JSON string.

    11. fromJSON(json) parses the JSON string and returns an array of accounts.

  4. Write an application, app.js, to test the Bank module by creating a couple of Current and Savings accounts, then performing a few deposits and withdrawals:

    1. Charge a monthly fee of 10.
    2. Display the total balance of all accounts after charging the monthly fee.
    3. Distribute a 6% benefit.
    4. Display the total balance of all accounts after distributing the benefit.
    5. Display the list of accounts in JSON format.

2 Classes

Classes in JavaScript are, for the most part, syntactic sugar for prototype-based inheritance (opens in a new tab).

UML diagram for Bank, BankAccount, CurrentAccount, and SavingsAccount classesBank#accountsconstructor(accounts)getaccounts()get(id)add(account)remove(id)totalBalance()toString()toJSON()BankAccount#id#balanceconstructor(account)getid()getbalance()deposit(amount)withdraw(amount)toString()toJSON()#generateId()CurrentAccount#feeconstructor(account)charge()toString()toJSON()getfee()setfee(amount)toJSON()SavingsAccount#benefitconstructor(account)distribute()toString()toJSON()getbenefit()setbenefit(amount)toJSON()

Rewrite the application from 1 Modules using the classes Bank, BankAccount, CurrentAccount, and SavingsAccount, as specified in the UML diagram, where a Bank is composed of multiple BankAccounts and each BankAccount is either a CurrentAccount or a SavingsAccount.

Complete the 3 Exceptions guide to learn more about throwing and catching exceptions.

3 Unit Testing

Bun’s test runner (opens in a new tab) is a built-in, Jest-compatible testing framework that supports test-driven development (TDD) and behavior-driven development (BDD). It provides describe, test, and expect functions with no external dependencies required.

Install the Bun for Visual Studio Code (opens in a new tab) extension for live in-editor error messages, test runner and debugger support, and running scripts from package.json.

There are numerous testing tools, frameworks, and libraries listed under Links → Testing.

  1. Install the convert (opens in a new tab) package using bun add convert, then import and use it to convert a few units:

    index.js
    import convert from "convert";
     
    console.log("360 s = %d m", convert(360, "seconds").to("minutes"));
    console.log("5 km = %f mi", convert(5, "kilometers").to("nautical miles"));
    console.log("12 lb = %d oz", convert(12, "pounds").to("ounces"));
    console.log("8,192 B = %d KiB", convert(8192, "bytes").to("KiB"));
    console.log("10 atm = %f kPa", convert(10, "atmospheres").to("kPa"));
    console.log("451 °F = %f °C", convert(451, "fahrenheit").to("celsius"));
  2. Create a test/convert.test.js test specification file using Bun’s built-in test runner to test some of the unit conversions provided by convert:

    test/convert.test.js
    import convert from "convert";
    import { describe, test, expect } from "bun:test";
     
    describe("convert", () => {
      describe("time", () => {
        test("should convert 60 s to 1 m", () => {
          expect(convert(60, "seconds").to("minutes")).toBe(1);
        });
        test("should convert 1 h to 3600 s", () => {
          expect(convert(1, "hours").to("seconds")).toBe(3600);
        });
      });
     
      describe("mass", () => {
        test("should convert 1 kg to 2.2 lb", () => {
          expect(convert(1, "kg").to("pounds")).toBe(2.2);
        });
      });
    });
  3. Run the tests using bun test and fix the failing test case using Number.toFixed() (opens in a new tab).

  4. Add more test cases using other matchers (opens in a new tab) provided by Bun:

    test/convert.test.js
    import convert from "convert";
    import { describe, test, expect } from "bun:test";
     
    describe("convert", () => {
      describe("time", () => {
        test("should convert 60 s to 1 m", () => {
          expect(convert(60, "seconds").to("minutes")).toBe(1);
        });
        test("should convert 1 h to 3600 s", () => {
          expect(convert(1, "hours").to("seconds")).toBe(3600);
        });
      });
     
      describe("mass", () => {
        test("should convert 1 kg to 2.2 lb", () => {
          expect(Number(convert(1, "kg").to("pounds").toFixed(1))).toBe(2.2);
        });
      });
     
      describe("best", () => {
        test("should convert to best with unit and quantity properties", () => {
          expect(convert(1500, "meters").to("best")).toHaveProperty("unit");
          expect(convert(1500, "meters").to("best")).toHaveProperty("quantity");
        });
      });
    });
  5. Write unit tests for each method of the Bank class and run them using bun test.

Structure

  • Resources