简体   繁体   中英

How to resolve “Cannot read property of undefined” in Node.js

I'm newbie in JS and I need to create simple CSV parser for test data. And I have a very strange problem.

This is code of my test:

'use strict';

const assert = require('assert');
const HomePage = require('../pages/HomePage');
const csv = require('../tools/CsvReader');

describe('Check if values are correct', () => {

    let dataProvider = csv.readFromCsv("test/resources/places.csv");

    function valuesTest(city, expectedLat, expectedLong) {
        it('Data must match', () => {
            let searchPlace = HomePage
                .open()
                .findByPlaceName(city)
                .getSearchPlace();
            assert.strictEqual(searchPlace.getLatitude(), expectedLat);
            assert.strictEqual(searchPlace.getLongtitude(), expectedLong);
            console.log(dataProvider[0].CITY);
        });
    }

    for (let i = 0; i < dataProvider.length; i++) {
        valuesTest(dataProvider[i].CITY, dataProvider[i].LAT, dataProvider[i].LONG)
    }

});

And code of my CSV-reader:

'use strict';

const csv = require('csv-parser');
const fs = require('fs');

class CsvReader {


    readFromCsv(path) {
        let results = [];

        fs.createReadStream(path)
            .pipe(csv())
            .on('data', (data) => results.push(data));
        return results;
    }

}

module.exports = new CsvReader();

And this is my CSV:

CITY,LAT,LONG
Kyiv,50.447731,30.542721
Lviv,49.839684,24.029716
Ivano-Frankivsk,48.922634,24.711117

The problem is as follows: Why can I use the variable "dataProvider" in the "valuesTest" block and it works correctly and returns the value of the CITY variable, but in the "for" loop I can't use it (variable "dataProvider", "CITY", etc. is unavailable there), although they are located in the same block "describe".

Your CSV Reader is an asynchronous operation. I suspect your for loop gets executed even before the csv is parsed and value is returned. Try putting your for loop in a function and passing to the readFromCsv function as a callback. Call this function on the data event, where you will be sure to get the data.

You should pass a callback to readFromCsv that will be executed when the createReadStream is complete. You can determine when createReadStream is complete by listening to its end event. (See csv-parser example code for instance).

In your case, you could do something like:

readFromCsv(path, onEnd) {
    let results = [];

    fs.createReadStream(path)
        .pipe(csv())
        .on('data', (data) => results.push(data))
        .on('end', () => onEnd(results));
}

csv.readFromCsv("test/resources/places.csv", function onData(data) {
    for (let i = 0; i < data.length; i++) {
        valuesTest(data[i].CITY, data[i].LAT, data[i].LONG);
    } 
});

You should use the end event instead of the data event to determine when the stream is complete. If you returned after the first data event, you might not get all of the data.

The data event is fired once per data chunk. If your data has more than one "chunk", you'll truncate it if you return in the data callback. See nodeJS docs for details on the different event types. You might not notice a difference with small test files, but make a larger file and you'll see the difference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM