📅
📁 Development & Coding
👁️ 95 views

Problem: Tests failing due to truncation with Math.floor

If your unit tests are failing with mismatches like 67.872 vs 67.87 or 2.3 vs 2.29, the culprit is likely a truncation helper that uses Math.floor (or any floor-based truncation). Flooring removes digits instead of rounding, producing systematically smaller values and different numbers of decimals.

Root cause

  • Truncation (Math.floor) discards fractional digits — it does NOT round to nearest.
  • Tests expect either the exact multiplication result or normal rounding behavior — not truncated values.
  • So a helper called strictDecimal that calls Math.floor(value * 100) / 100 will produce wrong assertions.

Fix strategy (high level)

  • Stop truncating values in cartTotal. Return the raw product (exact multiplication) unless tests explicitly require a fixed decimal format.
  • If you need fixed decimals, round using Math.round (or a small rounding helper).
  • Keep your filterEntries, mapEntries, reduceEntries utilities — they are fine.

Drop-in replacement code (no truncation)


// Utilities: filter/map/reduce for objects
function filterEntries(obj, callback) {
  return Object.fromEntries(
    Object.entries(obj).filter(([k, v]) => callback([k, v]))
  );
}

function mapEntries(obj, callback) {
  return Object.fromEntries(
    Object.entries(obj).map(([k, v]) => callback([k, v]))
  );
}

function reduceEntries(obj, callback, initial) {
  const entries = Object.entries(obj);
  let acc = initial;
  if (acc === undefined) {
    if (entries.length === 0) throw new TypeError("Reduce of empty object with no initial value");
    acc = entries[0];
    for (let i = 1; i < entries.length; i++) acc = callback(acc, entries[i]);
  } else {
    for (let i = 0; i < entries.length; i++) acc = callback(acc, entries[i]);
  }
  return acc;
}

// Proper rounding helper: rounds to d decimals (nearest)
function roundDecimal(num, d) {
  const p = Math.pow(10, d);
  return Math.round(num * p) / p;
}

// Example: totalCalories (keeps one-decimal rounding like sample)
function totalCalories(cart) {
  return Math.round(
    reduceEntries(
      cart,
      (acc, [k, amount]) => acc + (nutritionDB[k].calories * amount) / 100,
      0
    ) * 10
  ) / 10;
}

function lowCarbs(cart) {
  return filterEntries(
    cart,
    ([k, amount]) => (nutritionDB[k].carbs * amount) / 100 < 50
  );
}

// Fixed cartTotal: no truncation — exact multiplication results
function cartTotal(cart) {
  return mapEntries(cart, ([k, amount]) => {
    const nut = nutritionDB[k];
    const factor = amount / 100;
    const result = {};
    for (const key in nut) {
      if (Object.prototype.hasOwnProperty.call(nut, key)) {
        // Return the exact product. If you want 2-decimal rounding, use:
        // result[key] = roundDecimal(nut[key] * factor, 2);
        result[key] = nut[key] * factor;
      }
    }
    return [k, result];
  });
}

When to round vs return raw values

  • Return raw product when tests expect precise multiplication results or when further aggregation is required (safest).
  • Round to N decimals only when the UI or test explicitly requires formatted numbers. Use roundDecimal above to round to nearest.
  • Avoid truncation (floor) unless you deliberately want to always keep the lower bound (rare).

Example: sample inputs & expected outputs

Given nutritionDB entry:


nutritionDB["itemA"] = { calories: 200, carbs: 34.5, protein: 7.2 };
cart = { itemA: 33 }; // amount = 33 (meaning 33%)

Raw product for calories = 200 * 0.33 = 66

Raw product for carbs = 34.5 * 0.33 = 11.385

If you had truncated to 2 decimals using Math.floor you'd get 11.38, but if test expects 11.385 or 11.39 (rounded), floor will break.

Testing tips (quick unit test examples)


// Node.js assert example
const assert = require('assert');

const nutritionDB = {
  a: { calories: 100, carbs: 20.5 },
  b: { calories: 50, carbs: 10.2 }
};

const cart = { a: 50, b: 25 }; // 50% and 25%
const totals = cartTotal(cart);

// exact products:
assert.strictEqual(totals.a.calories, 50);            // 100 * 0.5
assert.strictEqual(totals.a.carbs, 20.5 * 0.5);       // 10.25
// If tests expect rounding to 2 decimals:
assert.strictEqual(roundDecimal(totals.a.carbs, 2), 10.25);

Summary

  • Truncation with Math.floor removes digits and breaks equality tests.
  • Fix cartTotal by returning raw multiplication results or by rounding with Math.round when required.
  • Use a roundDecimal helper to get consistent, nearest-value rounding.