Интеграционное тестирование Nodejs/Express/Mongoose с Jest/Supertest Одна модель Mongoose экономит, одна модель Mongoose — нет

Я использую Postman и пользовательский интерфейс ReactJS для вызова этого выполнения регистрации, и он работает так, как я ожидаю. Как ни странно, интеграционные тесты Jest и Supertest не дают ожидаемых результатов. При интеграционном тестировании Профиль создается, а Пользователь нет.

Архитектура довольно проста. MongoDB в контейнере Docker и Node, использующий nodemon в VSCode.

Я должен делать что-то не так, я просто не могу определить, что это такое.


// The Integration Test __test__/users/../user.test.js

const app = require('../../app');
const uuidv4 = require('uuid/v4');
const User = require('../../src/models/User');
const Profile = require('../../src/models/Profile');
const bcrypt = require('bcryptjs');
const mongoose = require('mongoose');
const request = require("supertest");

const {
    MONGO_URI,
    TEST_DB_NAME
} = process.env;
let DB_URI = MONGO_URI + TEST_DB_NAME;
let NAME = TEST_DB_NAME;

mongoose.connect(DB_URI, {
    useNewUrlParser: true,
    useCreateIndex: true,
    dbName: NAME
});

describe('User Integration Test', () => {
    // make sure app is imported without issues
    it('Has App Defined', () => {
        expect(app).toBeDefined();
    });

    let server;

    beforeAll(async () => {
        // Clear Test Data
        await User.deleteMany({});
        await Profile.deleteMany({});
        server = await app.listen(3001);
    });

    afterAll(async (done) => {
        // Clear Test Data
        await User.deleteMany({});
        await Profile.deleteMany({});
        // Close server
        await server.close(done);
    });

    describe('User route tests', () => {

        it('Can Register a User', async () => {
            const body = {
                "username": "User21",
                "email": "[email protected]",
                "password": "123456",
                "avatar": "image.jpg"
            }
            await request(server)
                .post('/api/v1/users')
                .send(body)
                .set('Accept', 'application/json')
                .set('Content-Type', 'application/json')
                .expect(200)
        });

});

// THE EXPRESS ROUTE in api/v1/users.js

const express = require('express');
const auth = require('../../middleware/auth');
const router = express.Router();
const { UserService } = require('../../services');
const {
    check,
    validationResult
} = require('express-validator/check');

// @route   POST api/users
// @desc    Register User
// @access  Public
// @return  status message
router.post('/', [
    check('email', 'Please provide a valid email address').isEmail(),
    check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 }),
    check('username', 'Username is Required.').not().isEmpty()
], async (req, res, next) => {
    try {
        //--Validate
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({
                errors: errors.array()
            });
        }

        const message = await UserService.register(req.body);
        return res.status(200).json(message)

    } catch (err) {
        next(err);
    }
});

// THE register METHOD found in ../../services/UserService.js

const register = async (data) => {
    try {

        // Destructure the data
        const {
            username,
            email,
            password,
            avatar
        } = data;

        // remove spaces from username and lcase it
        let user_name = username.replace(/\s/g, '').toLowerCase();
        // Check if the username or email already exists
        await doesUserExist(user_name, email);
        // Create a new user 
        const token = uuidv4();
        user = new User({
            email: email.toLowerCase(),
            username: user_name,
            avatar: avatar,
            verifyEmailToken: token
        });

        // encrypt the password
        const salt = await bcrypt.genSalt(10);
        user.password = await bcrypt.hash(password, salt);
        // Save the user
        // (Works Unless Running Jest Integration Tests)
        await user.save(); 

        // Create and save an empty Profile for the new user
        profile = new Profile();
        profile.user = user;
        // (Always Works)
        await profile.save();

        // Send verification email
        await send(user, 'Verify Your Email', token, 'verify-email.html');

        return { message: 'User was registered successfully.' };


    } catch (err) {
        throw err;
    }
}

// Does user exist method found in ./UserService.js
const doesUserExist = async (username, email) => {

    // Check if user exists by email
    let message = await checkEmail(email);
    if (!message.email_available) {
        throw new Error('Email already exists');
    }
    // Check if user exists by username
    message = await checkUserName(username.toLowerCase())
    if (!message.username_available) {
        throw new Error('Username already exists');
    }
    return false;
}

Когда я вызываю этот код через пользовательский интерфейс, Postman или curl, создаются как пользователь, так и профиль, как и ожидалось.

Когда я запускаю интеграционный тест, npm run test:integration или npm test, создается только профиль.

мои сценарии package.json: «тест»: «шутка», «тест: интеграция»: «шутка --testPathPattern Integration.test»,

Наконец, нигде не сообщается об ошибках. Пользователь просто не создан.


person chuck    schedule 25.06.2019    source источник


Ответы (2)


После нескольких часов работы и тестирования этой проблемы я обнаружил, что событие afterAll() возникает случайным образом. Иногда после того, как все тесты во внутреннем описателе() выполнялись, а иногда нет. Конечно, когда afterAll() запускал всех пользователей, которые были удалены из хранилища данных.

Если переместить beforeAll() и AfterAll() во внутреннее описать(). Это работало очень хорошо, пока я не включил другие интеграционные тесты, такие как авторизация, например, которые также использовали таблицу User. Я заметил, что наборы тестов не выполняются синхронно. Когда один тестовый набор выбрасывал данные из другого тестового набора, пока выполнялся другой тестовый набор.

Теперь я настроил базу данных для каждого набора тестов. Неуклюжий, шаткий, хакерский и неправильный, я знаю, но мне нужно двигаться дальше. Кто-нибудь знает, можете ли вы контролировать синхронное и/или асинхронное поведение Jest? Пожалуйста, не предлагайте мокко и/или чай.

person chuck    schedule 25.06.2019

Я столкнулся с похожей проблемой, когда возникала прерывистая гонка между отправкой документа и его получением.

Я исправил это, запустив jest с флагом --runInBand.

person S.Hinton    schedule 20.07.2021