เข้าร่วมการทดสอบจากหลายไฟล์ด้วย mocha.js


89

ฉันกำลังพยายามเข้าร่วมการทดสอบทั้งหมดจากหลายไฟล์ในไฟล์เดียวโดยมีลักษณะดังนี้:

  describe('Controllers', function() {
    describe('messages.js', function() {
      require('./controllertests/messages').test(options);
    })
    describe('users.js', function() {
      require('./controllertests/users').test(options);
    })
  })

ฉันค่อนข้างแน่ใจว่านี่ไม่ใช่วิธีที่ดีที่สุดในการเข้าร่วมการทดสอบฉันมีตัวอย่างการค้นหาที่ผิดพลาดเกี่ยวกับวิธีการทำสิ่งนี้: s


1
อยากรู้ไหมทำไมการทดสอบต้องรวมเข้าด้วยกันในไฟล์เดียว
thgaskell

2
สำหรับการแบ่งปันตัวแปรท้องถิ่นและองค์กร
coiso

อาจเหมาะสมกว่าหากคุณรวมการทดสอบไว้ในคำถาม ดูเหมือนว่าคุณอาจเอนเอียงไปที่การทดสอบการรวม (ซึ่งต่างจากการทดสอบหน่วย) โดยทั่วไปคุณไม่จำเป็นต้องแชร์ตัวแปรระหว่างการทดสอบ
thgaskell

2
และปัญหาใหญ่คือฉันต้องการมีไฟล์ 20 ไฟล์มากกว่า 1 ไฟล์
huuuuge

2
นอกจากนี้หากคุณดูว่า Mocha จัดการกับห้องสวีทด้วยแนวคิด.only()อย่างไรอาจเป็นประโยชน์ที่จะสามารถdescribe.only()เรียกใช้ไดเรกทอรีทั้งหมดของการทดสอบได้ นั่นคือสิ่งที่ทำให้ฉันมาที่นี่
คริส

คำตอบ:


115

หากคุณต้องการที่จะรวมหลาย ๆ โมดูลเข้าของคุณdescribeลำดับชั้นเหมือนที่คุณกำลังทำอยู่ในคำถามของคุณสิ่งที่คุณกำลังทำสวยมากมันถ้าคุณต้องการที่จะเขียนโหลดการทดสอบที่กำหนดเองสำหรับมอคค่า การเขียน Custom loader จะไม่ง่ายกว่าหรือทำให้โค้ดของคุณชัดเจนไปกว่าสิ่งที่คุณมีอยู่แล้ว

นี่คือตัวอย่างของวิธีที่ฉันจะเปลี่ยนบางสิ่ง testไดเรกทอรีย่อยในตัวอย่างนี้จะจัดเป็น:

.
└── test
    ├── a
    │   └── a.js
    ├── b
    │   └── b.js
    ├── common.js
    └── top.js

top.js:

function importTest(name, path) {
    describe(name, function () {
        require(path);
    });
}

var common = require("./common");

describe("top", function () {
    beforeEach(function () {
       console.log("running something before each test");
    });
    importTest("a", './a/a');
    importTest("b", './b/b');
    after(function () {
        console.log("after all tests");
    });
});

importTestฟังก์ชั่นเป็นเพียงการแสดงให้เห็นว่ามันจะเป็นไปได้ที่จะจัดการกับการทำซ้ำของการนำเข้าโมดูลหลายโดยไม่ต้องพิมพ์ทั้งdescribe(... require...สิ่งที่ทุกเวลาเดียว commonโมดูลจะหมายถึงการถือสิ่งที่คุณต้องการที่จะใช้ในหลายโมดูลของชุดทดสอบ ฉันไม่ได้ใช้มันจริง ๆtopแต่สามารถใช้ที่นั่นได้ถ้าจำเป็น

ผมจะทราบที่นี่ว่าbeforeEachจะเรียกใช้รหัสก่อนที่แต่ละคนและทุกทดสอบเดียวที่ลงทะเบียนกับitว่าพวกเขาปรากฏอยู่ภายในdescribeในtopหรือพวกเขาปรากฏในใด ๆ ของโมดูลที่นำเข้า ด้วย--recursiveที่beforeEachรหัสจะต้องมีการคัดลอกลงในแต่ละโมดูลหรือบางทีคุณอาจต้องการมีbeforeEachตะขอในแต่ละโมดูลที่เรียกฟังก์ชั่นที่นำเข้าจากโมดูลที่พบบ่อย

นอกจากนี้afterเบ็ดจะทำงานหลังจากการทดสอบทั้งหมดในชุด --recursiveนี้ไม่สามารถจำลองแบบด้วย หากคุณใช้--recursiveและเพิ่มรหัสของafterแต่ละโมดูลจะมีการดำเนินการหนึ่งครั้งต่อโมดูลแทนที่จะใช้เพียงครั้งเดียวสำหรับการทดสอบทั้งหมด

มีการทดสอบทั้งหมดปรากฏอยู่ภายใต้เดียวหัวไม่สามารถจำลองแบบโดยใช้top --recursiveด้วย--recursiveแต่ละไฟล์อาจมีdescribe("top"แต่จะสร้างใหม่topหัวข้อสำหรับแต่ละไฟล์

common.js:

var chai = require("chai");

var options = {
    foo: "foo"
};

exports.options = options;
exports.chai = chai;
exports.assert = chai.assert;

การใช้โมดูลที่มีชื่อcommonเช่นนี้เป็นสิ่งที่ฉันได้ทำในชุดทดสอบของฉันเพื่อหลีกเลี่ยงไม่ให้requireมีสิ่งต่างๆมากมายซ้ำแล้วซ้ำเล่าและเพื่อเก็บตัวแปรหรือฟังก์ชันแบบอ่านอย่างเดียวทั่วโลกที่ไม่รักษาสถานะ ฉันไม่ต้องการที่จะสร้างมลพิษให้กับglobalวัตถุเหมือนในคำตอบของ thgaskell เพราะวัตถุนี้สามารถเข้าถึงได้ทั่วโลกและสามารถเข้าถึงได้แม้ในไลบรารีของบุคคลที่สามโค้ดของคุณอาจกำลังโหลด นี่ไม่ใช่สิ่งที่ฉันยอมรับได้ในรหัสของฉัน

a/a.js:

var common = require("../common");
var options = common.options;
var assert = common.assert;

it("blah a", function () {
    console.log(options.foo);
    assert.isTrue(false);
});

b/b.js:

it("blah b", function () {});

3
แม้ว่าฉันจะยอมรับว่าคุณไม่ควรสร้างมลพิษglobalแต่ฉันก็ใช้สิ่งนี้สำหรับไลบรารียืนยันเพื่อให้ไฟล์ทดสอบสะอาด global.processมันไม่ได้เป็นเหมือนที่คุณกำลังเขียนทับ ตัวแปรโลคัลจะแทนที่globalเว้นแต่ไลบรารีอื่นจะเรียกอย่างชัดเจนglobal.XYZซึ่งไม่น่าเป็นไปได้ จะคงอยู่ในช่วงระยะเวลาของการทดสอบเท่านั้น ยังไม่ทำร้ายฉัน แต่ฉันจะแจ้งให้คุณทราบทันทีที่มันกัดฉันที่ตูด :)
thgaskell

อะไรคือความแตกต่างระหว่างimportTestและการโทรrequire('path')()เช่น?
CherryNerd

@CreasolDev importTestฟังก์ชันเป็นเพียงฟังก์ชันอำนวยความสะดวก สิ่งที่สำคัญคือปิดการrequireโทรในdescribeบล็อก เป็นสิ่งสำคัญที่การrequireเรียกจะถูกรวมไว้describeมิฉะนั้นโมดูลจะไม่ถูกแยกในบล็อกของตัวเองและ hook ใด ๆ ที่กำหนดโดยไฟล์ที่นำเข้าจะถูกตั้งค่าในบล็อกที่ไม่ถูกต้อง หากimportTestถูกแทนที่ด้วยการโทรโดยตรงไปยังrequireโดยไม่มีการห่อdescribeโมดูลa/aและb/bจะแชร์ตะขอ ตัวอย่างเช่นbeforeEachชุดเบ็ดb/bจะทำงานก่อนการทดสอบในแต่ละa/aครั้ง
Louis

1
ฉันจะไม่เรียกใช้ตรรกะใด ๆ เช่น beforeEach ในระดับสูงสุดของคุณอธิบาย ให้แต่ละไฟล์ทำก่อน "สิ่งของ" แต่ละไฟล์ คุณจะเชื่อมโยงการทดสอบของคุณเข้าด้วยกันและการใช้งานที่ไม่เกี่ยวข้องหากคุณทำเช่นนี้
PositiveGuy

1
ฉันจะทำการตัดคำอธิบายในไฟล์ที่เกี่ยวข้องด้วยไม่ใช่ในฟังก์ชัน importTest ระดับบนสุดที่อธิบายในแต่ละไฟล์ควรอธิบายถึงจุดประสงค์ของชุดทดสอบของตนอยู่ดี
PositiveGuy

35

แม้ว่าสิ่งนี้อาจไม่ได้เชื่อมโยงโดยตรงกับคำถาม แต่คำตอบที่ฉันต้องการคือ:

$ mocha --recursive

จะดำเนินการทดสอบทั้งหมดในไดเรกทอรีย่อยของโฟลเดอร์ "test" เรียบร้อย. บันทึกการรักษารายการการทดสอบที่ฉันต้องการโหลดและเรียกใช้ทุกอย่างเสมอ


3
ตอบดีที่สุด! ง่ายกว่าโซลูชันที่เสนออื่น ๆ
caiosm1005

12
@ caiosm1005 คำตอบนี้ไม่ได้เป็นการแก้ปัญหาที่ OP นำเสนออย่างแท้จริง แน่นอนว่าหากคุณไม่จำเป็นต้องทำในสิ่งที่ OP ต้องการคุณก็ควรใช้สิ่งนี้ อย่างไรก็ตามหากคุณต้องการรวมไฟล์ทดสอบแต่ละไฟล์ไว้ในหลาย ๆdescribeบล็อกdescribeบล็อกที่ขยายไฟล์--recursiveจะไม่ทำ เมื่อเห็นว่ามันไม่สามารถแก้ปัญหาของ OP ได้ฉันจะไม่เรียกมันว่า "ดีที่สุด"
Louis

@louis - ฉันเชื่อว่าคุณสามารถรวมไฟล์แต่ละไฟล์ไว้ในdescribeบล็อกได้
Ian Jamieson

4
@IanJamieson OP พยายามให้มีไฟล์หลายไฟล์ที่ครอบคลุมโดยบล็อกเดียว describeดูที่คำถาม "การควบคุม" describeบล็อกควรครอบคลุมการทดสอบของและ./controllertests/messages.js ./controllertests/users.jsการตบ--recursiveลงบนการวิงวอนของ Mocha ไม่ได้สร้างdescribe("Controllers"บล็อกอย่างน่าอัศจรรย์
Louis

3
@ หลุยส์แค่พยายามช่วย ขออภัยหากฉันทำให้คุณขุ่นเคืองด้วยการพยายามสร้างdescribeบล็อกอย่างน่าอัศจรรย์ซึ่งจริงๆแล้วฉันได้เรียนรู้การทำจากดัมเบิลดอร์
Ian Jamieson

16

ไม่มีสิ่งใดที่ป้องกันไม่ให้คุณเรียกใช้ไฟล์ทดสอบหลายไฟล์ โดยทั่วไปการทดสอบแต่ละครั้งไม่ควรขึ้นอยู่กับผลลัพธ์ของการทดสอบอื่นดังนั้นการแบ่งปันตัวแปรจึงไม่ใช่สิ่งที่คุณต้องการทำ

นี่คือตัวอย่างวิธีจัดระเบียบไฟล์ทดสอบของคุณ

.
├── app.js
└── test
    ├── common.js
    ├── mocha.opts
    │
    ├── controllers
    │   ├── messages-controller.js
    │   └── users-controller.js
    │
    └── models
        ├── messages-model.js
        └── users-model.js

จากนั้นภายใน mocha.optsไฟล์ให้แน่ใจว่าได้ตั้งค่า--recursiveตัวเลือก

มอคค่า

--ui bdd
--recursive

หากมีมีโมดูลทั่วไปที่คุณต้องการรวมไฟล์ทั้งหมดที่คุณสามารถเพิ่มนั้นไปยังcommon.jsไฟล์ ไฟล์ที่รูทของtestไดเร็กทอรีจะรันก่อนไฟล์ในไดเร็กทอรีที่ซ้อนกัน

common.js

global.chai = require('chai');
global.assert = chai.assert;
global.expect = chai.expect;
chai.should();
chai.config.includeStack = true;

process.env.NODE_ENV = 'test';

// Include common modules from your application that will be used among multiple test suites.
global.myModule = require('../app/myModule');

3
มีใครคิดจะเพิ่มรหัสสำหรับไฟล์ในไดเร็กทอรีคอนโทรลเลอร์และโมเดลหรือไม่? จะดีมากถ้ามีตัวอย่างเต็ม ๆ
Gavin

@Gavin - สิ่งเหล่านี้จะเป็นเพียงชุดทดสอบดังนั้นพวกเขาจะมีdescribe('mytest', function() { /* ..... etc */ });
Ian Jamieson

9

ฉันรู้ว่านี่เป็นโพสต์เก่า แต่ฉันอยากจะพูดถึงสิ่งที่เป็นทางออกที่ดีสำหรับฉันซึ่งคล้ายกับวิธีการที่ OP เสนอมาก

โครงการที่ฉันกำลังทำอยู่ได้รับการทดสอบอย่างดีและการทดสอบเติบโตขึ้นเรื่อย ๆ ฉันลงเอยด้วยการใช้requireเพราะมันเป็นแบบซิงโครนัสจึงทำให้ง่ายต่อการเขียนการทดสอบของคุณโดยไม่ต้องเปลี่ยนแปลงสถาปัตยกรรมมากเกินไป:

// inside test/index.js

describe('V1 ROUTES', () => {
  require('./controllers/claims.test');
  require('./controllers/claimDocuments.test');
  require('./controllers/claimPhotos.test');
  require('./controllers/inspections.test');
  require('./controllers/inspectionPhotos.test');
  require('./controllers/versions.test');
  require('./services/login.v1.test');
});

describe('V2 ROUTES', () => {
  require('./services/login.v2.test');
  require('./services/dec-image.v2.test');
});

describe('V3 ROUTES', () => {
  require('./services/login.v3.test');
  require('./services/getInspectionPhotosv3.test');
  require('./services/getPolicyInfo.v3.test');
});

describe('ACTIONS', () => {
  require('./actions/notifications.test');
});

2

ฉันมีปัญหาคล้าย ๆ กันที่ฉันมีการทดสอบมากมายสำหรับชั้นเรียนในหมวดหมู่เดียวกันและฉันต้องการจัดกลุ่มเข้าด้วยกันเพื่อให้ดูใน IDE ได้ง่ายขึ้น การทดสอบและโค้ดทั้งหมดของฉันใช้โมดูล ES6 อยู่แล้ว - ฉันไม่ต้องการเขียนใหม่ทั้งหมดเพื่อใช้requireเหมือนที่เห็นในตัวอย่างอื่น ๆ

ฉันแก้ไขได้โดยdescribeส่งออก"การจัดกลุ่ม" ของฉันจากนั้นนำเข้าในไฟล์ทดสอบของฉันและเพิ่มลงในไฟล์ที่นำเข้าdescribeโดยทางโปรแกรม ฉันลงเอยด้วยการสร้างวิธีการช่วยเหลือเพื่อแยกท่อประปาทั้งหมดออกไป

ใน someCategory.spec.js

const someCategory= describe("someCategory", () => {});


// Use this just like a regular `describe` to create a child of this scope in another file
export default function describeMember(skillName, testFn) {
  return describe(skillName, function configureContext() {
    // Make context a child of `someCategory` context
    function Context() {}
    Context.prototype = someCategory.ctx;
    this.ctx = new Context();
    // Re-parent the suite created by `describe` above (defaults to root scope of file it was created in)
    this.parent.suites.pop();
    someCategory.addSuite(this);
    // Invoke the fn now that we've properly set up the parent/context
    testFn.call(this);
  });
}

ในการทดสอบแต่ละครั้ง:

import { default as describeCategoryMember } from './someCategory.spec';

describeCategoryMember('something', () => {
    describe('somethingElse', () => {
        ...
    });

    it('a test', () => {
        ...
    });
})

-11
describe( 'Running automation test, Please wait for all test to complete!'.red, function () {


    var run = require( './Test.js' );

    for ( var i = 0; i < 2; i++ ) {
        run.badLogin();
        run.loginLimited();
        run.acceptJob();
        run.drivingToJob();
        run.arrivedAtJob();
        run.towingJob();
        run.arrivedDestination();
        run.jobComplete();
        run.restrictionLicensePlate();
        run.newNodeMainMenu();
        run.newNodeMainMenuToDrafts();
        run.draftDelete();
        run.resetAllData();
        run.companyVehicle();
        run.actionsScreenClockInOut();
        run.mainMenuLogout();
        run.loginAdmin();
        run.actionsScreenLogout();
    }
} );

3
ที่ดีที่สุดคือเพิ่มคำอธิบายควบคู่ไปกับรหัสเพื่อให้ผู้อื่นพิจารณาว่าเป็นคำตอบที่ยอมรับ
Suever

2
ทำไมต้องห่วง? อยู่ใน./Test.jsอะไร? ใครจะรู้? สำหรับบันทึกนี้ฉันเป็นผู้ตอบสูงสุดในแท็กมอคค่า ฉันรู้จักมอคค่าทั้งภายในและภายนอก แต่ฉันไม่สามารถเข้าใจคำตอบนี้ได้
Louis

@ หลุยส์ดูเหมือนว่าเขาต้องการเรียกใช้การทดสอบnครั้งโดยใช้ลูป
Akash Agarwal
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.