Unit and Functional Tests not passing on FCC

For the metric-imperial-converter I can pass all of the user stories other than the last two:

  • All 16 unit tests are complete and passing.
  • All 5 functional tests are complete and passing.

Locally (npm run dev) these all successfully pass so I am unsure why it is any different when hosted on heroku. Help appreciated.

Link to GitHub repo: GitHub - aidansabin/silver-journey-metimpconv
Heroku app: https://shielded-garden-21508.herokuapp.com/

api.js

'use strict';

const expect = require('chai').expect;
const ConvertHandler = require('../controllers/convertHandler.js');

module.exports = function (app) {

  let convertHandler = new ConvertHandler();

  app.route('/api/convert').get((req, res) => {
    let input = req.query.input;
    let initNum = convertHandler.getNum(input);
    let initUnit = convertHandler.getUnit(input);

    if (!initNum && !initUnit) {
      res.send('invalid number and unit');
    } else if (!initNum) {
      res.send('invalid number');
    } else if (!initUnit) {
      res.send('invalid unit');
    }

    let returnNum = convertHandler.convert(initNum, initUnit);
    let returnUnit = convertHandler.getReturnUnit(initUnit);
    let string = convertHandler.getString(initNum, initUnit, returnNum, returnUnit);

    if (initUnit === 'l') {
      initUnit = 'L';
    }
    if (returnUnit === 'l') {
      returnUnit = 'L';
    }
    
    res.send({ initNum: parseFloat(initNum), initUnit, returnNum: parseFloat(returnNum), returnUnit, string });
  });
};

unit-tests:

const chai = require('chai');
let assert = chai.assert;
const ConvertHandler = require('../controllers/convertHandler.js');

let convertHandler = new ConvertHandler();

suite('Unit Tests', function() {

  suite('Function convertHandler.getNum(input)', function() {

    test('Whole number input', function(done) {
      let input = '32L';
      assert.equal(convertHandler.getNum(input), 32);
      done();
    });

    test('Decimal number input', function(done) {
      let input = '32.2L';
      assert.equal(convertHandler.getNum(input), 32.2);
      done();
    });

    test('Fractional input', function(done) {
      let input = '3/4L';
      assert.equal(convertHandler.getNum(input), 3/4);
      done();
    });

    test('Fractional input with decimal', function(done) {
      let input = '3.5/4L';
      assert.equal(convertHandler.getNum(input), 3.5/4);
      done();
    });

    test('Double fraction input', function(done) {
      let input = '3/2/3L';
      assert.equal(convertHandler.getNum(input), undefined);
      done();
    });

    test('No numerical input', function(done) {
      let input = 'L';
      assert.equal(convertHandler.getNum(input), 1);
      done();
    });
  });

  suite('Function convertHandler.getUnit(input)', function() {

    test('Valid unit input', function(done) {
      let input = ['mi', 'km', 'gal', 'l', 'lbs', 'kg'];
      input.forEach(val => assert.equal(convertHandler.getUnit(val), val));
      done();
    });

    test('Invalid unit input', function(done) {
      let input = '32Li';
      assert.equal(convertHandler.getUnit(input), undefined);
      done();
    });
  });

  suite('Function convertHandler.getReturnUnit(initUnit)', function() {
    test('Correct return unit', function(done) {
      let input = ['mi', 'km', 'gal', 'l', 'lbs', 'kg'];
      let result = ['km', 'mi', 'l', 'gal', 'kg', 'lbs'];
      input.forEach((val, i) => assert.equal(convertHandler.getReturnUnit(val), result[i]));
      done();
    });
  });

  suite('Function convertHandler.spellOutUnit(unit)', function() {
    test('Correct spelled-out string', function(done) {
      let input = ['mi', 'km', 'gal', 'l', 'lbs', 'kg'];
      let result = ['miles', 'kilometers', 'gallons', 'liters', 'pounds', 'kilograms'];
      input.forEach((val, i) => assert.equal(convertHandler.spellOutUnit(val), result[i]));
      done();
    });
  });

  suite('Function convertHandler.convert(initNum, initUnit)', function() {

    test('Convert gal to L', function(done) {
      let input = ['32', 'gal'];
      assert.equal(convertHandler.convert(input[0], input[1]), 121.13312);
      done();
    });

    test('Convert L to gal', function(done) {
      let input = ['32', 'l'];
      assert.equal(convertHandler.convert(input[0], input[1]), 8.45351);
      done();
    });

    test('Convert mi to km', function(done) {
      let input = ['32', 'mi'];
      assert.equal(convertHandler.convert(input[0], input[1]), 51.49888);
      done();
    });

    test('Convert km to mi', function(done) {
      let input = ['32', 'km'];
      assert.equal(convertHandler.convert(input[0], input[1]), 19.88393);
      done();
    });

    test('Convert lbs to kg', function(done) {
      let input = ['32', 'lbs'];
      assert.equal(convertHandler.convert(input[0], input[1]), 14.51494);
      done();
    });

    test('Convert kg to lbs', function(done) {
      let input = ['32', 'kg'];
      assert.equal(convertHandler.convert(input[0], input[1]), 70.54798);
      done();
    });
  })
});

