ãã㯠JavaScript & Node.js ã®ä¿¡é Œæ§ã®ããã®A-Zãªã¬ã€ãã§ãã
æ¬ã¬ã€ãã¯ã沢山ã®çŽ æŽãããããã°èšäºãæžç±ãªã©ã®äžã«ããæ§ã
ãªããŒã«ããå
容ããã¥ã¬ãŒã·ã§ã³ããèŠçŽããŠäœãããŠããŸãã
åºç€ã¯ãã¡ããã®ããšãå€ãã®ã¢ããã³ã¹ããªãããã¯ïŒæ¬çªç°å¢ã§ã®ãã¹ãã»ãã¥ãŒããŒã·ã§ã³ãã¹ãã»property-basedãã¹ãã»æŠç¥çã§ãããã§ãã·ã§ãã«ãªããŒã«ã«ã€ããŠãªã©ïŒãŸã§åŠã¹ãæ ã«åºãŸãããïŒ ãã®ã¬ã€ããé ã ãŸã§èªã¿ããã°ãããªãã®ãã¹ãã¹ãã«ã¯äžŠã®ã¬ãã«ã倧ããåé§ããããšã§ãããã
ãŸãã¯ãã©ããªã¢ããªã±ãŒã·ã§ã³ã«ãšã£ãŠãæ ¹å¹¹ãšãªãæ®éçãªãã¹ãã®ç¿æ
£ãçè§£ãããšããããå§ããŸãããã
ãããŠãããã³ããšã³ã/UIãããã¯ãšã³ããCIããããã¯ãªããªããã®å
šãŠã§ããèªåã®èå³ã®ããåéãæ¢æ±ããŠãããŸãããã
- JavaScript & Node.js ã®ã³ã³ãµã«ã¿ã³ãã§ã
- ð Testing Node.js & JavaScript From A To Z - 10æé以äžã®åç»ã14çš®é¡ã®ãã¹ãã40以äžã®ãã¹ããã©ã¯ãã£ã¹ãåãæ±ã£ããåããããããªã³ã©ã€ã³ã³ãŒã¹ã§ã
- Twitterã¯ãã¡ã
-
ðµð±ããŒã©ã³ãèª - Michal Biesiadaããã®è²¢ç®
-
ðªðžã¹ãã€ã³èª - Miguel G. Sanguinoããã®è²¢ç®
-
ð§ð·ãã«ãã¬ã«èª - Iago Angelim Costa CavalcanteãããDouglas Mariano Valeroãããkooogeããã®è²¢ç®
-
èªåã®èšèªã«ç¿»èš³ãããã§ãã? issueããã²ç«ãŠãŠãã ãã ð
ä»ãã€ã³ã¹ãã€ã¢ãããã£ãã²ãšã€ã®ã¢ããã€ã¹ïŒ1çºã®ç¹å¥ãªåŒŸäžžïŒ
綺éºãªãã¹ããæ§ç¯ããããã®åå° (12çºã®åŒŸäžž)
ããã¯ãšã³ãããã³ãã€ã¯ããµãŒãã¹ã®ãã¹ãã广çã«æžãïŒ8çºã®åŒŸäžžïŒ
ã³ã³ããŒãã³ããã¹ããE2Eãã¹ããªã©ãå«ãWeb UIã®ãã¹ããæžãïŒ11çºã®åŒŸäžžïŒ
ç£èŠå¡ãç£èŠãã - ãã¹ãåè³ªãæž¬ã (4çºã®åŒŸäžž)
JSã®äžçã«ãããCIã®ã¬ã€ãã©ã€ã³ (9çºã®åŒŸäžž)
â Do: ãã¹ãã³ãŒãã¯æ¬çªã³ãŒããšã¯éããŸã - éåžžã«ã·ã³ãã«ã§ãçããæœè±¡ããæé€ãããã©ããã§ããªãŒã³ã§ãæžããããªããããªãã®ã«ããŸãããã誰ã§ãäžç®èŠãŠæå³ãããã«äŒãããããªãã¹ããå¿ãããŸãããã
ç§ãã¡ã®é ã¯ãã€ãã¡ã€ã³ã®æ¬çªã³ãŒãã®ããšã§ãã£ã±ããªã®ã§ãäœèšã«ããããããã®ã远å ãããããªãè³å ã®ã¹ããŒã¹ããªããŠãããŸãããããã远å ã§é£ããã³ãŒããæã ã®ã¡ã£ãœããªè³ã«æŒã蟌ãããªããŠããããã®ãªããããŒã ãé æ»ãããŸãããããã¯ãããããã¹ããæžãããã£ãçç±ãšéè¡ããŠããŠæ¬æ«è»¢åã§ããå®éã®ãšããããããå€ãã®ããŒã ããã¹ãã諊ããŠããŸãçç±ã§ãã
ãã¹ããšã¯ãããå¥ã®ããšã®ããã®æ©äŒã ãšæããŸããããããã¯ãäžç·ã«åæ¥ããŠæ¥œãããããªãå奜çã§ç¬é¡ã«æºã¡æº¢ããã¢ã·ã¹ã¿ã³ãã§ãããå°ããªæè³ã§å€§ããªãªã¿ãŒã³ããããããã®ãªã®ã§ãã
ç§åŠçã«äººéã«ã¯2ã€ã®è³ã®ã·ã¹ãã ããããŸã: ã·ã¹ãã 1ã¯ãããšãã°ã¬ã©ã¬ã©ã®éè·¯ãè»ãé転ãããããªåªåã®ãããªã掻åã®ããã«äœ¿ãããã·ã¹ãã 2ã¯ãããšãã°æ°åŒãè§£ããããªè€éã§æèçãªæäœã®ããã«äœ¿ãããŸãã
ã·ã¹ãã 1ã§åŠçã§ãããããªãã¹ãããã¶ã€ã³ããŸãããããã¹ãã³ãŒããšåãåãæã«ã¯ããŸãã§HTMLææžãç·šéãããã®ãããªæ°æ¥œããæããããã¹ãã§ãã£ãŠã2X(17 à 24)ãšããæ°åŒãè§£ãæã®ããã§ãã£ãŠã¯ãããŸããã
ãã®éæã®ããã«ã¯ããã¯ããã¯ãããŒã«ãè²»çšå¯Ÿå¹æãé«ããã¹ã察象ãéžæçã«åæšéžæãããšããã§ããããå¿ èŠãªãã®ã ãããã¹ãããææ·æ§ãä¿ã€ããšãå¿ãããŸããããæã«ã¯ãä¿¡é Œæ§ãææ·æ§ãšç°¡æœããšå€©ç§€ã«ãããããã€ãã®ãã¹ããæšãŠãããšãæå¹ã§ãã
ããããå ã®ã¢ããã€ã¹ã®ã»ãšãã©ã¯ããã®ååããæŽŸçãããã®ã§ãã
â ããããŸããã: ãã¹ãã¬ããŒããšã¯ãçŸåšã®ã¢ããªã±ãŒã·ã§ã³ã®å€æŽãèŠä»¶ãæºãããŠãããã©ãããäŒãããã®ã§ãªããã°ãªããŸããããã®èŠä»¶ãšã¯ã³ãŒãããŒã¹ã«è©³ãããšã¯éããªã人ãã¡ã«ãšã£ãŠã®ãã®ã§ãããããã¯ããã¹ã¿ãŒããããã€ãããDevOpsãšã³ãžãã¢ã2幎åŸã®ããªãã§ãã ãã®ããã«ã¯ããã¹ãèªäœãèŠä»¶ã¬ãã«ã§è©±ãã3ã€ã®èŠç¹ãå«ãã§ãããšããã§ãããã
(1) äœããã¹ããããŠããã®ãïŒ ããšãã°ãProductsService.addNewProduct ãšããã¡ãœãã
(2) ã©ã®ãããªç¶æ³ãšã·ããªãªãæ³å®ããŠãããïŒãããšãã°ã priceãã¡ãœããã«æž¡ãããªãã£ãæ
(3) ã©ããªãã¹ãçµæãäºæããŠãããïŒ ããšãã°ã æ°ããproductãæ¿èªãããªãããš
â ãããªãã°: ãããã€ã倱æãã"Add product"ãšããååã®ãã¹ããèœã¡ãŠããŸããããã§äœãäžå ·åãèµ·ãããŠãããæ£ç¢ºã«åãããšèšããŸããïŒ
ð Note: ããããã®åŒŸäžžã«ã¯ã³ãŒãäŸãã€ããŠããŠãæã«ã¯ã€ã©ã¹ãããããŸããã¯ãªãã¯ããŠåºããŠãã ããã
â ã³ãŒãäŸ
//1. ãã¹ã察象ã®ãŠããã
describe('Products Service', function() {
describe('productã远å ãã', function() {
//2. ã·ããªãª and 3. æåŸ
ããçµæ
it('priceãæå®ãããŠããªãæã productã®ã¹ããŒã¿ã¹ãæ¿èªåŸ
ã¡ã§ããããš', ()=> {
const newProduct = new ProductService().add(...);
expect(newProduct.status).to.equal('pendingApproval');
});
});
});
© ã¯ã¬ãžãã & ãã£ãšèªã
1. Roy Osherove - Naming standards for unit testsâ ããããŸããã: Arrange(æºåãã), Act(åãã), Assert(確èªãã)ãšãã3ã€ã®å·¥çšã§ãã¹ããæ§æããŸããããããããããšã§ãã³ãŒããèªã人ããã¹ãã®æ¹éãçè§£ããããã«è³å CPUãè²»ãããã«æžã¿ãŸãã
1ã€ç®ã®A - Arrange(æºåãã): ãã¹ããã·ãã¥ã¬ãŒããããç¶æ³ãã»ããã¢ããããããã®ã³ãŒãã§ããããã«ã¯ããã¹ãããã察象ãã€ã³ã¹ã¿ã³ã¹åãããDBã¬ã³ãŒãã远å ãããç¹å®ã®ãªããžã§ã¯ããã¢ãã¯/ã¹ã¿ãããããšãªã©ãå«ãŸããŸãã
2ã€ç®ã®A - Act(åãã): ãã¹ã察象ãåãããŸãã 倧æµã¯1è¡ã§æžã¿ãŸãã
3ã€ç®ã®A - Assert(確èªãã): è¿ãå€ãæåŸ ããŠããçµæãšãªã£ãŠãããã©ããã確èªããŸãã倧æµã¯1è¡ã§æžã¿ãŸãã
â ãããªãã°: ã¡ã€ã³ã®ã³ãŒããçè§£ããã®ã«äœæéãããã£ãŠããŸãã°ãããã1æ¥ã®ã¿ã¹ã¯ã®äžã§æ¬æ¥ã¯æãç°¡åã§ããã¯ãã®ãã¹ããæžããšããè¡çºã§è³ãããããã«ãªã£ãŠããŸããŸãã
â ã³ãŒãäŸ
describe("Customer classifier", () => {
test("ã«ã¹ã¿ããŒã500$è²»ãããæ, ãã¬ãã¢ã ãšããŠèå¥ãããããš", () => {
//Arrange(æºåãã)
const customerToClassify = { spent: 505, joined: new Date(), id: 1 };
const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" });
//Act(åãã)
const receivedClassification = customerClassifier.classifyCustomer(customerToClassify);
//Assert(確èªãã)
expect(receivedClassification).toMatch("premium");
});
});
test("ãã¬ãã¢ã ãšããŠèå¥ãããããš", () => {
const customerToClassify = { spent: 505, joined: new Date(), id: 1 };
const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" });
const receivedClassification = customerClassifier.classifyCustomer(customerToClassify);
expect(receivedClassification).toMatch("premium");
});
⪠ïž1.3 ãããã¯ãç±æ¥ã®èšèã§æåŸ ããæ¯ãèããèšè¿°ãã: BDDã¹ã¿ã€ã«ã®ã¢ãµãŒã·ã§ã³ã䜿ã
â
ããããŸããã: 宣èšçãªã¹ã¿ã€ã«ã§ãã¹ããæžãããšã¯ãèªè
ã«å°ããè³å
CPUã䜿ãããã«æŠèŠ³ãæŽãŸããå©ããšãªããŸããæ²¢å±±ã®æ¡ä»¶ããžãã¯ãå«ããããªåœä»€çãªã³ãŒããæžããšãèªè
ã¯è³å
CPUãæ²¢å±±äœ¿ãããšã匷å¶ãããŠããŸããŸãã
ãªã®ã§ã宣èšçãªBDDã¹ã¿ã€ã«ã§ã expect
ã should
ãªã©ãçšãããæè£œãé¿ãã€ã€ã人éçãªèšèã§æåŸ
ããçµæãæžããŸãããã
ããããChaiãJestãæ¬²ããã¢ãµãŒã·ã§ã³ã¡ãœããããã£ãŠãããããããŠãã®æ¬²ããã¢ãµãŒã·ã§ã³ã¡ãœãããäœåºŠã䜿ããããã®ã§ããã°ãJestã®ãããã£ãŒãæ¡åŒµããããšãã«ã¹ã¿ã Chaiãã©ã°ã€ã³ãæžãããšãæ€èšããŠã¿ãŠãã ããã
â ãããªãã°: ããŒã ããã¹ããæžããªããªããé¢åãªãã¹ãã.skip()ã§é£ã°ãããã«ãªããŸãã
â ã³ãŒãäŸ
ð ã¢ã³ããã¿ãŒã³äŸ: èªè ããã¹ãã®æ¹éãç¥ãããã«ãåœä»€çãªã³ãŒãããããªãã®é確èªããªããã°ãªããªã
test("ã¢ããã³ãååŸããæã ã¢ããã³ã®ã¿ãååŸçµæã«å«ãŸããããš", () => {
//"admin1"ã "admin2" ãšããã¢ããã³ãšã"user1"ãšãããŠãŒã¶ãŒã远å ããŠãããšä»®å®ãã
const allAdmins = getUsers({ adminOnly: true });
let admin1Found,
adming2Found = false;
allAdmins.forEach(aSingleUser => {
if (aSingleUser === "user1") {
assert.notEqual(aSingleUser, "user1", "A user was found and not admin");
}
if (aSingleUser === "admin1") {
admin1Found = true;
}
if (aSingleUser === "admin2") {
admin2Found = true;
}
});
if (!admin1Found || !admin2Found) {
throw new Error("Not all admins were returned");
}
});
it("ã¢ããã³ãååŸããæã ã¢ããã³ã®ã¿ãååŸçµæã«å«ãŸããããš", () => {
// 2人ã¢ããã³ã远å ããŠãããšä»®å®ãã
const allAdmins = getUsers({ adminOnly: true });
expect(allAdmins)
.to.include.ordered.members(["admin1", "admin2"])
.but.not.include.ordered.members(["user1"]);
});
âª ïž 1.4 ãã©ãã¯ããã¯ã¹ãã¹ããå®ã: ãããªãã¯ã¡ãœããã®ã¿ããã¹ããã
â
ããããŸããã: å
éšå®è£
ããã¹ãããŠã倧ããªãªãŒããŒãããã®å²ã«ãäœãåŸãããŸãããããã³ãŒããAPIãæ£ããçµæãè¿ããŠããã®ãªãã3æéããããŠ"ã©ã®ããã«"ãããéæããããããã¹ãããæŽã«ãã®ãããªå£ãããããã¹ããã¡ã³ãããŠããå¿
èŠããããŸããïŒ
å
¬éãããŠããæ¯ãèãããã¹ããããŠããæã¯ãåžžã«å
éšå®è£
ãæé»çã«ãã¹ããããŠããŠããã®ãã¹ããå£ããæãšããã®ã¯äœãç¹å®ã®åé¡ããã£ãæã ãã§ãïŒããšãã°ãåºåãééã£ãŠãããªã©ïŒã
ãã®ãããªã¢ãããŒãã¯behavioral testing
ãšåŒã°ããŸãã
éã«ãå
éšå®è£
ããã¹ãããå ŽåïŒãã¯ã€ãããã¯ã¹çã¢ãããŒã) - ãã©ãŒã«ã¹ãã³ã³ããŒãã³ãã®åºåããæ žå¿çãªè©³çްã«ç§»ããŸããå°ããªãªãã¡ã¯ã¿ãªã³ã°ã«ãã£ãŠãããšãåºåçµæãåé¡ãªãã£ããšããŠãããã¹ããå£ãããããããŸããã- ããã¯ã¡ã³ããã³ã¹ã³ã¹ããèããåäžãããŠããŸããŸãã
â ãããªãã°: ãã¹ãããªãªã«ãå°å¹Žã«ãªããŸã: ïŒäŸãã°ããã©ã€ããŒã倿°ã®ååãå€ãã£ãããšã§ãã¹ããå£ãããªã©ã®çç±ã§ïŒåã®å«ã³ããããŸããéçºè ãã¡ãCIã®éç¥ãç¡èŠãç¶ããŠããæ¥æ¬åœã®ãã°ãç¡èŠãããŠããŸãããã«ãªãã®ããå šãé©ãããšã§ã¯ãããŸããã
â ã³ãŒãäŸ
class ProductService {
// ãã®ã¡ãœããã¯å
éšã§ãã䜿ãããŠããªã
// ãã®ã¡ãœããåã倿Žãããšãã¹ããå£ãã
calculateVATAdd(priceWithoutVAT) {
return { finalPrice: priceWithoutVAT * 1.2 };
// äžèšã®è¿ãå€ã®åœ¢ãããŒåãå€ãããšãã¹ããå£ãã
}
//public method
getPrice(productId) {
const desiredProduct = DB.getProduct(productId);
finalPrice = this.calculateVATAdd(desiredProduct.price).finalPrice;
return finalPrice;
}
}
it("ãã¯ã€ãããã¯ã¹ãã¹ã: å
éšã¡ãœããã0 vatãåãåãæ, 0ãè¿ã", async () => {
// ãŠãŒã¶ãŒãVATãèšç®ã§ããããã«ããªããã°ãããªãèŠä»¶ã¯ãªããæçµéé¡ã瀺ããã°è¯ããããã«ãé¢ããããã¯ã©ã¹å
éšããã¹ãããããšã«åºå·ããŠããŸã£ãŠããã
expect(new ProductService().calculateVATAdd(0).finalPrice).to.equal(0);
});
âª ïž ïž1.5 æ£ãããã¹ãããã«ãéžæãã: ã¹ã¿ããã¹ãã€ã®ä»£ããã«ã¢ãã¯ã䜿ããªã
â ããããŸããã: ãã¹ãããã«ã¯ã¢ããªã±ãŒã·ã§ã³ã®å éšã«çµåããŠããããå¿ èŠæªã§ãããæã«ã¯å€§ããªäŸ¡å€ããããããŸãã (ãã¹ãããã«ã£ãŠäœã®ããšãå¿ããŠããŸã£ã人ã¯ãã¡ããèªã¿ãŸããã: ã¢ã㯠vs ã¹ã¿ã vs ã¹ãã€).
ãã¹ãããã«ã䜿ãåã«ç°¡åãªèªåãããŸãããïŒ ç§ããã¹ãããããšããŠããæ©èœã¯ä»æ§æžã«æžããŠããããããã¯ä»åŸæžããããããšãïŒããéããªããããã¯ãã¯ã€ãããã¯ã¹ãã¹ãã«ãªã£ãŠããŸã£ãŠããå¯èœæ§ããããŸãã
ããšãã°ãããæ±ºæžãµãŒãã¹ãèœã¡ãæã«ã©ããªé¢šã«ã¢ããªã±ãŒã·ã§ã³ãæ¯ãèãã®ããã¹ããããæã決æžãµãŒãã¹ãã¹ã¿ãããŠ'No Response'ãè¿åŽãããããšã§ãã¹ãå¯Ÿè±¡ãæ£ããå€ãè¿ããŠããã確èªããããšã§ããããããã¯ç¹å®ã®ã·ããªãªäžã«ãããã¢ããªã±ãŒã·ã§ã³ã®æ¯ãèããå¿çãçµæããã§ãã¯ããŸãããããã¯ã決æžãµãŒãã¹ãèœã¡ãŠããæã«ã¡ãŒã«ãéä¿¡ãããããšãã¹ãã€ã䜿ã£ãŠç¢ºèªããããšã§ãããã - ããã仿§æžã«ããããæžãããŠããããšã®æ¯ãèã確èªã§ããïŒ"決æžã«å€±æãããã¡ãŒã«ãéä¿¡ãã"ïŒ
äžæ¹ã§ã決æžãµãŒãã¹ãã¢ãã¯ããŠãããæ£ããJavaScriptã®åã§åŒã³åºãããŠããããšã確èªããå Žå - ã¢ããªã±ãŒã·ã§ã³ã®æ©èœãšã¯é¢ä¿ã®ãªãå
éšã®ããšã«ãã¹ãããã©ãŒã«ã¹ããŠããŸããé »ç¹ã«æŽæ°ããªããã°ãªããªãã§ãããã
â ãããªãã°: ã©ããªãªãã¡ã¯ã¿ãªã³ã°ãããã«ããã³ãŒãäžã§äœ¿ãããŠããå šãŠã®ã¢ãã¯ãæ¢ããŠæŽæ°ããããšãå¿ èŠã«ãªã£ãŠããŸããŸãããããšããã¹ãã¯é Œãããã®ãã芪åã§ã¯ãªããéè·ã«ãªã£ãŠããŸããŸãã
â ã³ãŒãäŸ
it("æå¹ãªãããã¯ããåé€ãããæ, ããŒã¿ã¢ã¯ã»ã¹çšã®DALãæ£ãããããã¯ããšæ£ããã³ã³ãã£ã°ã§1床ã ãåŒã°ããããš", async () => {
//æ¢ã«ãããã¯ãã远å ããŠãããšãã
const dataAccessMock = sinon.mock(DAL);
//ãããããããªãã§ãã: å
éšå®è£
ããã¹ãããããšããŽãŒã«ã«ãªã£ãŠããŸã£ãŠããŠããã ã®å¯äœçšã§ã¯ãªããªã£ãŠããŸã£ãŠããŸãã
dataAccessMock
.expects("deleteProduct")
.once()
.withArgs(DBConfig, theProductWeJustAdded, true, false);
new ProductService().deletePrice(theProductWeJustAdded);
dataAccessMock.verify();
});
ðæ£ããã³ãŒãäŸ: ã¹ãã€ã®é¢å¿ãèŠä»¶ããã¹ãããããšã«ãããå¯äœçšã¯çµæãšããŠå éšå®è£ ã«è§ŠããŠãã
it("æå¹ãªãããã¯ããåé€ãããæ, ã¡ãŒã«ãéä¿¡ãããããš", async () => {
//æ¢ã«ãããã¯ãã远å ããŠãããšãã
const spy = sinon.spy(Emailer.prototype, "sendEmail");
new ProductService().deletePrice(theProductWeJustAdded);
//ããããOK: ãããå
éšå®è£
ãããªãã®ãã£ãŠ? ããã§ãããã§ãã¡ãŒã«ãéä¿¡ãããšããèŠä»¶ããã¹ãããäžã§ã®å¯äœçšãšããŠã§ã
expect(spy.calledOnce).to.be.true;
});
ç§ã®ãªã³ã©ã€ã³ã³ãŒã¹ããã§ãã¯ããŠã¿ãŠãã ãã Testing Node.js & JavaScript From A To Z
â ããããŸããã: æã«ãããã¯ã·ã§ã³ã®ãã°ã¯äºæãã¬éåžžã«éå®çãªå ¥åå€ã«ãã£ãŠããããããŸã - ãã¹ãã®å ¥åå€ããªã¢ã«ã§ããã»ã©ããã°ãæ©æã«çºèŠã§ããå¯èœæ§ãé«ãŸããŸããFakerã®ãããªå°çšã®ã©ã€ãã©ãªã䜿ãããšã§ãæ¬äŒŒçã«ãªã¢ã«ã§ããããã¯ã·ã§ã³ã®æ§ã ãªç¶æ ã«äŒŒããããŒã¿ãçæããŸããããããšãã°ãããããã©ã€ãã©ãªã䜿ããšãªã¢ã«ã£ãœãé»è©±çªå·ããŠãŒã¶ãŒåãã¯ã¬ãžããã«ãŒãæ å ±ãäŒç€Ÿåããããã¯'lorem ipsum'ããã¹ããŸã§çæã§ããŸããfakerã®ããŒã¿ãã©ã³ãã ã«ããŠãã¹ã察象ãŠããããæ¡åŒµãããããªãã¹ããäœãããšãã§ããŸããïŒéåžžã®ãã¹ãã«å ããŠã§ãã代ããã«ã§ã¯ãªãïŒããããã¯å®éã®ãããã¯ã·ã§ã³ç°å¢ããããŒã¿ãã€ã³ããŒãããããšãã§ããŸãããã£ãšé«ãã¬ãã«ãã¿ããã§ããïŒæ¬¡ã®åŒŸäžžãã¿ãŠãã ããïŒproperty-basedãã¹ãïŒ
â ãããªãã°: "Foo"ã®ãããªäººå·¥çãªå ¥åå€ã䜿ã£ãŠãããšãéçºæã®ãã¹ãã§ã¯èª€ã£ãŠã°ãªãŒã³ã«ãªã£ãŠããŸããããããŸããããæ¬çªç°å¢ã§ããã«ãŒãâ@3e2ddsf . ##â 1 fdsfds . fds432 AAAAâã®ãããªèæ±ãæååãæž¡ããŠããããã¬ããã«ãªã£ãŠããŸããããããŸããã
â ã³ãŒãäŸ
ð ã¢ã³ããã¿ãŒã³äŸ: 人工çãªããŒã¿ã®ããã§ãã¹ãã¹ã€ãŒããéã£ãŠããŸã
const addProduct = (name, price) => {
const productNameRegexNoSpace = /^\S*$/; //ç©ºçœæååã蚱容ããªã
if (!productNameRegexNoSpace.test(name)) return false; //å
¥åå€ãç°¡æçãªããã§ããã®ãã¹ã«ã¯å°éããªã
//ãªã«ãããã«ããžãã¯ããããšãã
return true;
};
test("ãã¡ãªäŸ: æå¹ãªããããã£ã§productã远å ããæã確èªã«æåãã", async () => {
//"Foo"ãšããæååãå
šãŠã®ãã¹ãã§äœ¿ãããæ°žé ã«falseãªçµæãåŒãèµ·ãããªã
const addProductResult = addProduct("Foo", 5);
expect(addProductResult).toBe(true);
//誀ã£ãæå: 空çœã®å
¥ã£ãé·ãæååã§è©Šããªãã£ãã®ã§æåããŠããŸã£ã
});
it("è¯ãäŸ: æå¹ãªproductã远å ããæã確èªã«æåãã", async () => {
const addProductResult = addProduct(faker.commerce.productName(), faker.random.number());
//çæãããã©ã³ãã ãªå
¥åå€: {'Sleek Cotton Computer', 85481}
expect(addProductResult).to.be.true;
//ã©ã³ãã ãªå
¥åå€ã®ãããã§ãäºæããŠããªãã£ãã³ãŒããã¹ã«å°éããŠãã¹ãã倱æããã
//ãã°ãæ©æçºèŠã§ããïŒ
});
â ããããŸããã: éçºè ã¯åŸã ã«ããŠããããªå ¥åå€ã®ãã¿ãŒã³ã§ãã¹ããããŠããŸããã¡ã§ããå ¥åå€ã®ãã©ãŒããããçŸå®ã®ããŒã¿ã«è¿ãæã§ããïŒâfooã䜿ããªâã®é ãèªãã§ãã ããïŒãéçºè 㯠method(ââ, true, 1), method(âstringâ , false , 0) ã®ãããªéãããå ¥åå€ã®çµåãããã«ããŒããŸããã
ãããæ¬çªç°å¢ã§ã¯ã5åã®ãã©ã¡ãŒã¿ãŒãæã€APIã¯äœåãã®çµåãã§åŒã³åºãããŸããããã®äžã®1ã€ãããã»ã¹ãèœãšããŠããŸããããããŸãããïŒFuzz Testingãèªãã§ãã ããïŒã
ããã1åã®ãã¹ãã§1000çš®ãã®å
¥åå€ãèªåã§è©Šããã©ã®å
¥åå€ãæ£ããã¬ã¹ãã³ã¹ãè¿ããªãã£ãããææ¡ã§ããããšèšã£ããã©ãã§ããïŒ
Property-basedãã¹ããšããæè¡ã¯ãŸãã«ããããã£ãŠãããŸã: ããããå
šãŠã®å
¥åå€ã®ãã¿ãŒã³ããã¹ãå¯Ÿè±¡ã«æµã蟌ãããšã§ããã°ãçºèŠããæ©éãé«ããŠãããã®ã§ãã
ããšãã°ãaddNewProduct(id, name, isDiscount) ãšããã¡ãœããããããšããŸãããããã®ã¡ãœããã䜿ãã©ã€ãã©ãªã¯ã(1, âiPhoneâ, false), (2, âGalaxyâ, true) ã®ãããªã(number, string, boolean)ã®ããããçµåãã§åŒã³åºããŸãã
js-verify ã testcheck (ãã¡ãã®æ¹ãããã¥ã¡ã³ããæç¶è¯ãã§ã)ã®ãããªã©ã€ãã©ãªã䜿ããšãMochaãJestãªã©ã奜ã¿ã®ãã¹ãã©ã³ããŒã©ã€ãã©ãªã§property-basedãã¹ããå®è¡ããããšãã§ããŸãã
Update: Nicolas Dubienãããäžèšã®ã³ã¡ã³ãã§fast-checkãèŠãŠã¿ãŠãã ãããšææ¡ããŠãããŸãããããã¡ãã®ã©ã€ãã©ãªã¯ããã«ãã£ãŒãã£ãŒãæäŸããŠããŠãã¢ã¯ãã£ãã«ã¡ã³ããã³ã¹ãããŠããããã§ãã
â ãããªãã°: ã¡ãããšåãã³ãŒããã¹ããç¶²çŸ ããªããã¹ãå ¥åå€ãç¡æèã§éžæããŠããŸããŸããæ®å¿µãªããããã§ã¯ããã°ã衚åºãããããã®çžæ£ã§ãããã¹ãã®å¹çãäžããŠããŸããŸãã
â ã³ãŒãäŸ
import fc from "fast-check";
describe("Product service", () => {
describe("Adding new", () => {
//ããã¯ã©ã³ãã ãªããããã£ã§100åèµ°ã
it("æå¹ãªç¯å²å
ã§ã©ã³ãã ãªããããã£ã§productã远å ããæãåžžã«æåããããš", () =>
fc.assert(
fc.property(fc.integer(), fc.string(), (id, name) => {
expect(addNewProduct(id, name).status).toEqual("approved");
})
));
});
});
â ããããŸããã: ã¹ãããã·ã§ãããã¹ããå¿ èŠãªå Žåãçãéäžããã¹ãããã·ã§ããïŒ3ã7è¡çšåºŠïŒã䜿çšãããã¹ãã®äžéšãšããŠã€ã³ã©ã€ã³ã§å«ããããšïŒInline SnapshotïŒãæšå¥šãããŸããå€éšãã¡ã€ã«ã«ä¿åããªãããšã§ããã¹ãã¯èªå·±èª¬æçã§å£ãã«ãããªããŸãã
äžæ¹ã§ããã¯ã©ã·ãã¯ã¹ãããã·ã§ãããã®ãã¥ãŒããªã¢ã«ãããŒã«ã¯ã倧ããªãã¡ã€ã«ïŒäŸïŒã³ã³ããŒãã³ãã®ã¬ã³ããªã³ã°ããŒã¯ã¢ãããAPIã®JSONçµæïŒãå€éšã«ä¿åãããã¹ãå®è¡æã«åãåã£ãçµæãšä¿åãããããŒãžã§ã³ãæ¯èŒããããšãæšå¥šããŠããŸããããããããã«ããããã¹ãã1000è¡ã3000åã®ããŒã¿å€ã«æé»çã«çµã³ä»ãããããã¹ãäœæè ããããèªãã§çè§£ããããšãã§ããªããªããŸãããªããããåé¡ãªã®ã§ããããïŒ ãã®çç±ã¯å€å²ã«ããããã¹ãããã·ã§ãããç¡å¹ã«ãªãã®ã«1è¡ã®å€æŽã§ååã§ãããã¯é »ç¹ã«çºçããŸããå ·äœçã«ã¯ãã¹ããŒã¹ãã³ã¡ã³ããCSS/HTMLã®å°ããªå€æŽããšã«èµ·ããåŸãŸãããã®ãããªå Žåããã¹ãåãããšã©ãŒã®æããããåŸãããšã¯é£ãããåã«1000è¡ã倿Žãããªãã£ããããã§ãã¯ããã ãã«ãªããŸãããŸãããã¹ãäœæè ã確èªãæ€èšŒãã§ããªãé·æããã¥ã¡ã³ããæãŸããçå®ãšããŠåãå ¥ããããã«ä¿ããŸãããããã¯ãã¹ãŠãçŠç¹ãå®ãŸãããéå°ãªç®æšãéæããããšããäžéæã§é床ãªãã¹ãã®çç¶ã§ãã
ãªããã¹ããŒãã確èªããããŒã¿ã確èªããªãå ŽåïŒãã£ãŒã«ãã«çŠç¹ãåœãŠãå€ãæœåºããïŒããåãåãããã¥ã¡ã³ããã»ãšãã©å€æŽãããªãå Žåãªã©ãé·ãå€éšã¹ãããã·ã§ãããåãå
¥ããããã±ãŒã¹ãå°ãã ããããŸãã
â ãããªãã°: UIãã¹ãã倱æããŸããã³ãŒãã¯æ£ããããã«èŠããç»é¢ã¯å®ç§ã«ã¬ã³ããªã³ã°ãããŠããã®ã«ãäœãèµ·ãã£ãã®ã§ããããïŒ ã¹ãããã·ã§ãããã¹ãã¯ãå ã®ããã¥ã¡ã³ããšçŸåšåãåã£ããã®ãšã®å·®ç°ãçºèŠããŸããâããŒã¯ããŠã³ã«ã¹ããŒã¹ã1ã€è¿œå ãããŠããã ãã§ãã...
â ã³ãŒãäŸ
it("TestJavaScript.com is renderd correctly", () => {
//Arrange(æºåãã)
//Act(åãã)
const receivedPage = renderer
.create(<DisplayPage page="http://www.testjavascript.com"> Test JavaScript </DisplayPage>)
.toJSON();
//Assert(確èªãã)
expect(receivedPage).toMatchSnapshot();
//ããã§ç§ãã¡ã¯æé»çã«2000è¡ã®ããã¥ã¡ã³ããç¶æããããšã«ãªããŸã
//远å ã®æ¹è¡ãã³ã¡ã³ãããšã«ããã®ãã¹ãã¯å£ããŸã
});
it("When visiting TestJavaScript.com home page, a menu is displayed", () => {
//Arrange(æºåãã)
//Act(åãã)
const receivedPage = renderer
.create(<DisplayPage page="http://www.testjavascript.com"> Test JavaScript </DisplayPage>)
.toJSON();
//Assert(確èªãã)
const menu = receivedPage.content.menu;
expect(menu).toMatchInlineSnapshot(`
<ul>
<li>Home</li>
<li> About </li>
<li> Contact </li>
</ul>
`);
});
â
ããããŸããã: ãã¹ãçµæã«åœ±é¿ãäžããå¿
èŠãªè©³çްã¯ãã¹ãŠå«ããŸããããã以äžã¯å«ããŸãããäŸãã°ã100è¡ã®å
¥åJSONãåŠçãããã¹ããèããŠã¿ãŸãããããããæ¯åãã¹ãã«è²Œãä»ããã®ã¯æéã§ãããããå€éšã«æœåºã㊠transferFactory.getJSON()
ãšãããšããã¹ããææ§ã«ãªã£ãŠããŸããŸãâââããŒã¿ããªããã°ããã¹ãçµæãšãã®åå ãé¢é£ä»ããã®ãé£ãããªããŸãïŒããªã400ã¹ããŒã¿ã¹ãè¿ãã¹ããªã®ãïŒãïŒãå€å
žçãªæžç±ãx-unitãã¿ãŒã³ãã§ã¯ããã®ãã¿ãŒã³ããè¬ã®ã²ã¹ãããšåŒãã§ããŸãâââäœãèŠããªããã®ããã¹ãçµæã«åœ±é¿ãäžããŠããŸãããæ£ç¢ºã«ã¯äœã圱é¿ãäžããã®ãåãããŸãããç¹°ãè¿ã䜿ãããé·ãéšåãå€éšã«æœåºãããã¹ãã«éèŠãªå
·äœçãªè©³çްãæç€ºçã«ç€ºãããšã§ãæ¹åã§ããŸããäžèšã®äŸã䜿ããšããã¹ãã¯éèŠãªéšåã匷調ãããã©ã¡ãŒã¿ãŒãæž¡ãããšãã§ããŸãïŒtransferFactory.getJSON({sender: undefined})
ããã®äŸã§ã¯ãèªè
ã¯ããã«ãsenderãã£ãŒã«ãã空ã§ããããšãããã¹ããããªããŒã·ã§ã³ãšã©ãŒããã®ä»ã®é©åãªçµæãæåŸ
ããçç±ã§ããããšæšæž¬ãã¹ãã§ãã
â ãããªãã°: 500è¡ã®JSONãã³ããŒããããšã¯ããã¹ããç¶æäžå¯èœã§èªã¿ã«ããããŸãããã¹ãŠãå€éšã«ç§»åãããšãçè§£ãã«ããææ§ãªãã¹ããçãŸããŸã
â ã³ãŒãäŸ
ð ã¢ã³ããã¿ãŒã³äŸ: ãã¹ãã®å€±æãäžæç¢ºã§ãåå ãå€éšã«ããã倧ããªJSONå ã«é ããŠãã
test("ã¯ã¬ãžããããªãå Žåã転éã¯æåŠããã", async() => {
//Arrange(æºåãã)
const transferRequest = testHelpers.factorMoneyTransfer() //200è¡ã®JSONãè¿ã£ãŠãã;
const transferServiceUnderTest = new TransferService();
//Act(åãã)
const transferResponse = await transferServiceUnderTest.transfer(transferRequest);
//Assert(確èªãã)
expect(transferResponse.status).toBe(409);// ã§ããªã倱æãæåŸ
ããã®ã: ãã¹ãå
ã§ã¯ãã¹ãŠãå®ç§ã«æå¹ã«èŠãã ð€
});
test("ã¯ã¬ãžããããªãå Žåã転éã¯æåŠããã", async() => {
//Arrange(æºåãã)
const transferRequest = testHelpers.factorMoneyTransfer({userCredit:100, transferAmount:200}) // æããã«ã¯ã¬ãžãããäžè¶³ããŠãã
const transferServiceUnderTest = new TransferService({disallowOvercharge:true});
//Act(åãã)
const transferResponse = await transferServiceUnderTest.transfer(transferRequest);
//Assert(確èªãã)
expect(transferResponse.status).toBe(409); // ãŠãŒã¶ãŒã«ã¯ã¬ãžããããªãå Žåãæããã«å€±æããã¹ã
});
â ããããŸããã: ããå ¥åããšã©ãŒãåŒãèµ·ããããšãã¢ãµãŒãããããšãããšããtry-catch-finallyã䜿çšããŠcatchå¥ãå®è¡ãããããšã確èªããã®ã¯æ£ããããã«èŠãããããããŸããããããããã®çµæã¯ã·ã³ãã«ãªãã¹ãã®æå³ãçµæã®æåŸ ãé ããŠããŸããåé·ã§äžæ Œå¥œãªãã¹ãã±ãŒã¹ã«ãªããŸãïŒä»¥äžã®äŸåç §ïŒã
ãããšã¬ã¬ã³ããªä»£æ¿ææ®µã¯ãå°çšã®Chaiã¢ãµãŒã·ã§ã³ã1è¡ã§äœ¿çšããããšã§ãïŒexpect(method).to.throwïŒãŸãã¯Jestã§ã¯expect(method).toThrow()ïŒããŸããäŸå€ããšã©ãŒã¿ã€ãã瀺ãããããã£ãå«ãããšã確èªããããšã絶察ã«å¿ èŠã§ãããããªããšãåãªãäžè¬çãªãšã©ãŒãæž¡ãã ãã§ã¯ãã¢ããªã±ãŒã·ã§ã³ã¯ãŠãŒã¶ãŒã«å€±æãããã¡ãã»ãŒãžã衚瀺ãã以å€ã«å€ãã®ããšãã§ããŸããã
â ãããªãã°: ãã¹ãã¬ããŒãïŒäŸ: CIã¬ããŒãïŒããäœãåé¡ã ã£ãã®ããæšæž¬ããã®ãé£ãããªããŸãã
â ã³ãŒãäŸ
ð ã¢ã³ããã¿ãŒã³äŸ: try-catchã§ãšã©ãŒã®ååšãã¢ãµãŒãããããšããåé·ãªãã¹ãã±ãŒã¹
it("åååããªãå Žåããšã©ãŒ400ãã¹ããŒãã", async () => {
let errorWeExceptFor = null;
try {
const result = await addNewProduct({});
} catch (error) {
expect(error.code).to.equal("InvalidInput");
errorWeExceptFor = error;
}
expect(errorWeExceptFor).not.to.be.null;
// ãã®ã¢ãµãŒã·ã§ã³ã倱æããå Žåããã¹ãçµæ/ã¬ããŒãã«ã¯
// å€ãnullã§ããããšãã衚瀺ããããäŸå€ãèŠã€ãããªãããšã«ã€ããŠã¯èšåãããŸãã
});
it("åååããªãå Žåããšã©ãŒ400ãã¹ããŒãã", async () => {
await expect(addNewProduct({}))
.to.eventually.throw(AppError)
.with.property("code", "InvalidInput");
});
â ããããŸããã: ç°ãªããã¹ãã¯ç°ãªãã·ããªãªã§å®è¡ããå¿ èŠããããŸããäŸãã°ãã¯ã€ãã¯ãªã¹ã¢ãŒã¯ãã¹ããI/Oã¬ã¹ãªãã¹ãã¯ãéçºè ããã¡ã€ã«ãä¿åãŸãã¯ã³ããããããšãã«å®è¡ãããã«ãªãšã³ãããŒãšã³ããã¹ãã¯éåžžããã«ãªã¯ãšã¹ããæåºããããšãã«å®è¡ãããŸãããããå®çŸããããã«ã¯ã#cold #api #sanity ãªã©ã®ããŒã¯ãŒãã§ãã¹ãã«ã¿ã°ãä»ãããã¹ãããŒãã¹ã§grepã䜿ã£ãŠå¿ èŠãªãµãã»ãããåŒã³åºãããšãã§ããŸããäŸãã°ãMochaã§sanityãã¹ãã°ã«ãŒãã®ã¿ãåŒã³åºãæ¹æ³ã¯ä»¥äžã®éãã§ãïŒmochaâââgrep âsanityâ
â ãããªãã°: éçºè ãå°ããªå€æŽãå ãããã³ã«ãããŒã¿ããŒã¹ã¯ãšãªãæ°ååå®è¡ãããã¹ããå«ããŠãã¹ãŠã®ãã¹ããå®è¡ããã®ã¯éåžžã«é ããªããéçºè ããã¹ããå®è¡ããã®ãé¿ããåå ãšãªããŸãã
â ã³ãŒãäŸ
ð æ£ããäŸ: â#cold-testâãšããŠãã¹ãã«ã¿ã°ãä»ããããšã§ããã¹ãã©ã³ããŒã¯é«éãªãã¹ãã®ã¿ãå®è¡ã§ããããã«ãªããŸãïŒCold === I/Oãè¡ããªãè¿ éãªãã¹ãã§ãéçºè ãå ¥åããŠããéã§ãé »ç¹ã«å®è¡ã§ããŸãïŒ
//ãã®ãã¹ãã¯é«éã§ïŒDBã䜿çšããªãïŒãããã«å¿ããŠã¿ã°ä»ãããŠããŸã
//ããã§ãŠãŒã¶ãŒãCIã¯é »ç¹ã«å®è¡ã§ããŸã
describe("Order service", function() {
describe("æ°ããæ³šæã®è¿œå #cold-test #sanity", function() {
test("ã·ããªãª - é貚ãæå®ãããŠããªããæåŸ
- ããã©ã«ãé貚ãäœ¿çš #sanity", function() {
//ã³ãŒãããžãã¯ããã«
});
});
});
â ããããŸããã: ãã¹ãã¹ã€ãŒãã«ããçšåºŠã®æ§é ãé©çšããå¶ç¶èšªãã人ããã¹ãã®èŠä»¶ïŒãã¹ãã¯æé«ã®ããã¥ã¡ã³ãïŒããã¹ããããŠããããŸããŸãªã·ããªãªãç°¡åã«çè§£ã§ããããã«ããŸãããã®æ¹æ³ã®äžè¬çãªãã®ã¯ããã¹ãã®åã«å°ãªããšã2ã€ã®'describe'ãããã¯ãé 眮ããããšã§ãã1ã€ç®ã¯ãã¹ã察象ã®ãŠãããã®ååã2ã€ç®ã¯ã·ããªãªãã«ã¹ã¿ã ã«ããŽãªãŒãªã©ã®è¿œå ã®åé¡ã¬ãã«ã§ãïŒä»¥äžã®ã³ãŒãäŸãšã¹ã¯ãªãŒã³ã·ã§ãããåç §ïŒãããã«ããããã¹ãã¬ããŒããå€§å¹ ã«æ¹åãããŸãïŒèªè ã¯ãã¹ãã®ã«ããŽãªãŒãç°¡åã«æšæž¬ããåžæããã»ã¯ã·ã§ã³ã«æ·±å ¥ãããŠå€±æãããã¹ããšçžé¢ãããããšãã§ããŸããããã«ãå€ãã®ãã¹ããå«ãã¹ã€ãŒãã®ã³ãŒããéçºè ãããã²ãŒãããã®ããã£ãšç°¡åã«ãªããŸãããã¹ãã¹ã€ãŒãã®æ§é ã«é¢ããŠãgiven-when-then ã RITE ãªã©ã®è€æ°ã®ä»£æ¿æ§é ãæ€èšããããšãã§ããŸãã
â ãããªãã°: ãã©ããã§é·ããã¹ãã®ãªã¹ããèŠããšããé·ãããã¹ãããã£ãšèªãã§äž»èŠãªã·ããªãªãçµè«ãã倱æãããã¹ãã®å ±éç¹ãçžé¢ãããå¿ èŠããããŸããäŸãã°ã100ã®ãã¹ãã®ãã¡7ã€ã倱æããå Žåããã©ãããªãªã¹ãã§ã¯å€±æãããã¹ãã®ããã¹ããèªã¿ãã©ã®ããã«é¢é£ããŠãããã確èªããªããã°ãªããŸãããããããéå±€çãªã¬ããŒãã§ã¯ããã¹ãŠãåããããŒãã«ããŽãªã®äžã«ãããåå ããã°ããæšæž¬ã§ããŸããå°ãªããšãã©ããæ ¹æ¬çãªå€±æã®åå ããæšæž¬ããããšãã§ããŸãã
â ã³ãŒãäŸ
ð æ£ããäŸ: ãã¹ã察象ãŠããããšã·ããªãªã§ã¹ã€ãŒããæ§é åãããšã以äžã®ãããªäŸ¿å©ãªã¬ããŒããåŸãããŸã
// ãã¹ã察象ãŠããã
describe("Transfer service", () => {
// ã·ããªãª
describe("When no credit", () => {
// æåŸ
å€
test("Then the response status should decline", () => {});
// æåŸ
å€
test("Then it should send email to admin", () => {});
});
});
ð ã¢ã³ããã¿ãŒã³äŸ: ãã¹ãã®ãã©ãããªãªã¹ãã¯ãèªè ããŠãŒã¶ãŒã¹ããŒãªãŒãç¹å®ãã倱æãããã¹ããé¢é£ä»ããã®ãé£ãããã
test("ã¬ã¹ãã³ã¹ã¹ããŒã¿ã¹ãæåŠãããã¹ã", () => {});
test("ããã管çè
ã«ã¡ãŒã«ãéä¿¡ããã¹ã", () => {});
test("æ°ãã転éã¬ã³ãŒããäœæãããªãã¹ã", () => {});
â ããããŸããã: ãã®æçš¿ã¯ãNode JSã«é¢é£ããããŸãã¯å°ãªããšãNode JSãäŸã«æããŠèª¬æã§ãããã¹ãã«é¢ããã¢ããã€ã¹ã«çŠç¹ãåœãŠãŠããŸãããã ãããã®é ç®ã§ã¯ãNodeã«é¢é£ããªãããã€ãã®ããç¥ãããã¢ããã€ã¹ããŸãšããŠããŸãã
TDDã®ååãåŠã³ãå®è·µããâââããã¯å€ãã®äººã«ãšã£ãŠéåžžã«äŸ¡å€ããããŸãããèªåã®ã¹ã¿ã€ã«ã«åããªãå Žåã§ãæ°ã«ããªãã§ãã ãããããªããå¯äžã®äººã§ã¯ãããŸãããã³ãŒãã®åã«ãã¹ããæžãããšãæ€èšããã¬ããã»ã°ãªãŒã³ã»ãªãã¡ã¯ã¿ãªã³ã°ã¹ã¿ã€ã«ãæ¡çšããåãã¹ããæ£ç¢ºã«1ã€ã®ããšããã§ãã¯ããããã«ããŸãããããã°ãèŠã€ããå Žåã¯ããããä¿®æ£ããåã«ãã®ãã°ãå°æ¥æ€åºã§ãããã¹ããæžããå°ãªããšã1åã¯ãã¹ãã倱æãããåŸã«æåããããã«ããŸããããã¢ãžã¥ãŒã«ãå§ãããšãã¯ãæåã«ãã¹ããæºããããã®ç°¡åã§ã·ã³ãã«ãªã³ãŒããæžãããã®åŸãåŸã
ã«ãªãã¡ã¯ã¿ãªã³ã°ããŠæ¬çªçšã®å質ã«ä»äžããŠãããŸããç°å¢ïŒãã¹ãOSãªã©ïŒãžã®äŸåã¯é¿ããŸãããã
â ãããªãã°: æ°å幎ã«ããã£ãŠéããããç¥æµã®å®ç³ãèŠéãããšã«ãªããŸã
âªïž 2.1 ããªãã®ãã¹ãããŒããã©ãªãªãè±ãã«ããïŒUnit testsãšãã©ããããè¶ ããŠ
â ããããŸãããïŒ testing pyramidã¯10幎以äžåã®ãã®ã§ããã3ã€ã®ãã¹ãã¿ã€ããææ¡ããå€ãã®éçºè ã®ãã¹ãæŠç¥ã«åœ±é¿ãäžããçŽ æŽãããé¢é£æ§ã®é«ãã¢ãã«ã§ããåæã«ãtesting pyramidã®åœ±ã«é ããèŒãããæ°ãããã¹ãææ³ãããã€ãç»å ŽããŸãããéå»10幎ã«èŠãããïŒMicroservicesãcloudãserverlessïŒãšãã£ãåçãªå€åãèãããšãäžã€ã®å€ãã¢ãã«ãå šãŠã®ã¢ããªã±ãŒã·ã§ã³ã¿ã€ãã«é©åããããšãå¯èœãªã®ã§ããããïŒãã¹ãã®äžçã¯æ°ãããã¹ãææ³ãæè¿ãã¹ãã§ã¯ãªãã§ããããïŒ
誀解ããªãã§ãã ããã2019幎ã«ãããŠããtesting pyramidãTDDãunit testsã¯äŸç¶ãšããŠåŒ·åãªææ³ã§ãããå€ãã®ã¢ããªã±ãŒã·ã§ã³ã«ãšã£ãŠæé©ãªéžæã§ããããããããä»ã®ã©ããªã¢ãã«ãšåæ§ã«ããã®æçšæ§ã«ãããããããæã«ã¯ééã£ãŠããã«éããããŸãããäŸãã°ãKafka/RabbitMQã®ãããªã¡ãã»ãŒãžãã¹ã«å€ãã®ã€ãã³ããåã蟌ãIoTã¢ããªã±ãŒã·ã§ã³ãèããŠã¿ãŸãããããããã¯ããŒã¿ãŠã§ã¢ããŠã¹ã«æµã蟌ã¿ãæçµçã«ã¯åæçšUIã§ã¯ãšãªãããŸããã»ãšãã©ããžãã¯ããªããçµ±åãäžå¿ã®ã¢ããªã±ãŒã·ã§ã³ã«å¯ŸããŠããã¹ãäºç®ã®50%ãUnit testsã®äœæã«è²»ããã¹ãã§ããããïŒã¢ããªã±ãŒã·ã§ã³ã¿ã€ãã®å€æ§æ§ãå¢ãã«ã€ããŠïŒbotsãcryptoãAlexa-skillsïŒãtesting pyramidãæé©ãªé©åã§ã¯ãªãã·ããªãªãèŠã€ããå¯èœæ§ãé«ããªããŸãã
ããªãã®ãã¹ãããŒããã©ãªãªãè±ãã«ããããå€ãã®ãã¹ãã¿ã€ãã«ç²Ÿéããæã§ãïŒæ¬¡ã®ç®æ¡æžãã§ããã€ãã®ã¢ã€ãã¢ãææ¡ããŸãïŒãtesting pyramidã®ãããªãã€ã³ãã¢ãã«ãæã¡ãªããããçŽé¢ããŠããçŸå®ã®åé¡ã«ãã¹ãã¿ã€ããåãããŸãããïŒããããAPIãå£ããŠãããconsumer-driven contract testingãæžããïŒãïŒããªã¹ã¯åæã«åºã¥ããŠããŒããã©ãªãªãæ§ç¯ããæè³å®¶ã®ããã«ãã¹ãã倿§åããŸãããâåé¡ãçºçãåŸãç®æãè©äŸ¡ãããããã®æœåšçãªãªã¹ã¯ã軜æžããããã®äºé²çãé©çšããŸãã
泚æç¹ïŒãœãããŠã§ã¢ã®äžçã«ãããTDDã«é¢ããè°è«ã¯å žåçãªåœã®äºåæ³ãåããŠããŸããããããå Žæã§äœ¿ãã¹ãã ãšèª¬ã人ãããã°ãæªéã ãšèãã人ãããŸãã絶察çãªèšãæ¹ããã人ã¯çãééã£ãŠããŸã :]
â ãããªãã°ïŒ é©ãã»ã©ROIã®é«ãããŒã«ãèŠéãããšã«ãªããŸããFuzzãlintãmutationã®ãããªãã®ã¯10åã§äŸ¡å€ãæäŸã§ããŸã
â ã³ãŒãäŸ
ð æ£ããäŸ: Cindy Sridharan ã¯åœŒå¥³ã®çŽ æŽãããæçš¿ãTesting Microservicesâââthe same wayãã§è±å¯ãªãã¹ãããŒããã©ãªãªãææ¡ããŠããŸã
â ããããŸããã: åãŠããããã¹ãã¯ã¢ããªã±ãŒã·ã§ã³ã®ããäžéšãã«ããŒããŠãããå šäœãã«ããŒããã«ã¯é«ã³ã¹ãã§ããäžæ¹ããšã³ãããŒãšã³ããã¹ãã¯å€ããç°¡åã«ã«ããŒã§ããŸãããä¿¡é Œæ§ãäœããé ãã§ãããªãããã©ã³ã¹ã®åããã¢ãããŒããé©çšãããŠããããã¹ããã倧ããããšã³ãããŒãšã³ããã¹ãããå°ãããã¹ããæžãããšãèããªãã®ã§ããããïŒã³ã³ããŒãã³ããã¹ãã¯ãã¹ãã®äžçã®é ããåæ²ã§ãâââ圌ãã¯äž¡è ã®è¯ããšãããæäŸããŸãã劥åœãªããã©ãŒãã³ã¹ãšTDDãã¿ãŒã³ã®é©çšå¯èœæ§ããããŠçŸå®çã§çŽ æŽãããã«ãã¬ããžãæäŸããŸãã
ã³ã³ããŒãã³ããã¹ãã¯ãã€ã¯ããµãŒãã¹ã®ãåäœãã«çŠç¹ãåœãŠãŠãããAPIã«å¯ŸããŠåäœãããã€ã¯ããµãŒãã¹èªäœã«å±ãããã®ïŒäŸãã°ãå®éã®DBããããã¯å°ãªããšããã®DBã®ã€ã³ã¡ã¢ãªçïŒãã¢ãã¯ãããä»ã®ãã€ã¯ããµãŒãã¹ãžã®åŒã³åºããªã©ã®å€éšã®ãã®ã¯ã¹ã¿ãããŸãããã®æ¹æ³ã«ããããããã€ãããã®ããã¹ãããã¢ããªãå€éšããå
éšãžãšã¢ãããŒãããåççãªæéã§å€§ããªèªä¿¡ãåŸãããšãã§ããŸãã
â ãããªãã°: ãŠããããã¹ããæžãããšã«é·ãæ¥ã ãè²»ãããã·ã¹ãã ã®ãã£ã20ïŒ ããã«ããŒã§ããŠããªãããšãåãããããããŸããã
â ã³ãŒãäŸ
⪠ïž2.3 ã³ã³ãã©ã¯ããã¹ãã䜿çšããŠæ°ãªãªãŒã¹ãAPIãå£ããªãããšãä¿èšŒãã
â
ããããŸããã: ããªãã®ãã€ã¯ããµãŒãã¹ã«ã¯è€æ°ã®ã¯ã©ã€ã¢ã³ãããããäºææ§ã®çç±ã§è€æ°ã®ããŒãžã§ã³ã®ãµãŒãã¹ãå®è¡ããŠããŸãïŒãã¹ãŠã®äººãæºè¶³ãããããïŒãããã§ãäœããã£ãŒã«ãã倿Žããããšãããããã«ã³ïŒããšããã®ãã£ãŒã«ãã«äŸåããŠããéèŠãªã¯ã©ã€ã¢ã³ããæã£ãŠããŸããŸãããããã¯çµ±åã®äžçã®ãžã¬ã³ãã§ãïŒãµãŒããŒåŽãè€æ°ã®ã¯ã©ã€ã¢ã³ãã®æåŸ
ããã¹ãŠèæ
®ããã®ã¯éåžžã«ææŠçã§ãããäžæ¹ã§ã¯ã©ã€ã¢ã³ãã¯ãµãŒããŒããªãªãŒã¹æ¥ãå¶åŸ¡ããããããã¹ããå®è¡ã§ããŸãããConsumer-driven contracts ãšãã¬ãŒã ã¯ãŒã¯PACTã¯ãéåžžã«é©æ°çãªã¢ãããŒãã§ãã®ããã»ã¹ã圢åŒåããããã«çãŸããŸããããµãŒããŒã¯èªåèªèº«ã®ãã¹ãèšç»ãå®çŸ©ããã®ã§ã¯ãªããã¯ã©ã€ã¢ã³ãããµãŒããŒã®ãã¹ããå®çŸ©ããŸãïŒPACTã¯ã¯ã©ã€ã¢ã³ãã®æåŸ
ãèšé²ããå
±æå ŽæããããŒã«ãŒãã«é
眮ããããšã§ããµãŒããŒãPACTã©ã€ãã©ãªã䜿çšããŠåãã«ãã§æåŸ
ãåŒãåºããç Žããå¥çŽâââæºããããªãã¯ã©ã€ã¢ã³ãã®æåŸ
âââãæ€åºããããšãã§ããŸãããã®æ¹æ³ã«ããããã¹ãŠã®ãµãŒããŒã¯ã©ã€ã¢ã³ãAPIã®äžäžèŽããã«ã/CIã®æ©æã«çºèŠããã倧ããªãã©ã¹ãã¬ãŒã·ã§ã³ãé¿ããããšãã§ãããããããŸããã
â ãããªãã°: ä»ã®éžæè¢ã¯ç²ããæåãã¹ãããããã€ã®ææã§ãã
â
ããããŸããã: ããã«ãŠã§ã¢ãã¹ããé¿ãã人ã¯å€ãã§ãããããã¯ã·ã¹ãã ã®å°ããªéšåã§ãããã©ã€ãã®ExpressãµãŒããŒãå¿
èŠãšããããã§ãããããã®çç±ã¯ã©ã¡ããééã£ãŠããŸãâââ ããã«ãŠã§ã¢ã¯å°ããã§ããããã¹ãŠãŸãã¯ã»ãšãã©ã®ãªã¯ãšã¹ãã«åœ±é¿ãäžãã{req,res} JSãªããžã§ã¯ããåãåãçŽé¢æ°ãšããŠç°¡åã«ãã¹ãã§ããŸããããã«ãŠã§ã¢é¢æ°ããã¹ãããã«ã¯ããããåŒã³åºãã{req,res}ãªããžã§ã¯ããšã®ãããšããã¹ãã€ïŒäŸãã°Sinonã䜿çšããŠïŒããŠã颿°ãæ£ããã¢ã¯ã·ã§ã³ãå®è¡ããããšã確èªããã ãã§ããã®ã§ããnode-mock-httpã©ã€ãã©ãªã¯ã{req,res}ãªããžã§ã¯ããå æ°åè§£ãããããã®æ¯ãèããã¹ãã€ããããšããã«é²ã¿ãŸããäŸãã°ãresãªããžã§ã¯ãã«èšå®ãããHTTPã¹ããŒã¿ã¹ãæåŸ
ã«åã£ãŠããããã¢ãµãŒãã§ããŸãïŒä»¥äžã®äŸãåç
§ïŒã
â ãããªãã°: Expressããã«ãŠã§ã¢ã®ãã° === ãã¹ãŠãŸãã¯ã»ãšãã©ã®ãªã¯ãšã¹ãã®ãã°
â ã³ãŒãäŸ
ð æ£ããäŸ: ãããã¯ãŒã¯åŒã³åºããçºè¡ãããExpresså šäœãèµ·åãããã«ããã«ãŠã§ã¢ãåäœã§ãã¹ã
// ãã¹ããããããã«ãŠã§ã¢
const unitUnderTest = require("./middleware");
const httpMocks = require("node-mocks-http");
// Jestã®ææ³ãMochaã®describe() & it()ãšåç
test("èªèšŒããããŒãªãã®ãªã¯ãšã¹ãã¯ãhttpã¹ããŒã¿ã¹403ãè¿ãã¹ã", () => {
const request = httpMocks.createRequest({
method: "GET",
url: "/user/42",
headers: {
authentication: ""
}
});
const response = httpMocks.createResponse();
unitUnderTest(request, response);
expect(response.statusCode).toBe(403);
});
â ããããŸããã: éçè§£æããŒã«ã䜿çšããããšã§ãã³ãŒãã®å質ãåäžãããã³ãŒããä¿å®å¯èœã«ä¿ã€ããã®å®¢èгçãªæ¹æ³ãæäŸãããŸããéçè§£æããŒã«ãCIãã«ãã«è¿œå ããŠãã³ãŒãã®æªèãçºèŠãããšãã«ãã«ããäžæ¢ããããšãã§ããŸããéåžžã®ãªã³ãã£ã³ã°ã«å¯Ÿããäž»ãªå£²ãã®ãã€ã³ãã¯ãè€æ°ã®ãã¡ã€ã«ã®æèã§åè³ªãæ€æ»ããèœåïŒäŸ: éè€ã®æ€åºïŒãé«åºŠãªè§£æã®å®æœïŒäŸ: ã³ãŒãã®è€éãïŒãããŠã³ãŒãã®åé¡ã®å±¥æŽãšé²æã®è¿œè·¡ã§ãã䜿çšã§ããããŒã«ã®äŸãšããŠãSonarQubeïŒ4,900以äžã®starsïŒãšCode ClimateïŒ2,000以äžã®starsïŒããããŸãã
ã¯ã¬ãžãã: Keith Holliday
â ãããªãã°: äœå質ã®ã³ãŒãã§ã¯ããã°ãšããã©ãŒãã³ã¹ãåžžã«åé¡ãšãªããã©ããªæ°ããã©ã€ãã©ãªãæå ç«¯ã®æ©èœã§ã解決ã§ããŸããã
â ã³ãŒãäŸ
â
ããããŸããã: å¥åŠãªããšã«ãã»ãšãã©ã®ãœãããŠã§ã¢ãã¹ãã¯ããžãã¯ãšããŒã¿ã®ã¿ã§ãããææªã®åºæ¥äºïŒãããŠæ¬åœã«ç·©åãé£ãããã®ïŒã¯ã€ã³ãã©ã¹ãã©ã¯ãã£ã®åé¡ã§ããäŸãã°ãããã»ã¹ã¡ã¢ãªãéè² è·ã«ãªã£ããããµãŒããŒ/ããã»ã¹ãã¯ã©ãã·ã¥ããããAPIã®åŠçã50ïŒ
é
ããªã£ããšãã«ç£èŠã·ã¹ãã ãèªèãããã©ããããã¹ãããããšããããŸããïŒãããã®æªåœ±é¿ããã¹ãããŠç·©åããããã«âââã«ãªã¹ãšã³ãžãã¢ãªã³ã°ã¯Netflixã«ãã£ãŠçã¿åºãããŸãããããã¯ãæ··ä¹±ããåé¡ã«å¯Ÿããã¢ããªã®å埩åããã¹ãããããã®èªèããã¬ãŒã ã¯ãŒã¯ãããŒã«ãæäŸããããšãç®çãšããŠããŸããäŸãã°ããã®æåãªããŒã«ã®äžã€ãã«ãªã¹ã¢ã³ããŒã¯ãµãŒããŒãã©ã³ãã ã«åæ¢ãããããšã§ããµãŒãã¹ããŠãŒã¶ãŒã«ãµãŒãã¹ãæäŸãç¶ããåäžã®ãµãŒããŒã«äŸåããªãããšã確èªããŸãïŒãŸããKubernetesçãšããŠãããã忢ããkube-monkeyããããŸãïŒããããã®ããŒã«ã¯ãã¹ãŠãã¹ãã£ã³ã°/ãã©ãããã©ãŒã ã¬ãã«ã§æ©èœããŸãããçŽç²ãªNodeã«ãªã¹ããã¹ãããã³çæãããå Žåã¯ã©ãã§ãããããããšãã°ãNodeããã»ã¹ãæªåŠçã®ãšã©ãŒãæªåŠçã®promiseæåŠã1.7GBã®æå€§èš±å¯ã¡ã¢ãªã§ã®v8ã¡ã¢ãªéè² è·ã«ã©ã®ããã«å¯ŸåŠãããããŸãã¯ã€ãã³ãã«ãŒããé »ç¹ã«ãããã¯ããããšãã«UXãæºè¶³ãªç¶æ
ã§ãããã©ããã確èªããå Žåãªã©ãèæ
®ããããã«ãç§ã¯node-chaosïŒã¢ã«ãã¡çïŒãäœæããŸãããããã¯Nodeé¢é£ã®ã«ãªã¹çãªè¡çºããã¹ãŠæäŸããŸãã
â ãããªãã°: ããã«ã¯éãå Žã¯ãããŸãããããŒãã£ãŒã®æ³åãããªãã®ãããã¯ã·ã§ã³ã容赊ãªã襲æããŸã
â ã³ãŒãäŸ
⪠ïž2.7 ã°ããŒãã«ãªãã¹ããã£ã¯ã¹ãã£ãšã·ãŒããé¿ãããã¹ãããšã«ããŒã¿ã远å ãã
â
ããããŸããã: ãŽãŒã«ãã³ã«ãŒã«ïŒç®æ¡æžã0ïŒã«åŸããšãåãã¹ãã¯ç¬èªã®DBè¡ã»ããã远å ããããã«åºã¥ããŠåäœããçµåãé²ãããã¹ããããŒã«ã€ããŠç°¡åã«çç±ã¥ãã§ããããã«ãã¹ãã§ããçŸå®ã«ã¯ãããã©ãŒãã³ã¹åäžã®ããã«ãã¹ãåã«DBã«ããŒã¿ãã·ãŒãïŒããããããã¹ããã£ã¯ã¹ãã£ãïŒãè¡ããã¹ã¿ãŒã«ããããã®ã«ãŒã«ã¯ãã°ãã°éåãããŸãã確ãã«ããã©ãŒãã³ã¹ã¯æ£åœãªæžå¿µã§ããïŒãã³ã³ããŒãã³ããã¹ããã®ç®æ¡æžããåç
§ïŒããã¹ãã®è€éãã¯ä»ã®èæ
®äºé
ãæ¯é
ãã¹ãèŸãåé¡ã§ããå®è·µçã«ã¯ãåãã¹ãã±ãŒã¹ãå¿
èŠãªDBã¬ã³ãŒããæç€ºçã«è¿œå ãããããã®ã¬ã³ãŒãã®ã¿ã«åºã¥ããŠåäœããŸããããããã©ãŒãã³ã¹ãéèŠãªæžå¿µãšãªã£ãå Žåã¯ãããŒã¿ãå€åãããªãäžéšã®ãã¹ãã¹ã€ãŒãïŒäŸ: ã¯ãšãªïŒã®ã¿ãã·ãŒããããšãã圢ã§ãã©ã³ã¹ã®åãã劥åããããããããŸããã
â ãããªãã°: ããã€ãã®ãã¹ãã倱æãããããã€ãäžæ¢ãããç§ãã¡ã®ããŒã ã¯è²Žéãªæéãè²»ããããšã«ãªããããã°ããããŸããïŒããšèª¿æ»ããããšã«ãªãããšãŠã倧å€ã§ãããããŠããããã2ã€ã®ãã¹ããåãã·ãŒãããŒã¿ãå€åãããŠããŸããããšæ°ã¥ãã®ã§ãã
â ã³ãŒãäŸ
ð ã¢ã³ããã¿ãŒã³äŸ: ãã¹ããç¬ç«ããŠããããã°ããŒãã«ãªããã¯ã«äŸåããŠã°ããŒãã«ãªDBããŒã¿ãäŸçµŠ
before(async () => {
// ãµã€ããšç®¡çè
ããŒã¿ãDBã«è¿œå ããŸããããŒã¿ã¯ã©ãã§ããïŒå€éšã§ããããã€ãã®å€éšjsonãŸãã¯ç§»è¡ãã¬ãŒã ã¯ãŒã¯
await DB.AddSeedDataFromJson('seed.json');
});
it("ãµã€ãåãæŽæ°ãããšãæåã®ç¢ºèªãåŸã", async () => {
// ãµã€ãå "portal" ãååšããããšãç¥ã£ãŠãã - ã·ãŒããã¡ã€ã«ã§ãããèŠã
const siteToUpdate = await SiteService.getSiteByName("Portal");
const updateNameResult = await SiteService.changeName(siteToUpdate, "newName");
expect(updateNameResult).to.be(true);
});
it("ãµã€ãåã§ã¯ãšãªãããšãæ£ãããµã€ããååŸãã", async () => {
// ãµã€ãå "portal" ãååšããããšãç¥ã£ãŠãã - ã·ãŒããã¡ã€ã«ã§ãããèŠã
const siteToCheck = await SiteService.getSiteByName("Portal");
expect(siteToCheck.name).to.be.equal("Portal"); // 倱æïŒåã®ãã¹ãã§ååã倿Žããã :[
});
ð æ£ããäŸ: ãã¹ãå ã§å®æœãç¶ããããåãã¹ãã¯ç¬èªã®ããŒã¿ã»ããã§åäœ
it("ãµã€ãåãæŽæ°ãããšãæåã®ç¢ºèªãåŸã", async () => {
// ãã¹ãã¯æ°ããã¬ã³ãŒãã远å ããã¬ã³ãŒãã«å¯ŸããŠã®ã¿åäœãã
const siteUnderTest = await SiteService.addSite({
name: "siteForUpdateTest"
});
const updateNameResult = await SiteService.changeName(siteUnderTest, "newName");
expect(updateNameResult).to.be(true);
});
â ããããŸããã: ã³ã³ããŒãã³ãããžãã¯ã®ãã¹ãã«éäžãããšãã¯ãUIã®è©³çްããã€ãºã«ãªãåŸãããåãé€ãã¹ãã§ããããã«ããããã¹ããçŽç²ãªããŒã¿ã«çŠç¹ãåœãŠãããŸããå®éã«ã¯ãããŒã¯ã¢ããããã°ã©ãã£ãã¯ã®å®è£ ã«ããŸãçµã³ã€ããªãæœè±¡çãªæ¹æ³ã§å¿ èŠãªããŒã¿ãæœåºããçŽç²ãªããŒã¿ïŒHTML/CSSã®ã°ã©ãã£ãã¯è©³çްã§ã¯ãªãïŒã®ã¿ãã¢ãµãŒãããé å»¶ãåŒãèµ·ããã¢ãã¡ãŒã·ã§ã³ãç¡å¹ã«ããŸããUIã®è£åŽïŒäŸ: ãµãŒãã¹ãã¢ã¯ã·ã§ã³ãã¹ãã¢ïŒã®ã¿ããã¹ãããŠã¬ã³ããªã³ã°ãé¿ããããªããããããŸããããããã¯çŸå®ãšäŒŒãŠããªãæ¶ç©ºã®ãã¹ããšãªããæ£ããããŒã¿ãUIã«å±ããŠããªãã±ãŒã¹ãæããã«ããŸããã
â ãããªãã°: ãã¹ãã®çŽç²ãªèšç®ããŒã¿ã¯10msã§æºåãã§ãããããããŸããããé¢ä¿ã®ãªãã¢ãã¡ãŒã·ã§ã³ã«ãã£ãŠå šäœã®ãã¹ãã500msïŒ100ã®ãã¹ã = 1åïŒã«ãªããããããŸããã
â ã³ãŒãäŸ
test("users-listã«VIPã®ã¿ã衚瀺ãããã©ã°ãèšå®ãããŠããå ŽåãVIPã¡ã³ããŒã®ã¿ã衚瀺ãã", () => {
// Arrange
const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }];
// Act
const { getAllByTestId } = render(<UsersList users={allUsers} showOnlyVIP={true} />);
// Assert - ãŸãUIããããŒã¿ãæœåºãã
const allRenderedUsers = getAllByTestId("user").map(uiElement => uiElement.textContent);
const allRealVIPUsers = allUsers.filter(user => user.vip).map(user => user.name);
expect(allRenderedUsers).toEqual(allRealVIPUsers); // ããŒã¿ãšããŒã¿ãæ¯èŒãUIã¯ããã«ã¯ãªã
});
test("users-listã«VIPã®ã¿ã衚瀺ãããã©ã°ãèšå®ãããŠããå ŽåãVIPã¡ã³ããŒã®ã¿ã衚瀺ãã", () => {
// Arrange
const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }];
// Act
const { getAllByTestId } = render(<UsersList users={allUsers} showOnlyVIP={true} />);
// Assert - ã¢ãµãŒã·ã§ã³ã«UIãšããŒã¿ãæ··åšããã
expect(getAllByTestId("user")).toEqual('[<li data-test-id="user">John Doe</li>]');
});
â ããããŸããã: CSSã»ã¬ã¯ã¿ãŒããã©ãŒã ã©ãã«ã®ããã«ã°ã©ãã£ãã¯ã®å€æŽã«ãèããå¯èœæ§ããã屿§ã«åºã¥ããŠHTMLèŠçŽ ãã¯ãšãªããŸããæå®ãããèŠçŽ ã«ãã®ãããªå±æ§ããªãå Žåã 'test-id-submit-button' ã®ãããªå°çšã®ãã¹ã屿§ãäœæããŸãããã®æ¹æ³ã ãšãæ©èœ/ããžãã¯ãã¹ãã¯å€èгã®å€æŽã«ãã£ãŠå£ããããšããªããªãã ãã§ãªãããã®èŠçŽ ãšå±æ§ããã¹ãã§äœ¿çšããããã®ããåé€ãããã¹ãã§ã¯ãªãããšãããŒã å šäœã«æç¢ºã«ãªããŸãã
â ãããªãã°: å€ãã®ã³ã³ããŒãã³ããããžãã¯ããµãŒãã¹ã«ããããã°ã€ã³æ©èœããã¹ãããããšããæºåã¯å®ç§ â ã¹ã¿ããã¹ãã€ãAjaxã³ãŒã«ãå€ç«ããŠããŸãããã¹ãŠã¯å®ç§ã«èŠããŸããããããã¶ã€ããŒãdivã®CSSã¯ã©ã¹ã 'thick-border' ãã 'thin-border' ã«å€æŽããããã«ãã¹ãã倱æããŸãã
â ã³ãŒãäŸ
// ããŒã¯ã¢ããã³ãŒãïŒReactã³ã³ããŒãã³ãã®äžéšïŒ
<h3>
<Badge pill className="fixed_badge" variant="dark">
<span data-test-id="errorsLabel">{value}</span>
<!-- 屿§ data-test-id ã«æ³šç® -->
</Badge>
</h3>
// ãã®äŸã¯react-testing-libraryã䜿çšããŠããŸã
test("Whenever no data is passed to metric, show 0 as default", () => {
// Arrange
const metricValue = undefined;
// Act
const { getByTestId } = render(<dashboardMetric value={undefined} />);
expect(getByTestId("errorsLabel").text()).toBe("0");
});
<!-- ããŒã¯ã¢ããã³ãŒãïŒReactã³ã³ããŒãã³ãã®äžéšïŒ -->
<span id="metric" className="d-flex-column">{value}</span>
<!-- ãã¶ã€ããŒãã¯ã©ã¹ã倿Žãããã©ãããŸããïŒ -->
// ãã®äŸã¯enzymeã䜿çšããŠããŸã
test("Whenever no data is passed, error metric shows zero", () => {
// ...
expect(wrapper.find("[className='d-flex-column']").text()).toBe("0");
});
⪠ïž3.3 å¯èœãªéãããªã¢ã«ã§å®å šã«ã¬ã³ããªã³ã°ãããã³ã³ããŒãã³ãã§ãã¹ããã
â ããããŸããã: ãµã€ãºãé©åã§ããéãããŠãŒã¶ãŒãè¡ãããã«ã³ã³ããŒãã³ããå€ãããã¹ãããUIãå®å šã«ã¬ã³ããªã³ã°ããããã«å¯ŸããŠæäœãè¡ããã¬ã³ããªã³ã°ãããUIãæåŸ éãã«åäœããããšãã¢ãµãŒãããŸãããã¹ãŠã®çš®é¡ã®ã¢ãã¯ãéšåçããã³æµ ãã¬ã³ããªã³ã°ãé¿ããŸã â ãã®ã¢ãããŒãã¯ãè©³çŽ°ã®æ¬ åŠã«ãããã°ã®èŠèœãšããããã¹ããå éšã«å¹²æžããããã¡ã³ããã³ã¹ãé£ããããå¯èœæ§ããããŸãïŒããã©ãã¯ããã¯ã¹ãã¹ãã奜ãããåç §ïŒãããåã³ã³ããŒãã³ãã®äžã€ãèããåäœãé ããããïŒäŸ: ã¢ãã¡ãŒã·ã§ã³ïŒãã»ããã¢ãããè€éã«ãããããå Žåã¯ãæç€ºçã«åœã®ãã®ã«çœ®ãæããããšãæ€èšããŠãã ããã
ããã«å ããŠã泚æãèŠããèšèã®äžã€ããããŸãïŒãã®æè¡ã¯ãé©åãªãµã€ãºã®åã³ã³ããŒãã³ããããã¯ããŠããå°èŠæš¡/äžèŠæš¡ã®ã³ã³ããŒãã³ãã«å¯ŸããŠæ©èœããŸããããŸãã«å€ãã®åãæã€ã³ã³ããŒãã³ããå®å šã«ã¬ã³ããªã³ã°ããããšã¯ããã¹ãã®å€±æïŒæ ¹æ¬åå ã®åæïŒã«ã€ããŠã®èª¬æãå°é£ã«ããããããšããããããŸãã«ãé ããªãå¯èœæ§ããããŸãããã®ãããªå Žåããã®å€§ããªèŠªã³ã³ããŒãã³ãã«å¯ŸããŠã¯ããã€ãã®ãã¹ããè¡ãããã®åã«å¯ŸããŠã¯å€ãã®ãã¹ããè¡ããŸãã
â ãããªãã°: ã³ã³ããŒãã³ãã®ãã©ã€ããŒãã¡ãœãããåŒã³åºãããã®å éšç¶æ ããã§ãã¯ããããšã«ãã£ãŠãã³ã³ããŒãã³ãã®å éšãã€ã€ããšããã³ã³ããŒãã³ãã®å®è£ ããªãã¡ã¯ã¿ãªã³ã°ãããšãã«ãã¹ãŠã®ãã¹ãããªãã¡ã¯ã¿ãªã³ã°ããå¿ èŠããããŸãããã®ã¬ãã«ã®ã¡ã³ããã³ã¹ãè¡ãèœåããããŸããïŒ
â ã³ãŒãäŸ
class Calendar extends React.Component {
static defaultProps = { showFilters: false };
render() {
return (
<div>
A filters panel with a button to hide/show filters
<FiltersPanel showFilter={showFilters} title="Choose Filters" />
</div>
);
}
}
// äŸã¯React & Enzymeã䜿çš
test("ãªã¢ã«ãªã¢ãããŒã: ãã£ã«ã¿ãŒã衚瀺ããããã«ã¯ãªãã¯ãããšããã£ã«ã¿ãŒã衚瀺ããã", () => {
// Arrange
const wrapper = mount(<Calendar showFilters={false} />);
// Act
wrapper.find("button").simulate("click");
// Assert
expect(wrapper.text().includes("Choose Filter"));
// ãŠãŒã¶ãŒã¯ãã®èŠçŽ ã«æ¬¡ã®ããã«ã¢ãããŒããã: by text
});
test("æµ
ã/ã¢ãã¯ãããã¢ãããŒã: ãã£ã«ã¿ãŒã衚瀺ããããã«ã¯ãªãã¯ãããšããã£ã«ã¿ãŒã衚瀺ããã", () => {
// Arrange
const wrapper = shallow(<Calendar showFilters={false} title="Choose Filter" />);
// Act
wrapper
.find("filtersPanel")
.instance()
.showFilters();
// å
éšã«çŽæ¥ã¢ã¯ã»ã¹ããUIãã¹ãããããã¡ãœãããåŒã³åºããŸãããã¯ã€ãããã¯ã¹ã¢ãããŒã
// Assert
expect(wrapper.find("Filter").props()).toEqual({ title: "Choose Filter" });
// ããããããåã倿Žããããé¢é£ãããã®ãäœãæž¡ããªãã£ããã©ããªããŸããïŒ
});
⪠ïž3.4 ã¹ãªãŒãããããã¬ãŒã ã¯ãŒã¯ã®çµã¿èŸŒã¿ãµããŒãã䜿ã£ãŠéåæã€ãã³ããåŠçããå¯èœãªéãé«éåãã
â
ããããŸããã: ãã¹ã察象ã®ãŠãããã®å®äºæéãäžæã§ããå Žåãå€ããããŸãïŒäŸ: ã¢ãã¡ãŒã·ã§ã³ãèŠçŽ ã®åºçŸãé
å»¶ãããïŒããã®å Žåãã¹ãªãŒãïŒäŸ: setTimeOutïŒãé¿ããã»ãšãã©ã®ãã©ãããã©ãŒã ãæäŸããããæ±ºå®è«çãªã¡ãœãããå©çšããŸããäžéšã®ã©ã€ãã©ãªã§ã¯æäœãåŸ
ã€ããšãèš±å¯ãããŠããŸãïŒäŸ: Cypress cy.request('url')ïŒãä»ã¯@testing-library/dom ã¡ãœãã wait(expect(element))ã®ããã«åŸ
æ©çšã®APIãæäŸããŠããŸãããããšã¬ã¬ã³ããªæ¹æ³ãšããŠã¯ãAPIã®ãããªé
ããªãœãŒã¹ãã¹ã¿ãåããã¬ã¹ãã³ã¹ã®ã¿ã€ãã³ã°ã決å®è«çã«ãªã£ããšãã«ã³ã³ããŒãã³ããæç€ºçã«åã¬ã³ããªã³ã°ããããšããããŸããå€éšã³ã³ããŒãã³ããã¹ãªãŒãããå Žåãæèšãæ¥ãããããšãæçšãªå ŽåããããŸããã¹ãªãŒãã¯ãã¹ããé
ããããïŒçãæéãåŸ
ã€ãšãã®å ŽåïŒå±éºã«ãããããããé¿ããã¹ããã¿ãŒã³ã§ããã¹ãªãŒããããŒãªã³ã°ãé¿ããããããã¹ããã¬ãŒã ã¯ãŒã¯ã®ãµããŒãããªãå Žåã¯ãwait-for-expectã®ãããªnpmã©ã€ãã©ãªãåæ±ºå®è«çãªè§£æ±ºçãæäŸããã®ã«åœ¹ç«ã€ããšããããŸãã
â ãããªãã°: é·æéã¹ãªãŒãããå Žåããã¹ãã¯æ¡éãã«é ããªããŸããå°ããªæ°ã§ã¹ãªãŒãããããšãããšããã¹ã察象ã®ãŠããããã¿ã€ã ãªãŒã«åå¿ããªãã£ãå Žåã«ãã¹ãã倱æããŸããã€ãŸãããã¬ãŒã¯æ§ãšæªãããã©ãŒãã³ã¹éã®ãã¬ãŒããªãã«åž°çµããŸãã
â ã³ãŒãäŸ
// Cypressã䜿çš
cy.get("#show-products").click(); // ããã²ãŒã
cy.wait("@products"); // ã«ãŒãã衚瀺ãããã®ãåŸ
ã€
// ãã®è¡ã¯ã«ãŒããæºåå®äºã®ãšãã«ã®ã¿å®è¡ãããŸã
// @testing-library/dom
test("æ ç»ã¿ã€ãã«ã衚瀺ããã", async () => {
// èŠçŽ ã¯åæç¶æ
ã§ã¯ååšããªã...
// 衚瀺ãåŸ
ã€
await wait(() => {
expect(getByText("the lion king")).toBeInTheDocument();
});
//衚瀺ãåŸ
ã£ãŠèŠçŽ ãè¿ã
const movie = await waitForElement(() => getByText("the lion king"));
});
test("æ ç»ã¿ã€ãã«ã衚瀺ããã", async () => {
// èŠçŽ ã¯åæç¶æ
ã§ã¯ååšããªã...
// ç¬èªã®åŸ
æ©ããžãã¯ïŒæ³šæ: åçŽãããã¿ã€ã ã¢ãŠããªãïŒ
const interval = setInterval(() => {
const found = getByText("the lion king");
if (found) {
clearInterval(interval);
expect(getByText("the lion king")).toBeInTheDocument();
}
}, 100);
// åºçŸãåŸ
ã£ãŠèŠçŽ ãè¿ã
const movie = await waitForElement(() => getByText("the lion king"));
});
⪠ïž3.5 ã³ã³ãã³ãããããã¯ãŒã¯ãä»ããŠã©ã®ããã«é ä¿¡ããããã芳å¯ãã
â ããããŸããã: å®éã®ãããã¯ãŒã¯äžã§ã®ããŒãžããŒããæé©åãããŠããããšãä¿èšŒããã¢ã¯ãã£ãã¢ãã¿ãŒãé©çšããŸããããã«ã¯ãããŒãžããŒããé ãã£ãããããã¡ã€ãããŠããªããã³ãã«ãªã©ãUXã«é¢ããæžå¿µãå«ãŸããŸããã€ã³ã¹ãã¯ã·ã§ã³ããŒã«ã®åžå Žã¯è±å¯ã§ãpingdomãAWS CloudWatchãgcp StackDriverãªã©ã®åºæ¬çãªããŒã«ã¯ããµãŒããŒãåç¶ããåççãªSLAã®äžã§å¿çãããã©ãããç£èŠããããã«ç°¡åã«èšå®ã§ããŸããããã ãã§ã¯ééããçºçããå¯èœæ§ãããå€ãã®ç¶æ³ãç¶²çŸ ã§ããªããããããã³ããšã³ãã«ç¹åãããããªãããªåæãè¡ãããŒã«ãéžã¶ããšãæãŸããã§ãïŒäŸ: lighthouseãpagespeedïŒãçŠç¹ã¯UXã«çŽæ¥åœ±é¿ãäžããçç¶ãææšãäŸãã°ããŒãžããŒãæéãæå³ã®ãããã€ã³ããããŒãžã®å¯Ÿè©±å¯èœã«ãªããŸã§ã®æé(TTI)ãªã©ã«çœ®ãã¹ãã§ããå ããŠãã³ã³ãã³ããå§çž®ãããŠããããæåã®ãã€ããŸã§ã®æéãç»åã®æé©åãåççãªDOMãµã€ãºã®ç¢ºä¿ãSSLãªã©ã®æè¡çãªåå ãç£èŠããããšãéèŠã§ãããããã®ãªãããªã¢ãã¿ãŒãéçºäžãCIã®äžéšãšããŠããããŠæãéèŠãªã®ã¯ããããã¯ã·ã§ã³ã®ãµãŒããŒ/CDNã24æé365æ¥ç£èŠããããšãæšå¥šãããŸãã
â ãããªãã°: UIã®å¶äœã«å€å€§ãªæ³šæãæãã100%ã®æ©èœãã¹ããåæ ŒããæŽç·Žããããã³ãã«ãè¡ã£ãã«ãããããããCDNã®èª€èšå®ã«ããUXãã²ã©ãé ããšæ°ä»ãã®ã¯å€±æã§ãã
â ããããŸããã: ã¡ã€ã³ã¹ããªãŒã ã®ãã¹ãïŒE2Eãã¹ãã§ã¯ãªãïŒãã³ãŒãã£ã³ã°ããéã«ã¯ãããã¯ãšã³ãAPIã®ããã«è²¬ä»»ãšå¶åŸ¡ãè¶ ãããªãœãŒã¹ãé¢äžãããã®ã¯é¿ãã代ããã«ã¹ã¿ãïŒã€ãŸããã¹ãããã«ïŒã䜿çšããŸããå®éã«ã¯ãAPIãžã®å®éã®ãããã¯ãŒã¯åŒã³åºãã®ä»£ããã«ãSinonãTest doublesã®ãããªãã¹ãããã«ã©ã€ãã©ãªã䜿ã£ãŠAPIã®ã¬ã¹ãã³ã¹ãã¹ã¿ãåããŸããäž»ãªå©ç¹ã¯ãã¬ãŒã¯æ§ãé²ãããšã§ãããã¹ããã¹ããŒãžã³ã°APIã¯é«ãå®å®æ§ããªããããæã ãã³ã³ããŒãã³ãããã£ããåäœããŠããã«ããããããããã¹ãã倱æããããšããããŸãïŒãããã¯ã·ã§ã³ç°å¢ã¯ãã¹ãç®çã§èšèšãããŠããããéåžžãªã¯ãšã¹ããå¶éããŠããŸãïŒããããè¡ãããšã§ãããŒã¿ãèŠã€ãããªãã£ãå ŽåãAPIããšã©ãŒãæããå Žåã®ãããªãã³ã³ããŒãã³ãã®åäœãé§åããã¹ãæ§ã ãªAPIã®åäœãã·ãã¥ã¬ãŒãã§ããŸããå ããŠããããã¯ãŒã¯åŒã³åºãã¯ãã¹ããå€§å¹ ã«é ãããŸãã
â ãããªãã°: å¹³åçãªãã¹ãã¯æ°ããªç§ä»¥å ã«çµäºããŸãããå žåçãªAPIåŒã³åºãã¯100ms以äžã§ããããã«ãããåãã¹ãã¯çŽ20åé ããªããŸãã
â ã³ãŒãäŸ
// ãã¹ã察象ã®ãŠããã
export default function ProductsList() {
const [products, setProducts] = useState(false);
const fetchProducts = async () => {
const products = await axios.get("api/products");
setProducts(products);
};
useEffect(() => {
fetchProducts();
}, []);
return products ? <div>{products}</div> : <div data-test-id="no-products-message">No products</div>;
}
// ãã¹ã
test("ååãååšããªãå Žåãé©åãªã¡ãã»ãŒãžã衚瀺ãã", () => {
// Arrange
nock("api")
.get(`/products`)
.reply(404);
// Act
const { getByTestId } = render(<ProductsList />);
// Assert
expect(getByTestId("no-products-message")).toBeTruthy();
});
â ããããŸããã: ãšã³ãããŒãšã³ãïŒE2EïŒã¯éåžžãå®éã®ãã©ãŠã¶ã䜿ã£ãUIã®ã¿ã®ãã¹ããæå³ããŸããïŒ3.6ãåç §ïŒãä»ã®äººã ã«ãšã£ãŠã¯å®éã®ããã¯ãšã³ããå«ãã·ã¹ãã å šäœã«åã¶ãã¹ããæå³ããŸããåŸè ã®ã¿ã€ãã®ãã¹ãã¯éåžžã«äŸ¡å€ããããããã³ããšã³ããšããã¯ãšã³ãã®çµ±åãã°ãã«ããŒããŠãããããããŒã¿ã¹ããŒãã®èª€è§£ã«ãã£ãŠèµ·ããå¯èœæ§ã®ããåé¡ãé²ããŸãããŸããããã¯ãšã³ãéã®çµ±ååé¡ãçºèŠããå¹ççãªæ¹æ³ã§ããïŒäŸ: ãã€ã¯ããµãŒãã¹Aããã€ã¯ããµãŒãã¹Bã«èª€ã£ãã¡ãã»ãŒãžãéä¿¡ããïŒããããã€ã¡ã³ãã®å€±æãæ€åºããããã«ãå©çšã§ããŸããUIãã¬ãŒã ã¯ãŒã¯ã®CypressãPuppeteerã®ããã«ãããã¯ãšã³ãã®E2Eãã¹ãã®ããã®èŠªãã¿ãããæçãããã¬ãŒã ã¯ãŒã¯ã¯ååšããŸããããã®ãããªãã¹ãã®æ¬ ç¹ã¯ãå€ãã®ã³ã³ããŒãã³ããæã€ç°å¢ã®æ§æã«é«ã³ã¹ãããããããšããããŠå€ãã®å Žåãã®èãã§ãã50ã®ãã€ã¯ããµãŒãã¹ã®ãã¡1ã€ã§ã倱æããã°ãE2Eå šäœã倱æããŠããŸããŸãããã®ããããã®ææ³ã¯æ§ããã«äœ¿çšãã1ã10çš®é¡çšåºŠã«çããã¹ãã§ããããã§ããå°æ°ã®E2Eãã¹ãã§ãã£ãŠãããããã€ã¡ã³ããšçµ±åã®äžå ·åãæ€åºããèœåã¯é«ãã§ãããããã¯ã·ã§ã³ã«è¿ãã¹ããŒãžã³ã°ç°å¢ã§ããããå®è¡ããããšãæšå¥šãããŸãã
â ãããªãã°: UIãèªæ©èœã®ãã¹ãã«å€å€§ãªæè³ãããŠããããã¯ãšã³ããè¿ãããã€ããŒãïŒUIãæ±ãã¹ãããŒã¿ã¹ããŒãïŒãæåŸ ãšã¯å€§ããç°ãªãããšã«æ°ä»ãã®ãéåžžã«é ãããããããŸããã
â ããããŸããã: å®éã®ããã¯ãšã³ããå«ãE2Eãã¹ãã§ãAPIåŒã³åºãã«æå¹ãªãŠãŒã¶ãŒããŒã¯ã³ãå¿ èŠãªå Žåãæ¯åãŠãŒã¶ãŒãäœæããŠãã°ã€ã³ããããããªã¬ãã«ãŸã§ãã¹ããåé¢ããã®ã¯åŸçã§ã¯ãããŸããã代ããã«ããã¹ãå®è¡éå§åã«äžåºŠã ããã°ã€ã³ãïŒbefore-allããã¯ã§ïŒãããŒã¯ã³ãããŒã«ã«ã¹ãã¬ãŒãžã«ä¿åãããªã¯ãšã¹ãéã§åå©çšããŸããããã¯ãã¹ãã®åºæ¬ååã®1ã€ããªãœãŒã¹ã®çµåãªãã«ãã¹ããèªåŸçã«ä¿ã€ãšãããã®ãéåããŠããããã«èŠããŸãããE2Eãã¹ãã§ã¯æ§èœãéèŠã§ãããåã ã®ãã¹ããéå§ããåã«1ã3ã®APIãªã¯ãšã¹ããçæããããšã¯ãå®è¡æéã«å€§ããªæªåœ±é¿ãäžãããããããŸãããè³æ Œæ å ±ãåå©çšããããšã¯ããã¹ããåããŠãŒã¶ãŒã¬ã³ãŒãã§åäœããããšãæå³ããŸããããŠãŒã¶ãŒã¬ã³ãŒãã«äŸåããïŒäŸ: ãŠãŒã¶ãŒã®æ¯æãå±¥æŽããã¹ãïŒå Žåã¯ããããã®ã¬ã³ãŒãããã¹ãã®äžéšãšããŠçæããä»ã®ãã¹ããšå ±æããªãããã«ããŠãã ããããŸããããã¯ãšã³ããåœè£ ã§ããããšãå¿ããªãã§ãã ããããã¹ããããã³ããšã³ãã«éäžããŠããå Žåã¯ããããåé¢ããŠããã¯ãšã³ãAPIã®ã¹ã¿ãåãæ€èšããæ¹ãè¯ãå ŽåããããŸãïŒ3.6ç¯ãåç §ïŒã
â ãããªãã°: 200ã®ãã¹ãã±ãŒã¹ãããããã°ã€ã³ã«100msããããšä»®å®ãããšããã°ã€ã³ãç¹°ãè¿ãã ãã§åèš20ç§ããããŸãã
â ã³ãŒãäŸ
let authenticationToken;
// ãã¹ãŠã®ãã¹ããå®è¡ãããåã«è¡ããã
before(() => {
cy.request('POST', 'http://localhost:3000/login', {
username: Cypress.env('username'),
password: Cypress.env('password'),
})
.its('body')
.then((responseFromLogin) => {
authenticationToken = responseFromLogin.token;
})
})
// åãã¹ããå®è¡ãããåã«è¡ããã
beforeEach(setUser => () {
cy.visit('/home', {
onBeforeLoad (win) {
win.localStorage.setItem('token', JSON.stringify(authenticationToken))
},
})
})
â ããããŸããã: æ¬çªã¢ãã¿ãªã³ã°ãéçºæã®æ£åžžæ§ãã§ãã¯ã®ããã«ããã¹ãŠã®ããŒãžããŸãã¯å€§éšåã®ãµã€ãããŒãžã蚪ããäœãå£ããŠããªãããšã確èªããåäžã®E2Eãã¹ããå®è¡ããŸãããã®ã¿ã€ãã®ãã¹ãã¯ãå·çãšä¿å®ãéåžžã«ç°¡åã§ãããªãããæ©èœçããããã¯ãŒã¯çãããã³ãããã€ã¡ã³ãã®åé¡ãå«ãããããçš®é¡ã®éå®³ãæ€åºã§ãããããéåžžã«é«ãæè³å¯Ÿå¹æããããããŸããä»ã®ã¹ã¢ãŒã¯ããã³æ£åžžæ§ãã§ãã¯ã®ã¹ã¿ã€ã«ã¯ä¿¡é Œæ§ãç¶²çŸ æ§ãå£ããŸãããªãã¬ãŒã·ã§ã³ããŒã ã®äžéšã¯ããŒã ããŒãžã ããpingããïŒæ¬çªç°å¢ïŒããŸãã¯å€ãã®çµ±åãã¹ããå®è¡ããéçºè ã¯ãããã±ãŒãžã³ã°ããã©ãŠã¶ã®åé¡ãçºèŠããªãããšããããŸãããã¡ãããã¹ã¢ãŒã¯ãã¹ãã¯æ©èœãã¹ãã®ä»£æ¿ã§ã¯ãªããåã«çŽ æ©ãå顿€åºãç®æããã®ã§ãã
â ãããªãã°: ãã¹ãŠãå®ç§ã«èŠãããããããŸããããã¹ãŠã®ãã¹ããééããæ¬çªã®ãã«ã¹ãã§ãã¯ãè¯å¥œã§ãããPaymentã³ã³ããŒãã³ãã«ã¯ããã±ãŒãžã³ã°ã®åé¡ãããã/Paymentã«ãŒãã ããã¬ã³ããªã³ã°ãããªãç¶æ³ããããŸãã
â ã³ãŒãäŸ
it("å
šããŒãžã§ã¹ã¢ãŒã¯ãã¹ããè¡ããšãããã¹ãŠæ£åžžã«ããŒããããã¹ãã§ãã", () => {
// Cypressã䜿ã£ãŠç€ºããŠããŸãããä»»æã®E2Eã¹ã€ãŒãã§ç°¡åã«å®è£
ã§ããŸã
cy.visit("https://mysite.com/home");
cy.contains("Home");
cy.contains("https://mysite.com/Login");
cy.contains("Login");
cy.contains("https://mysite.com/About");
cy.contains("About");
});
â ããããŸããã: ã¢ããªã®ä¿¡é Œæ§ãåäžãããã ãã§ãªãããã¹ãã¯ãã1ã€ã®é åçãªæ©äŒãæäŸããŸããããã¯ãã©ã€ãã®ã¢ããªããã¥ã¡ã³ããšããŠæ©èœããããšã§ãããã¹ãã¯æ¬è³ªçã«ãæè¡çã§ãªã補å/UXã®èšèªã§è¡šçŸããããããé©åãªããŒã«ã䜿çšããããšã§ãéçºè ãšé¡§å®¢ãã¹ãŠã®ä»²éã倧ããäžèŽãããã³ãã¥ãã±ãŒã·ã§ã³ã¢ãŒãã£ãã¡ã¯ããšããŠåœ¹ç«ã€ããšãã§ããŸããäŸãã°ãããã€ãã®ãã¬ãŒã ã¯ãŒã¯ã§ã¯ã人éãèªã¿ãããèšèªã䜿ã£ãŠãããŒãšæåŸ å€ïŒã€ãŸããã¹ãèšç»ïŒã衚çŸã§ãã補å管çè ãå«ãããããã¹ããŒã¯ãã«ããŒããã¹ããèªã¿ãæ¿èªããååã§ããã®ã§ããã¹ãã¯ã©ã€ãã®èŠä»¶ããã¥ã¡ã³ãã«ãªããŸãããã®ææ³ã¯ãåå ¥ãã¹ãããšãåŒã°ãã顧客ãå¹³æãªèšèã§åãå ¥ãåºæºãå®çŸ©ã§ããããã«ããŸããããã¯BDDïŒæ¯ãèãé§åéçºïŒã®æãçŽç²ãªåœ¢ã§ãããããå¯èœã«ãã人æ°ã®ãã¬ãŒã ã¯ãŒã¯ã®1ã€ãCucumberïŒJavaScriptçšã®ããŒãžã§ã³ããïŒã§ãã以äžã®äŸãã芧ãã ããããã1ã€ã®é¢é£ããæ©äŒãšããŠãStoryBookã¯ãUIã³ã³ããŒãã³ããã°ã©ãã£ãã¯ã«ã¿ãã°ãšããŠå ¬éããããããã®ã³ã³ããŒãã³ãã®æ§ã ãªç¶æ ããã©ããªããããã®èŠãç®ããã®ç¶æ ãããªã¬ãŒããæ¹æ³ãæç€ºããããšãã§ããŸããããã«ããã補åã®æ åœè ã«ãã¢ããŒã«ããããšãã§ããäž»ã«ãã®ã³ã³ããŒãã³ããæ¶è²»ããéçºè ã®ããã®ã©ã€ãããã¥ã¡ã³ããšããŠåœ¹ç«ã¡ãŸãã
â ãããªãã°: ãã¹ãã«ããããªãœãŒã¹ãæè³ããåŸããã®æè³ã掻çšããŠå€§ããªäŸ¡å€ãåŸãããªãã®ã¯ããã ã®æ²åã§ãã
â ã³ãŒãäŸ
// collaborateã䜿ã£ããã¹ãã¯ããã®ããã«èª¬æããããšãã§ããã
Feature: Twitter new tweet
I want to tweet something in Twitter
@focus
Scenario: Tweeting from the home page
Given I open Twitter home
Given I click on "New tweet" button
Given I type "Hello followers!" in the textbox
Given I click on "Submit" button
Then I see message "Tweet saved"
â ããããŸããã: 倿Žãããå Žåã«UIã®ã¹ã¯ãªãŒã³ã·ã§ããããã£ããã£ããã³ã³ãã³ãã®éãªãã厩ããšãã£ãèŠèŠçãªåé¡ãæ€åºããããã®èªååããŒã«ãã»ããã¢ããããŸããããããã«ãããæ£ããããŒã¿ãçšæãããã ãã§ãªãããŠãŒã¶ãŒãããã䟿å©ã«èŠãããšãã§ããããã«ãªããŸãããã®ææ³ã¯åºãæ¡çšãããŠããããã§ã¯ãããŸããããç§ãã¡ã®ãã¹ãã®æèæ³ã¯æ©èœãã¹ãã«åŸããŠããã®ã«å¯ŸããèŠèŠçãªèŠçŽ ããŠãŒã¶ãŒäœéšã§ãããæ§ã ãªããã€ã¹ã¿ã€ãã®ååšã«ããããã€ãã®åä»ãªUIãã°ãèŠéããã®ã¯éåžžã«ç°¡åã§ããç¡æã®ããŒã«ã§ãåºæ¬ãæäŸã§ããŸããã€ãŸãã人éã®ç®ã«ããæ€æ»ã®ããã«ã¹ã¯ãªãŒã³ã·ã§ãããçæã»ä¿åãããšããããšã§ãããã®ã¢ãããŒãã¯å°èŠæš¡ãªã¢ããªã«ã¯ååãããããŸãããã人éã®åŽåãèŠæ±ããããããæåãã¹ããšåæ§ã«æ¬ ç¹ããããŸããäžæ¹ã§ãUIã®åé¡ãèªåçã«æ€åºããã®ã¯éåžžã«é£ããããããã®åéã§ã¯'Visual Regression'ãç»å ŽããŸããããã¯ãå€ãUIãšææ°ã®å€æŽãæ¯èŒããŠéããæ€åºããããšã§ãã®ããºã«ã解決ããŸããããã€ãã®OSS/ç¡æããŒã«ãåºæ¬çãªæ©èœãæäŸããããšãã§ããŸãïŒäŸ: wraith, PhantomCSSïŒããã»ããã¢ããã«ããªãã®æéãããããããããŸãããåçšã®ããŒã«ïŒäŸ: Applitools, Percy.ioïŒã¯ããã«äžæ©é²ãã§ãã€ã³ã¹ããŒã«ãã¹ã ãŒãºã«ãã管çUIãã¢ã©ãŒãã'èŠèŠçãã€ãº'ïŒäŸ: åºåãã¢ãã¡ãŒã·ã§ã³ïŒãæé€ããã¹ããŒããã£ããã£ãŒãåé¡ãåŒãèµ·ãããDOM/CSSã®å€æŽã®æ ¹æ¬åå åæãšãã£ãé«åºŠãªæ©èœãæèŒããŠããŸãã
â ãããªãã°: çŽ æŽãããã³ã³ãã³ãã衚瀺ãïŒ100%ã®ãã¹ããåæ ŒïŒãç¬æã«ããŒããããããã³ã³ãã³ããšãªã¢ã®ååãé ããŠããã³ã³ãã³ãããŒãžã¯ã©ãã»ã©è¯ãã§ããããïŒ
â ã³ãŒãäŸ
â# å¿
èŠãªã ããã¡ã€ã³ã远å ããŸããããŒãã©ãã«ãšããŠæ©èœããŸãâ
domains:
english: "http://www.mysite.com"â
â# 以äžã«ã¹ã¯ãªãŒã³ã®å¹
ãå
¥åããŸããããã€ãã®äŸã瀺ããŸãâ
screen_widths:
- 600â
- 768â
- 1024â
- 1280â
â# 以äžã«ããŒãžã®URLãã¹ãå
¥åããŸããããã€ãã®äŸã瀺ããŸãâ
paths:
about:
path: /about
selector: '.about'â
subscribe:
path: /subscribe
selector: '.subscribe'â
ð æ£ããäŸ: Applitoolsã䜿çšããŠã¹ãããã·ã§ããã®æ¯èŒããã®ä»ã®é«åºŠãªæ©èœãå©çšãã
import * as todoPage from "../page-objects/todo-page";
describe("visual validation", () => {
before(() => todoPage.navigate());
beforeEach(() => cy.eyesOpen({ appName: "TAU TodoMVC" }));
afterEach(() => cy.eyesClose());
it("should look good", () => {
cy.eyesCheckWindow("empty todo list"); // 空ã®ToDoãªã¹ãã確èª
todoPage.addTodo("Clean room");
todoPage.addTodo("Learn javascript");
cy.eyesCheckWindow("two todos"); // 2ã€ã®ToDoã確èª
todoPage.toggleTodo(0);
cy.eyesCheckWindow("mark as completed"); // å®äºãšããŠããŒã¯ããã§ãã¯
});
});
⪠ïž4.1 ååãªã«ãã¬ããžã確ä¿ããŠèªä¿¡ãæã€ãã80%ã幞éãªæ°åã®ãã
â ããããŸããã: ãã¹ãã®ç®çã¯éãåãããã®ååãªèªä¿¡ãåŸãããšã§ãããã¡ãããããå€ãã®ã³ãŒãããã¹ããããã°ããŒã ã¯ããèªä¿¡ãæãŠãŸããã«ãã¬ããžã¯ããã¹ãã«ãã£ãŠã©ãã ãã®ã³ãŒãè¡ïŒããã³ãã©ã³ããã¹ããŒãã¡ã³ããªã©ïŒãå°éãããŠããããæž¬ãææšã§ããã§ã¯ãã©ã®çšåºŠãååã§ããããïŒ10â30%ã¯ãã«ãã®æ£ç¢ºæ§ã«ã€ããŠå€æããã«ã¯æããã«äœãããéã«100%ã¯éåžžã«é«äŸ¡ã§ãé倧ãªçµè·¯ããã³ãŒãã®çããé ã«é¢å¿ãç§»ãå¯èœæ§ããããŸããé·ãçããšããŠã¯ãå€ãã®èŠå ãäŸãã°ã¢ããªã±ãŒã·ã§ã³ã®çš®é¡ã«ãããŸããæ¬¡äžä»£ã®Airbus A380ãæ§ç¯ãããªã100%ã¯å¿ é ã§ãããæŒ«ç»ã®ç»åãµã€ãã§ã¯50%ã§ããå€ããããããããŸãããã»ãšãã©ã®ãã¹ãæå¥œå®¶ã¯ãé©åãªã«ãã¬ããžéŸå€ãã³ã³ããã¹ãã«äŸåãããšäž»åŒµããŸãããå€ãã®äººã80%ãšããæ°å€ãäžè¬çãªã«ãŒã«ãšããŠèšåããŠããŸãïŒFowler: "äžäœã®80ãŸãã¯90%"ïŒãããã¯ããããã»ãšãã©ã®ã¢ããªã±ãŒã·ã§ã³ãæºè¶³ãããã§ãããã
å®è£ ã®ãã³ã: ç¶ç¶çã€ã³ãã°ã¬ãŒã·ã§ã³ïŒCIïŒã«ã«ãã¬ããžéŸå€ãèšå®ããããã«æºæ ããªããã«ãã忢ããããã«èšå®ãããããããŸããïŒJestãªã³ã¯ïŒãã³ã³ããŒãã³ãããšã«éŸå€ãèšå®ããããšãå¯èœã§ãïŒäžèšã®ã³ãŒãäŸãåç §ïŒãå ããŠãæ°ããã³ããããããã³ãŒããã«ãã¬ããžãæžå°ããŠããå Žåã®æ€åºãæ€èšããŠãã ãããããã«ããéçºè ã¯ãã¹ãã³ãŒãã®éãå¢ããããå°ãªããšãç¶æããããä¿ãããŸãããšã¯èšããã«ãã¬ããžã¯äžã€ã®ææšã§ãããå®éçã«åºã¥ãããã®ã§ããã¹ãã®å ç¢æ§ãäŒããã«ã¯äžååã§ãããããŠã次ã®ç®æ¡æžãã§ç€ºãããã«ã誀éåãããããšããããŸãã
â ãããªãã°: èªä¿¡ãšæ°åã¯å¯æ¥ãªé¢ä¿ããããã·ã¹ãã ã®ã»ãšãã©ããã¹ããããšæ¬åœã«ç¥ãããšããªããã°ãææãçãŸãããã®ææã¯ããªããé ãããŸãã
â ã³ãŒãäŸ
⪠ïž4.2 ã«ãã¬ããžã¬ããŒããæ€æ»ããŠæªãã¹ãã®é åããã®ä»ã®ç°åžžãæ€åºãã
â ããããŸããã: äžéšã®åé¡ã¯èŠèœãšãããäŒçµ±çãªããŒã«ã䜿ã£ãŠèŠã€ããã®ãéåžžã«é£ããã§ãããããã¯æ¬åœã«ãã°ã§ã¯ãªããé倧ãªåœ±é¿ãåãŒãå¯èœæ§ã®ããé©ãã¹ãã¢ããªã±ãŒã·ã§ã³ã®æåã§ããäŸãã°ãããã³ãŒããšãªã¢ããŸã£ããåŒã³åºãããªãããã»ãšãã©åŒã³åºãããªãããšããããŸãââââPricingCalculatorâã¯ã©ã¹ãåžžã«è£œåäŸ¡æ Œãèšå®ããŠãããšæã£ãŠããã®ã«ãå®éã«ã¯äžåºŠãåŒã³åºãããŠããªãããšããããšããããŸããDBã«ã¯10000ã®è£œåããããå€ãã®è²©å£²ãããã®ã«âŠ ã³ãŒãã«ãã¬ããžã¬ããŒãã¯ã¢ããªã±ãŒã·ã§ã³ãããªãã®æãéãã«åäœããŠãããã©ãããçè§£ããã®ã«åœ¹ç«ã¡ãŸãããã以å€ã«ããã©ã®çš®é¡ã®ã³ãŒãããã¹ããããŠããªããã匷調衚瀺ããããšãã§ããŸãâââã³ãŒãã®80%ããã¹ãããããšããæ å ±ã§ã¯ãéèŠãªéšåãã«ããŒãããŠãããã¯ããããŸãããã¬ããŒãã®çæã¯ç°¡åã§ãâââã«ãã¬ããžè¿œè·¡ãä»ããŠæ¬çªç°å¢ãŸãã¯ãã¹ãäžã«ã¢ããªãå®è¡ããåã³ãŒããšãªã¢ãã©ã®ãããã®é »åºŠã§åŒã³åºããããã匷調ããã«ã©ãã«ãªã¬ããŒãã確èªããŸããããã«ç®ãéãæéãåãã°ãããã€ãã®éèŠãªçºèŠããããããããŸããã
â ãããªãã°: ã³ãŒãã®ã©ã®éšåããã¹ããããŠããªããããããªããã°ãåé¡ãã©ãããæ¥ãå¯èœæ§ããããããããŸããã
â ã³ãŒãäŸ
ð ã¢ã³ããã¿ãŒã³äŸ: ãã®ã«ãã¬ããžã¬ããŒãã®äœãåé¡ãªã®ã§ããããïŒ
å®éã®äºäŸã«åºã¥ããQAã§ã¢ããªã±ãŒã·ã§ã³ã®äœ¿çšã远跡ããè峿·±ããã°ã€ã³ãã¿ãŒã³ãçºèŠããŸããïŒãã³ã: ãã°ã€ã³å€±æã®åæ°ãäžçžå¿ã§ãäœããæããã«ãããããæçµçã«ã¯ãããããã³ããšã³ãã®ãã°ãããã¯ãšã³ãã®ãã°ã€ã³APIãç¹°ãè¿ãå©ããŠããããšã倿ããŸããïŒ
â ããããŸããã: äŒçµ±çãªã«ãã¬ããžææšã¯ãã°ãã°èª€è§£ãæããŸãã100%ã®ã³ãŒãã«ãã¬ããžã瀺ããŠããŠãã颿°ãã²ãšã€ããæ£ããå¿çãè¿ããŠããªãå¯èœæ§ããããŸããã©ãããŠã§ãããïŒããã¯ãã¹ããã©ã®ã³ãŒãè¡ã蚪ããããæž¬å®ããã ãã§ãå®éã«ãã¹ããäœããæ€èšŒãããã©ããâââæ£ããå¿çã確èªãããã確èªããŠããªãããã§ããããžãã¹ã§æ è¡ããŠãã¹ããŒãã®ã¹ã¿ã³ããèŠãã人ã®ããã«âââããã¯ã©ããªä»äºããããã蚌æããŠããããã§ã¯ãªããåã«ããã€ãã®ç©ºæž¯ãããã«ã蚪ããããšã瀺ããŠããã«éããŸããã
ãã¥ãŒããŒã·ã§ã³ããŒã¹ã®ãã¹ãã¯ãåã«èšªåããã ãã§ãªããå®éã«ãã¹ããããã³ãŒãã®éãæž¬å®ããããšã«ãã£ãŠå©ãã«ãªããŸããStrykerã¯JavaScriptã®ãã¥ãŒããŒã·ã§ã³ãã¹ãçšã©ã€ãã©ãªã§ãå®è£ ã¯éåžžã«ã¹ããŒãã§ã:
(1) ã³ãŒããæå³çã«å€æŽããããã°ãæ€ãä»ããããäŸãã°ãã³ãŒã newOrder.price===0
ã newOrder.price!=0
ã«å€ããããã®ããã°ããå€ç°(mutations)ãšåŒã³ãŸãã
(2) ãã¹ããå®è¡ãããã¹ãŠæåãããåé¡ããããŸãâââãã¹ãã¯ãã°ãçºèŠãããšããç®çãæãããªãã£ãããšãæå³ãããã®å€ç°ã¯çãæ®ã£ãããšã«ãªããŸãããã¹ãã倱æãããªãçŽ æŽããããå€ç°ã¯é§éãããŸããã
ãã¹ãŠãŸãã¯ã»ãšãã©ã®ãã¥ãŒããŒã·ã§ã³ãé§éãããããšãç¥ã£ãŠããããšã¯ãäŒçµ±çãªã«ãã¬ããžãããã¯ããã«é«ãä¿¡é Œãäžããã»ããã¢ããæéã¯åæ§ã§ãã
â ãããªãã°: 85%ã®ã«ãã¬ããžããããªãã®ãã¹ããã³ãŒãã®85%ã§ãã°ãæ€åºããããšãæå³ãããšæã蟌ãŸãããŸãã
â ã³ãŒãäŸ
function addNewOrder(newOrder) {
logger.log(`Adding new order ${newOrder}`);
DB.save(newOrder);
Mailer.sendMail(newOrder.assignee, `A new order was placed ${newOrder}`);
return { approved: true };
}
it("Test addNewOrder, don't use such test names", () => {
addNewOrder({ assignee: "[email protected]", price: 120 });
}); //100%ã®ã³ãŒãã«ãã¬ããžãåŒãèµ·ãããŸãããäœããã§ãã¯ããŠããŸãã
â ããããŸããã: ESLintãã©ã°ã€ã³ã®ã»ããã¯ããã¹ãã³ãŒãã®ãã¿ãŒã³ãæ€æ»ããŠåé¡ãçºèŠããããã«ç¹å¥ã«æ§ç¯ãããŸãããäŸãã°ãeslint-plugin-mochaã¯ããã¹ããã°ããŒãã«ã¬ãã«ïŒdescribe()ã¹ããŒãã¡ã³ãã®åã§ã¯ãªãïŒã§æžãããŠããå Žåãããã¹ããã¹ããããããŠããå Žåã«èŠåãåºããŸããããã«ããããã¹ãŠã®ãã¹ããéã£ãŠãããšãã誀ã£ãèªèãçããå¯èœæ§ããããŸããåæ§ã«ãeslint-plugin-jestã¯ããã¹ãã«å šãã¢ãµãŒã·ã§ã³ããªãå ŽåïŒäœããã§ãã¯ããŠããªãïŒã«èŠåãåºãããšãã§ããŸãã
â ãããªãã°: 90%ã®ã³ãŒãã«ãã¬ããžãš100%ã®ã°ãªãŒã³ãã¹ããèŠããšãé¡ã倧ããªç¬é¡ã«å ãŸããŸãããå€ãã®ãã¹ããäœãã¢ãµãŒã·ã§ã³ããŠããããå€ãã®ãã¹ãã¹ã€ãŒããã¹ããããããŠããããšã«æ°ä»ããŸã§ã®ããšã§ãã幞éãç¥ãããšã«ãããªãããã®èª€ã£ãèªèã«åºã¥ããŠäœããããã€ããŠããªãã£ãããšãã
â ã³ãŒãäŸ
ð ã¢ã³ããã¿ãŒã³äŸ: ãšã©ãŒã ããã®ãã¹ãã±ãŒã¹ã幞ãã«ããã¹ãŠãªã³ã¿ãŒãæ€åº
describe("Too short description", () => {
const userToken = userService.getDefaultToken() // *ãšã©ãŒ:no-setup-in-describeã代ããã«ããã¯ãïŒæ
éã«ïŒäœ¿çšããŠãã ãã
it("Some description", () => {});//* ãšã©ãŒ: valid-test-description. "Should"ãšããåèªãå«ã + å°ãªããšã5èª
});
it.skip("Test name", () => {// *ãšã©ãŒ:no-skipped-tests, ãšã©ãŒ:error:no-global-tests. ãã¹ãã¯describeãŸãã¯ã¹ã€ãŒãã®äžã«ã®ã¿é
眮
expect("somevalue"); // ãšã©ãŒ:no-assert
});
it("Test name", () => {*//ãšã©ãŒ:no-identical-title. ãã¹ãã«ãŠããŒã¯ãªã¿ã€ãã«ãå²ãåœãŠãŠãã ãã
});
â
ããããŸããã: ãªã³ã¿ãŒã¯ããªãŒã©ã³ãã§ãã5åã®ã»ããã¢ããã§ãã³ãŒããå®ãèªåæçžŠè£
眮ãç¡æã§æã«å
¥ããããšãã§ããéèŠãªåé¡ããã£ããããããšãã§ããŸãã ãªã³ãã£ã³ã°ãè£
食ã®ããã®ãã®ã ã£ãæä»£ã¯ããçµãããŸãããçŸåšã§ã¯ãªã³ã¿ãŒã¯ãæ£ããã¹ããŒãããªãããšã«ãã£ãŠæ
å ±ã倱ãããŠããŸããšã©ãŒã®ãããªæ·±å»ãªåé¡ãæ€ç¥ããããšãã§ããŸãã ESLint standard ã Airbnb style ã®ãããªåºæ¬çãªã«ãŒã«ã»ããã«å ããeslint-plugin-chai-expect ã¯ã¢ãµãŒã·ã§ã³ã®ãªããã¹ãããeslint-plugin-promise 㯠resolve ããªã promise ããeslint-plugin-security 㯠DOS æ»æã«äœ¿ãããå¯èœæ§ã®ããæ£èŠè¡šçŸããeslint-plugin-you-dont-need-lodash-underscore 㯠Lodash ã®_.map(âŠ)
ãã㪠V8 ã³ã¢ã¡ãœããã®äžéšã§ãããŠãŒãã£ãªãã£ãŒã©ã€ãã©ãªã¡ãœãããã³ãŒãã䜿çšããŠããå Žåã«èŠåããããšãã§ããŸãã
â ãããªãã°: éšã®æ¥ã«ãæ¬çªç°å¢ãã¯ã©ãã·ã¥ãç¶ããŠããã®ã«ããã°ã«ã¯ãšã©ãŒã®ã¹ã¿ãã¯ãã¬ãŒã¹ã衚瀺ãããŠããªãå ŽåãèããŠã¿ãŸããããäœãèµ·ãã£ãã®ã§ããããïŒããªãã®ã³ãŒãã誀ã£ãŠãšã©ãŒã§ã¯ãªããªããžã§ã¯ããæããŠããŸããã¹ã¿ãã¯ãã¬ãŒã¹ã倱ãããã®ã§ãããããªããšãèµ·ãã£ãæ¥ã«ã¯ãå£ã«é ãæã¡ä»ããããªããŸãããã5åéã®ãªã³ã¿ãŒã®ã»ããã¢ããã§ãã®ã¿ã€ããæ€åºããããªãã®äžæ¥ãæãããšãã§ããŸãã
â ã³ãŒãäŸ
â ããããŸããã: ãã¹ãããªã³ãã£ã³ã°ãè匱æ§ãã§ãã¯ãªã©ã®åè³ªæ€æ»ããã«ã€ãã®CIã䜿ã£ãŠããŸããïŒ éçºè ããã€ãã©ã€ã³ãããŒã«ã«ã§å®è¡ãå³åº§ã«ãã£ãŒãããã¯ãåŸãããããã«ããŠããã£ãŒãããã¯ã«ãŒã ãçãããŸãããããªããïŒ å¹ççãªãã¹ãããã»ã¹ã¯ãå€ãã®å埩çãªã«ãŒããæ§æããŠããããã§ãã(1)ãã©ã€ã¢ãŠã -> (2)ãã£ãŒããã㯠-> (3)ãªãã¡ã¯ã¿ãªã³ã°ããã£ãŒãããã¯ãæ©ããã°æ©ãã»ã©ãéçºè ã¯ã¢ãžã¥ãŒã«ããšã«æ¹åã®ååŸ©åæ°ãå¢ããçµæãå®ç§ã«ããããšãã§ããŸããéã«ããã£ãŒãããã¯ãé ããªããšã1æ¥ã«ã§ããæ¹åã®ååŸ©åæ°ãå°ãªããªããããŒã ã¯ãã§ã«å¥ã®ãããã¯/ã¿ã¹ã¯/ã¢ãžã¥ãŒã«ã«é²ãã§ããŸãããã®ã¢ãžã¥ãŒã«ãæ¹åããæ°ã«ãªããªããããããŸããã
å®éã«ãããã€ãã®CIãã³ããŒïŒäŸïŒCircleCI local CLI) ã¯ããã€ãã©ã€ã³ãããŒã«ã«ã§å®è¡ããããšãã§ããŸããwallaby ã®ãããªããã€ãã®åçšããŒã«ã¯ãéçºè
ã®ãããã¿ã€ããšããŠéåžžã«äŸ¡å€ã®ãããã¹ãçšã®ã€ã³ãµã€ããæäŸããŠããŸãããŸãã¯ããã¹ãŠã®å質ãã§ãã¯ã®ã³ãã³ãïŒäŸïŒãã¹ãããªã³ããè匱æ§ãã§ãã¯ïŒãå®è¡ããnpmã¹ã¯ãªãããpackage.jsonã«è¿œå ããã ãã§ãæ§ããŸããã䞊ååã®ããã« concurrently ã®ãããªããŒã«ã䜿çšããããŒã«ã®1ã€ã倱æããå Žåã§ã0以å€ã®çµäºã³ãŒããè¿ãããã«ããŸããããéçºè
ã¯ãnpm run qualityããªã©ã®ã³ãã³ããå®è¡ããã ãã§ãå³åº§ã«ãã£ãŒãããã¯ãåŸãããšãã§ããŸããgithookã䜿ã£ãŠå質ãã§ãã¯ã«å€±æãããšãã«ã³ããããäžæ¢ããããšãæ€èšããŠã¿ãŸããã(husky ã䜿ããŸãïŒã
â ãããªãã°: å質ãã§ãã¯ã®çµæãã³ãŒãã®ç¿æ¥ã«åºãããã§ã¯ããã¹ãã¯éçºã®äžéšã§ã¯ãªããåŸä»ã®åœ¢åŒçãªææç©ã«ãªã£ãŠããŸããŸãã
â ã³ãŒãäŸ
ð æ£ããäŸ: ã³ãŒãå質ã®ãã§ãã¯ãè¡ãnpmã¹ã¯ãªããã¯ãæåãŸãã¯éçºè ãæ°ããã³ãŒããããã·ã¥ããããšããŠãããšãã«èªåã§ãã¹ãŠäžŠè¡ããŠå®è¡ãããŸãã
"scripts": {
"inspect:sanity-testing": "mocha **/**--test.js --grep \"sanity\"",
"inspect:lint": "eslint .",
"inspect:vulnerabilities": "npm audit",
"inspect:license": "license-checker --failOn GPLv2",
"inspect:complexity": "plato .",
"inspect:all": "concurrently -c \"bgBlue.bold,bgMagenta.bold,yellow\" \"npm:inspect:quick-testing\" \"npm:inspect:lint\" \"npm:inspect:vulnerabilities\" \"npm:inspect:license\""
},
"husky": {
"hooks": {
"precommit": "npm run inspect:all",
"prepush": "npm run inspect:all"
}
}
â
ããããŸããã: ãšã³ãããŒãšã³ã (e2e) ãã¹ãã£ã³ã°ã¯ããã¹ãŠã®CIãã€ãã©ã€ã³ã®äž»ãªèª²é¡ã§ã - æ¬çªç°å¢ãšåäžã®äžæçãªç°å¢ããé¢é£ãããã¹ãŠã®ã¯ã©ãŠãã»ãµãŒãã¹ãšäžç·ã«ãã®å Žã§äœæããã®ã¯é¢åã§ã³ã¹ããããããŸããæé©ãªåХ忡ãèŠã€ããã®ãããªãã®ä»äºã§ã: Docker-compose ã¯ã1ã€ã®ãã¬ãŒã³ãªããã¹ããã¡ã€ã«ã䜿çšããŠãåäžã®ã³ã³ããã§éé¢ãããdockerç°å¢ãäœãããšãã§ããŸãããè£åŽã®æè¡ïŒäŸ: ãããã¯ãŒã¯ããããã€ã¡ã³ãã¢ãã«ïŒã¯ãå®éã®æ¬çªç°å¢ãšã¯ç°ãªããŸããAWS Local
ãšçµã¿åãããããšã§ãå®éã®AWSãµãŒãã¹ã®ã¹ã¿ããå©çšããããšãã§ããŸãããµãŒããŒã¬ã¹ã«ããå Žåã¯ãserverless ã AWS SAM ãªã©ã®è€æ°ã®ãã¬ãŒã ã¯ãŒã¯ã«ãããFaaSã³ãŒãã®ããŒã«ã«èµ·åãå¯èœã«ãªããŸãã
巚倧ãªKubernetesã®ãšã³ã·ã¹ãã ã§ã¯ãå€ãã®æ°ããããŒã«ãé »ç¹ã«çºè¡šãããŠããŸãããããŒã«ã«ããã³CI-ãã©ãŒãªã³ã°ã®ããã®æšæºçã§äŸ¿å©ãªããŒã«ã¯ãŸã å
¬åŒåãããŠããŸããã1ã€ã®ã¢ãããŒããšã㊠Minikube ã MicroK8s ãªã©ã®ããŒã«ã䜿ã£ãŠæå°åãããKubernetes
ãå®è¡ããæ¹æ³ããããŸãããããã®ããŒã«ã¯æ¬ç©ã«äŒŒãŠããŸããããªãŒããŒããããå°ãªãã®ãç¹åŸŽã§ãã ä»ã®ã¢ãããŒããšããŠã¯ããªã¢ãŒãã®å®éã®Kubernetes
äžã§ãã¹ãããæ¹æ³ããããŸããããã€ãã®CIãããã€ããŒ(äŸïŒCodefresh) ã¯Kubernetesç°å¢ãšãã€ãã£ãã«çµ±åãããŠãããå®éã®Kubernetesäžã§CIãã€ãã©ã€ã³ãç°¡åã«å®è¡ã§ããŸããä»ã®ãããã€ããŒã¯ãªã¢ãŒãã®Kubernetesã«å¯ŸããŠã«ã¹ã¿ã ã¹ã¯ãªãããå®è¡ã§ããŸãã
â ãããªãã°: æ¬çªç°å¢ãšãã¹ãç°å¢ã§ç°ãªããã¯ãããžãŒã䜿çšãããšã2ã€ã®ãããã€ã¡ã³ãã¢ãã«ãç¶æããå¿ èŠããããéçºè ãšéçšããŒã ãåé¢ãããŠããŸããŸãã
â ã³ãŒãäŸ
ð äŸ: CIãã€ãã©ã€ã³äžã§ãã®å Žã§Kubernetesã¯ã©ã¹ã¿ãçæãã (åºå ž: Dynamic-environments Kubernetes)
deploy:
stage: deploy
image: registry.gitlab.com/gitlab-examples/kubernetes-deploy
script:
- ./configureCluster.sh $KUBE_CA_PEM_FILE $KUBE_URL $KUBE_TOKEN
- kubectl create ns $NAMESPACE
- kubectl create secret -n $NAMESPACE docker-registry gitlab-registry --docker-server="$CI_REGISTRY" --docker-username="$CI_REGISTRY_USER" --docker-password="$CI_REGISTRY_PASSWORD" --docker-email="$GITLAB_USER_EMAIL"
- mkdir .generated
- echo "$CI_BUILD_REF_NAME-$CI_BUILD_REF"
- sed -e "s/TAG/$CI_BUILD_REF_NAME-$CI_BUILD_REF/g" templates/deals.yaml | tee ".generated/deals.yaml"
- kubectl apply --namespace $NAMESPACE -f .generated/deals.yaml
- kubectl apply --namespace $NAMESPACE -f templates/my-sock-shop.yaml
environment:
name: test-for-ci
â ããããŸããã: æ£ããæ¹æ³ã§è¡ãã°ããã¹ãã¯24æé365æ¥ã»ãŒå³åº§ã«ãã£ãŒãããã¯ãæäŸããŠãããå人ã§ãã ããããå®è·µçã«ã¯ã1ã€ã®ã¹ã¬ããã§500ã®CPUããŠã³ãã®ãŠããããã¹ããå®è¡ããã«ã¯æéãããããããŸãã 幞ããªããšã«ãææ°ã®ãã¹ãã©ã³ããŒãCIãã©ãããã©ãŒã ïŒJest ã AVA ãMocha extensions ã®ãããªïŒã§ã¯ããã¹ããè€æ°ã®ããã»ã¹ã«äžŠååãããã£ãŒãããã¯ãŸã§ã®æéãå€§å¹ ã«æ¹åããããšãã§ããŸããCIãã³ããŒã®äžã«ã¯ããã¹ããã³ã³ããéïŒïŒïŒã§äžŠååãããã®ããããããã«ãããã£ãŒãããã¯ã«ãŒããããã«ççž®ãããŸããããŒã«ã«ã§è€æ°ã®ããã»ã¹ã䜿çšããŠããã¯ã©ãŠãã®CLIã§è€æ°ã®ãã·ã³ã䜿çšããŠãããããããç°ãªãããã»ã¹ã§å®è¡ãããå¯èœæ§ãããããã䞊ååã«ãã£ãŠãã¹ããèªåŸçã«ç¶æããå¿ èŠããããŸãã
â ãããªãã°: æ°ããã³ãŒããããã·ã¥ããŠãã1æéåŸã«ãã¹ãã®çµæãåºãã®ã§ã¯ããã®é ã«ã¯æ¢ã«æ¬¡ã®æ©èœã®ã³ãŒãã£ã³ã°ãããŠããã§ãããããããã¹ãã®å¹æãåæžãããŠããŸããŸãã
â ã³ãŒãäŸ
ð æ£ããäŸ: ãã¹ãã®äžŠååã«ãããMocha parallelãšJestã¯åŸæ¥ã®Mochaãç°¡åã«åé§ããŸãã (åºå ž: JavaScript Test-Runners Benchmark)
â ããããŸããã: ã©ã€ã»ã³ã¹ãççšã®åé¡ã¯ãããããä»ã¯äž»ãªé¢å¿äºã§ã¯ãªãã§ããããã10åã§ãã®é ç®ãæºããããšãããã©ãã§ãããïŒ license check ã plagiarism check ïŒåçšå©çšå¯èœãªç¡æãã©ã³ïŒãªã©ã®npmããã±ãŒãžã¯ãCIãã€ãã©ã€ã³ã«ç°¡åã«çµã¿èŸŒãããšãã§ããå¶éä»ãã©ã€ã»ã³ã¹ã®äŸåé¢ä¿ããStack Overflowããã³ããŒããŒã¹ããããã³ãŒããªã©ãæããã«èäœæš©ã«éåããŠããã³ãŒããæ€æ»ããããšãã§ããŸãã
â ãããªãã°: æå³ããã«äžé©åãªã©ã€ã»ã³ã¹ã®ããã±ãŒãžã䜿çšããããåçšã³ãŒããã³ããŒããŒã¹ããããããŠãæ³çãªåé¡ãçºçããå¯èœæ§ããããŸãã
â ã³ãŒãäŸ
// license-checker ãããŒã«ã«åã¯CIç°å¢ã«ã€ã³ã¹ããŒã«ããŠãã ãã
npm install -g license-checker
// ãã¹ãŠã®ã©ã€ã»ã³ã¹ãã¹ãã£ã³ããæªæ¿èªã®ã©ã€ã»ã³ã¹ãèŠã€ããå Žåã¯0以å€ã®çµäºã³ãŒãã§å€±æããããã«ããŸããCIç°å¢ã§ã¯ããã®å€±æããã£ããããŠããã«ãã忢ããå¿
èŠããããŸãã
license-checker --summary --failOn BSD
â ããããŸããã: Expressãªã©ã®æãä¿¡é Œã§ããäŸåé¢ä¿ã§ãã£ãŠããæ¢ç¥ã®è匱æ§ããããŸããããã¯ãnpm audit ã®ãããªã³ãã¥ããã£ããŒã«ããsnyk ïŒç¡æã®ã³ãã¥ããã£ããŒãžã§ã³ããããŸãïŒã®ãããªåçšããŒã«ã䜿ãã°ãç°¡åã«è§£æ±ºã§ããŸãããããã®ããŒã«ã¯ããã«ãã®ãã³ã«CIããèµ·åããããšãã§ããŸãã
â ãããªãã°: å°çšã®ããŒã«ã䜿ããã«ã³ãŒããè匱æ§ããå®ãã«ã¯ãæ°ããªè åšã«é¢ãããªã³ã©ã€ã³ã®æ å ±ãåžžã«ãã§ãã¯ããå¿ èŠããããŸããéåžžã«é¢åã§ãã
â ããããŸããã: Yarnãšnpmã®package-lock.jsonã®å°å ¥ã¯ãæ·±å»ãªèª²é¡ããããããŸããïŒå°çãžã®éã¯åæã§æ·ãããŠããŸãïŒ - æšæºã§ã¯ãããã±ãŒãžã¯ãã¯ãæŽæ°ãããŸãããânpm installâ ãš ânpm updateâ ã§äœåºŠããããã€ãç¹°ãè¿ãããŒã ã§ããæ°ããã¢ããããŒãã¯åŸãããŸããããã®çµæãäŸåããã±ãŒãžã®ããŒãžã§ã³ã¯è¯ããŠãæšæºä»¥äžã«ãªããææªã®å Žåã¯è匱ãªã³ãŒãã«ãªããŸããçŸåšãããŒã ã¯æåã§package.jsonãæŽæ°ããããã«éçºè ã®åæãšèšæ¶åã«é Œã£ãŠããããncu ã®ãããªããŒã«ãæåã§äœ¿çšããŠããŸãã ãã確å®ãªæ¹æ³ã¯ãæãä¿¡é Œæ§ã®é«ãäŸåé¢ä¿ã®ããŒãžã§ã³ãååŸããããã»ã¹ãèªååããããšã§ããããŸã éã®åŒŸäžžã®ãããªè§£æ±ºçã¯ãããŸããããã ãå¯èœæ§ã®ããèªååã®éã¯2ã€ãããŸã:
(1) CIã§ãânpm outdatedâ ãânpm-check-updates (ncu)âãªã©ã®ããŒã«ã䜿ã£ãŠãå€ãäŸåé¢ä¿ãæã€ãã«ãã倱æãããŸããããã«ãããéçºè ã«äŸåé¢ä¿ã®æŽæ°ã匷å¶ããããšãã§ããŸãã
(2) ã³ãŒããã¹ãã£ã³ããŠãäŸåé¢ä¿ãæŽæ°ãããã«ãªã¯ãšã¹ããèªåçã«äœæããåçšããŒã«ã䜿çšããŸããæ®ãäžã€ã®è峿·±ãåé¡ã¯ãäŸåé¢ä¿ã®æŽæ°ããªã·ãŒãã©ãããããšããããšã§ãã- ãããããšã«æŽæ°ãããšãªãŒããŒãããã倧ãããªããããŸãããã¡ãžã£ãŒãªãªãŒã¹çŽåŸã«æŽæ°ãããšäžå®å®ãªããŒãžã§ã³ã«ãªã£ãŠããŸãå¯èœæ§ãããã§ãããïŒå€ãã®ããã±ãŒãžããªãªãŒã¹åŸæ°æ¥ã§è匱æ§ãçºèŠãããŠããŸããeslint-scopeã®ã€ã³ã·ãã³ã ãã¿ãŠãã ããïŒã
å¹ççãªã¢ããããŒãããªã·ãŒã§ã¯ãããã€ãã®ãæš©å©ç¢ºå®æéããèšããããšãã§ããŸã - ããŒã«ã«ãå€ããªã£ããšå€æããåã«ãã³ãŒãã@latestããããã°ããé
ããããŒãžã§ã³ã«ãªãããã«ããŸãïŒäŸïŒããŒã«ã«ããŒãžã§ã³ã¯1.3.1ããªããžããªããŒãžã§ã³ã¯1.3.8ïŒã
â ãããªãã°: äœæè ã«ãã£ãŠãªã¹ã¯ããããšæç€ºçã«ã¿ã°ä»ããããããã±ãŒãžããããã¯ã·ã§ã³ã§å®è¡ãããŸãã
â ã³ãŒãäŸ
ð äŸ: ncu ã¯æåãŸãã¯CIãã€ãã©ã€ã³äžã§ãã³ãŒããã©ã®çšåºŠææ°ããŒãžã§ã³ããé ããŠããããæ€åºããããã«äœ¿çšã§ããŸãã
â ããããŸããã: ãã®èšäºã¯ãNode JSã«é¢é£ããããå°ãªããšãNode JSã§äŸç€ºã§ãããã¹ãã®ã¢ããã€ã¹ã«çŠç¹ãåœãŠãŠããŸããã§ãããã®é ç®ã§ã¯ãNodeã«é¢é£ããªãããã©ããç¥ãããŠããCIã®Tipsãããã€ããŸãšããŠç޹ä»ããŸãã
- 宣èšåã®æ§æã䜿çšãããã»ãšãã©ã®ãã³ããŒã§ã¯ãããå¯äžã®éžæè¢ã§ãããJenkinsã®å€ãããŒãžã§ã³ã§ã¯ãã³ãŒããUIã䜿çšããããšãã§ããŸãã
- Dockerã«ãã€ãã£ãã§å¯Ÿå¿ããŠãããã³ããŒãéžã¶ã
- æ©æã«å€±æããæéã®ãã¹ããæåã«å®è¡ãããè€æ°ã®é«éãªæ€æ»ïŒäŸïŒãªã³ãã£ã³ã°ããŠããããã¹ãïŒããŸãšãããã¹ã¢ãŒã¯ãã¹ããã®ã¹ããã/ãã€ã«ã¹ããŒã³ãäœæããã³ãŒãã³ããã¿ãŒã«è¿ éãªãã£ãŒãããã¯ãæäŸããŸãããã
- ãã¹ãã¬ããŒããã«ãã¬ããžã¬ããŒãããã¥ãŒããŒã·ã§ã³ã¬ããŒãããã°ãªã©ããã¹ãŠã®ãã«ãææç©ã«ç°¡åã«ç®ãéãããšãã§ããã
- ã€ãã³ãããšã«è€æ°ã®ãã€ãã©ã€ã³/ãžã§ããäœæãããããã®éã§ã¹ããããåå©çšãããäŸãã°ããã£ãŒãã£ãŒãã©ã³ãã®ã³ãããçšã®ãžã§ããšããã¹ã¿ãŒãã©ã³ãã®PRçšã®ãžã§ãã«ã¯å¥ã®ãžã§ããèšå®ããŸãããããããå ±æã¹ãããã䜿ã£ãŠããžãã¯ãåå©çšã§ããããã«ããŸãïŒã»ãšãã©ã®ãã³ããŒãã³ãŒãåå©çšã®ããã®äœããã®ã¡ã«ããºã ãæäŸããŠããŸãïŒã
- ãžã§ã宣èšã«æ©å¯æ å ±ãåã蟌ãŸãªããç¹å®ã®ä¿åå Žæããžã§ãã®èšå®ããæ©å¯æ å ±ãååŸããããã«ããŠãã ããã
- ãªãªãŒã¹ãã«ãã§æç€ºçã«ããŒãžã§ã³ãäžããããå°ãªããšãéçºè ãè¡ã£ãããšãä¿èšŒããã
- äžåºŠã ããã«ãããåäžã®ãã«ãææç©ïŒäŸïŒDocker ImageïŒã«å¯ŸããŠãã¹ãŠã®æ€æ»ãå®è¡ãã
- ãã«ãéã§ç¶æ ãç§»åããªãçåœãªç°å¢ã§ãã¹ããè¡ããnode_modulesã®ãã£ãã·ã¥ã¯å¯äžã®äŸå€ãããããŸããã
â ãããªãã°: é·å¹Žã®ç¥æµã倱ãããšã«ãªãã§ããã
âª ïž 5.9 ãã«ããããªãã¯ã¹: è€æ°ã®NodeããŒãžã§ã³ã§åãCIã¹ããããå®è¡ãã
â
ããããŸããã: å質ãã§ãã¯ã¯å¶ç¶ã®çºèŠã§ãããã«ããŒããç¯å²ãåºããã°åºãã»ã©ãåé¡ãæ©æã«çºèŠããããšãã§ããŸããåå©çšå¯èœãªããã±ãŒãžãéçºããããæ§ã
ãªæ§æãNodeã®ããŒãžã§ã³ãæã€è€æ°ã®é¡§å®¢ã®è£œåãéçºããå ŽåãCIã¯ãã¹ãŠã®æ§æã®çµã¿åããã«å¯ŸããŠãã¹ãã®ãã€ãã©ã€ã³ãå®è¡ããå¿
èŠããããŸãã äŸãã°ããã顧客ã«ã¯MySQLã䜿çšããä»ã®é¡§å®¢ã«ã¯Postgresã䜿çšããå Žåãããã€ãã®CIãã³ããŒã¯ããããªãã¯ã¹ããšåŒã°ããæ©èœããµããŒãããŠãããMySQLãPostgresããããŠNodeããŒãžã§ã³8ã9ã10ã®ãããªè€æ°ã®ãã¹ãŠã®çµã¿åããã«å¯ŸããŠãã¹ãã¹ã€ãŒããå®è¡ããããšãã§ããŸããããã¯èšå®ã®ã¿ã§è¡ããã远å ã®æéã¯ããããŸããïŒãã¹ããŸãã¯ãã®ä»ã®å質ãã§ãã¯ãæ¢ã«ããããšãåæãšããŠããŸãïŒããããªãã¯ã¹ããµããŒãããŠããªãä»ã®CIã§ã¯ãæ¡åŒµæ©èœãèª¿æŽæ©èœã§å¯Ÿå¿ããŠãããããããŸããã
â ãããªãã°: ãã¹ããæžããšãã倧å€ãªäœæ¥ããã¹ãŠçµããåŸã«ãèšå®ã®åé¡ã ãã§ãã°ãçŽã蟌ãã®ãèš±ãã®ã§ããããïŒ
â ã³ãŒãäŸ
ð äŸ: TravisïŒCIãã³ããŒïŒã®ãã«ãå®çŸ©ã䜿ã£ãŠãåããã¹ããè€æ°ã®NodeããŒãžã§ã³ã§å®è¡ãã
language: node_js
node_js:
- "7"
- "6"
- "5"
- "4"
install:
- npm install
script:
- npm run test
圹å²: ã©ã€ã¿ãŒ
æŠèŠ: ç§ã¯ç¬ç«ããã³ã³ãµã«ã¿ã³ãã§ãããFortune 500äŒæ¥ãã¬ã¬ãŒãžã¹ã¿ãŒãã¢ãããšååããŠJS & Node.jsã¢ããªã±ãŒã·ã§ã³ã®ç£šãäžããè¡ã£ãŠããŸããä»ã®ã©ã®ãããã¯ãããããã¹ãã®æè¡ã«é äºãããç¿åŸãç®æããŠããŸãããŸããNode.js Best Practicesã®èè ã§ããããŸãã
ð ãªã³ã©ã€ã³ã³ãŒã¹: ãã®ã¬ã€ããæ°ã«å ¥ã£ãŠããã¹ãã¹ãã«ã極éãŸã§é«ããããšèããŠãããªããç§ã®å æ¬çãªã³ãŒã¹ Testing Node.js & JavaScript From A To Z ã蚪åããŠã¿ãŠãã ããã
Follow:
圹å²: æè¡ã¬ãã¥ã¢ãŒããã³ã¢ããã€ã¶ãŒ
ãã¹ãŠã®ããã¹ãã®æ¹èšãæ¹åããªã³ãã£ã³ã°ã磚ãäžããæ åœ
æŠèŠ: ãã«ã¹ã¿ãã¯ãŠã§ããšã³ãžãã¢ãNode.js & GraphQLã®ãšã³ã¹ãŒãžã¢ã¹ã
圹å²: ã³ã³ã»ããããã¶ã€ã³ãçŽ æŽãããã¢ããã€ã¹
æŠèŠ: 粟éããããã³ããšã³ãããããããŒãCSSã®å°éå®¶ãçµµæåæå¥œå®¶
圹å²: ãã®ãããžã§ã¯ãã®éå¶ãæ¯æŽããã»ãã¥ãªãã£é¢é£ã®ãã©ã¯ãã£ã¹ãã¬ãã¥ãŒ
æŠèŠ: Node.jsãããžã§ã¯ããšãŠã§ãã¢ããªã±ãŒã·ã§ã³ã»ãã¥ãªãã£ã«æºããããšãæãã
ãã®ãªããžããªã«è²¢ç®ããŠãããçŽ æŽããã人ã ã«æè¬ããŸãïŒ