functional tests:

const chaiHttp = require('chai-http');
const chai = require('chai');
let assert = chai.assert;
const server = require('../server');

chai.use(chaiHttp);

suite('Functional Tests', function() {

  test('Convert 10L (valid input)', function(done) {
    chai
      .request(server)
      .get('/api/convert')
      .query({ input: '10L' })
      .end((err, res) => {
        assert.equal(res.status, 200);
        assert.equal(res.body.initNum, 10);
        assert.equal(res.body.initUnit, 'L');
        assert.equal(res.body.returnNum, 2.64172);
        assert.equal(res.body.returnUnit, 'gal');
        done();
      });
  });

  test('Convert 32g (invalid input)', function(done) {
    chai
      .request(server)
      .get('/api/convert')
      .query({ input: '32g' })
      .end((err, res) => {
        assert.equal(res.status, 200);
        assert.equal(res.body.initUnit, undefined);
        done();
      });
  });

  test('Convert 3/7.2/4kg (invalid input)', function(done) {
    chai
      .request(server)
      .get('/api/convert')
      .query({ input: '3/7.2/4kg' })
      .end((err, res) => {
        assert.equal(res.status, 200);
        assert.equal(res.body.initNum, undefined);
        done();
      });
  });

  test('Convert 3/7.2/4g (invalid number and unit)', function(done) {
    chai
      .request(server)
      .get('/api/convert')
      .query({ input: '3/7.2/4g' })
      .end((err, res) => {
        assert.equal(res.status, 200);
        assert.equal(res.body.initNum, undefined);
        assert.equal(res.body.initUnit, undefined);
        done();
      });
  });

  test('Convert kg (no number input)', function(done) {
    chai
      .request(server)
      .get('/api/convert')
      .query({ input: 'kg' })
      .end((err, res) => {
        assert.equal(res.status, 200);
        assert.equal(res.body.initNum, 1);
        assert.equal(res.body.initUnit, 'kg');
        done();
      });
  });
});

convertHandler.js

function ConvertHandler() {
  const unitPhrases = {
    "mi": "miles",
    "km": "kilometers",
    "gal": "gallons",
    "l": "liters",
    "kg": "kilograms",
    "lbs": "pounds"
  }
  const unitPairs = {
    "mi": "km",
    "km": "mi",
    "gal": "l",
    "l": "gal",
    "kg": "lbs",
    "lbs": "kg"
  }

  this.checkFraction = function(input) {
    if (input[0].split('/').length === 2) {
      return true;
    } else if (input[0].split('/').length > 2) {
      return undefined;
    } else {
      return false;
    }
  }

  this.getNum = function(input) {
    let result = input.match(/[.\d\/]+/g) || ['1'];
    if (this.checkFraction(result) === undefined) {
      return undefined;
    } else if (this.checkFraction(result)) {
      let fraction = result[0].split('/');
      return fraction[0] / fraction[1];
    } else {
      return result[0];
    }
  };

  this.getUnit = function(input) {
    let result = input.match(/[a-z]+/ig);
    if (unitPhrases.hasOwnProperty(result[0].toLowerCase())) {
      return result[0].toLowerCase();
    } else {
      return undefined;
    }
  };

  this.getReturnUnit = function(initUnit) {
    let result = unitPairs[initUnit];
    return result;
  };

  this.spellOutUnit = function(unit) {
    let result = unitPhrases[unit];
    return result;
  };

  this.convert = function(initNum, initUnit) {
    const galToL = 3.78541;
    const lbsToKg = 0.453592;
    const miToKm = 1.60934;
    let result;
    switch(initUnit.toString()) {
      case "gal":
        result = initNum * galToL;
        break;
      case "l":
        result = initNum / galToL;
        break;
      case "lbs":
        result = initNum * lbsToKg;
        break;
      case "kg":
        result = initNum / lbsToKg;
        break;
      case "mi":
        result = initNum * miToKm;
        break;
      case "km":
        result = initNum / miToKm;
        break;
    }
    return result.toFixed(5);
  };

  this.getString = function(initNum, initUnit, returnNum, returnUnit) {
    let result = initNum + " " + this.spellOutUnit(initUnit) + " converts to " + returnNum + " " + this.spellOutUnit(returnUnit);
    return result;
  };

}

module.exports = ConvertHandler;

Hello!

You have a bug here.

You’re effectively sending the expected response, but for unit and functional tests, an error is thrown because the execution is not actually stopped.

Let’s say we send just a value and no unit to your API. Your code would run like this:

// GET: /api/convert?input=1
let input = req.query.input;
let initNum = convertHandler.getNum(input);
let initUnit = convertHandler.getUnit(input);

// initNum = 1
// initUnit = undefined

if (!initNum && !initUnit) { // Skipped
  res.send('invalid number and unit');
} else if (!initNum) { // Skipped
  res.send('invalid number');
} else if (!initUnit) { // This one is executed
  // Ok, you send the correct response.
  res.send('invalid unit');
  // However, the execution doesn't stop
}

// And the next line is this:
let returnNum = convertHandler.convert(initNum, initUnit);
// ...where an error is thrown and the tests fail

As a general rule, if you’re sending more than one response (res.send, or any other res.*), put a return in front:

if (!initNum && !initUnit) {
  return res.send('invalid number and unit');
} else if (!initNum) {
  return res.send('invalid number');
} else if (!initUnit) {
  return res.send('invalid unit');
}

// Everything should be valid at this point
res.send(...);

adding the returns did not seem to help unfortunately although i do appreciate that they do actually need to be there so thank you for that tip.

after adding them and then changing the code a bit it seems to function perfectly fine and I feel like all I’m doing is making loads of different invalid input handling systems which I doubt is actually my problem as FCC just wants to know if my unit and functional tests.

It now also fails the user story “You can use fractions, decimals or both in the parameter (ie. 5, 1/2, 2.5/6), but if nothing is provided it will default to 1.” despite the fact that it handles fractions and decimals without issue and also defaults to 1.

Unless you can see or explain any obvious errors I am making is this just a case of FCC having tests that are a bit too specific? Honestly at a loss as to what I’m doing wrong

1 Like

Hello there,

Running your app presents these errors in the console:

I hope this clarifies the issues

EDIT: This is where the test error is going wrong: silver-journey-metimpconv/fcctesting.js at gomix · aidansabin/silver-journey-metimpconv · GitHub

1 Like

thank you, I always forget to check the console of the actual fcc testing page!
just one last question though… how could you tell that the code was failing at that specific point (the part mentioned in you edit)?

A bit of experience :wink: But the experience came from finding where { status: 'unavailable' } is responded with.

@sky020,

I hope you don’t mind that I respond to this post, but I have a ‘similar’ console error:

frame-runner.js:98 Error: At least 16 tests passed: expected 0 to be at least 16
    at eval (eval at <anonymous> (VM897 frame-runner.1c375504ee6a75a80029.js:2), <anonymous>:18:11)
_callee$ @ frame-runner.js:98
frame-runner.js:98 Error: At least 5 tests passed: expected 0 to be at least 5
    at eval (eval at <anonymous> (VM897 frame-runner.1c375504ee6a75a80029.js:2), <anonymous>:18:11)

All unit tests pass locally and on Heroku, but not on fcc. There are no functional tests yet. But for both unit and functional tests result in console is similar, so it seems fcc is not seeing my tests. I added the .env with NODE_ENV=test to Heroku config variables. Is there anything else I need to set for fcc to access the tests? All other tests on fcc pass.

my solution: "https://unitconvert.herokuapp.com
my github: "https://github.com/danmikes/heroku

I removed the environment variable on heroku and now I am receiving this message:

frame-runner.js:98 Error: expected { status: 'unavailable' } to be an array
    at eval (eval at <anonymous> (VM897 frame-runner.1c375504ee6a75a80029.js:2), <anonymous>:18:11)

So now it looks even more similar to @aidansabin 's message. So how to fix this?

I got the functional tests to work, so only the unit tests. Have no idea where to look

Hello there,

I recommend for future posts you open your own topic.

As for the issue, the tests expect the boilerplate format to be followed. That is, the tests expect the context to have the word Unit Tests

const unitTests = getTests.filter(test => {
  return !!test.context.match(/Unit Tests/gi);
});

Hope this helps

Ok, I will. Thank you for your reply.

This is probably a stupid question, but where do I enter this context?

Got it, thanks a lot!

Not a silly question at all. (Glad you got it :rocket: )

If you have any more questions, do feel free to open a new topic :slightly_smiling_face:

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.