Проблема в этом вопросе продолжает возникать: каким-то образом состояния, устанавливаемые некоторыми методами тестирования, каким-то образом переносятся в другие тесты.
Я кодирую в Google Apps Script и выполняю модульное тестирование с использованием QUnit для Google Apps Script.
Тестируемые функции
/**
* Creates menu with driver items
**/
function createDriverMenu() {
SpreadsheetApp.getUi()
.createMenu("New Project Analysis")
.addItem("From File", "openFilePrompt")
.addToUi()
}
/**
* Sets formatting on the Sheet
* @param { Sheet } sheet
* @returns { Sheet } sheet
**/
function setFormattingFor(sheet) {
if (!sheet) throw Error("sheet required to setFormattingFor !")
// set conditional formatting on the range C2:C
var range = sheet.getRange("C2:C")
var rules = sheet.getConditionalFormatRules()
// if a cell's text equals FALSE, set that cell to white text, and the background to red
Logger.log(SpreadsheetApp)
ruleForFalseText = SpreadsheetApp.newConditionalFormatRule()
.setRanges([range])
.whenTextEqualTo("FALSE")
.setBackground("red")
.setFontColor("white")
.build()
rules.push(ruleForFalseText)
ruleForTrueText = SpreadsheetApp.newConditionalFormatRule()
.setRanges([range])
.whenTextEqualTo("TRUE")
.setBackground("green")
.setFontColor("white")
.build()
rules.push(ruleForTrueText)
ruleForInProgressText = SpreadsheetApp.newConditionalFormatRule()
.setRanges([range])
.whenTextEqualTo("IN PROGRESS")
.setBackground("orange")
.setFontColor("white")
.build()
rules.push(ruleForInProgressText)
ruleForPartialText = SpreadsheetApp.newConditionalFormatRule()
.setRanges([range])
.whenTextEqualTo("PARTIAL")
.setBackground("yellow")
.setFontColor("black")
.build()
rules.push(ruleForPartialText)
ruleForClosedTilTestableText = SpreadsheetApp.newConditionalFormatRule()
.setRanges([range])
.whenTextEqualTo("CLOSED TIL TESTABLE")
.setBackground("black")
.setFontColor("white")
.build()
rules.push(ruleForClosedTilTestableText)
sheet.setConditionalFormatRules(rules)
}
Тесты
function testCreateDriverMenu() {
var menuStub // the stub for the menu
var savedSpreadsheetApp // the state of SpreadsheetApp from Google's Sheets API
QUnit.testStart(function() {
// create a menu stub
menuStub = {
addItemCalls : [],
addItem : function(txt, funcName) {
this.addItemCalls.push({ args : { displayTxt : txt, funcName: funcName } })
return this
},
addToUiCalled : false,
addToUi : function() { this.addToUiCalled = true }
}
// save the state of SpreadsheetApp to temporary variable
savedSpreadsheetApp = SpreadsheetApp;
SpreadsheetApp = {
ui : {
createMenuCalls : [],
createMenu : function(str) {
this.createMenuCalls.push({ args: str })
return menuStub
}
},
getUi : function() { return this.ui }
}
})
QUnit.test("testing createDriverMenu",
function() {
// hit the method under test
createDriverMenu({})
// do assertions
ok(SpreadsheetApp.ui.createMenuCalls.length, "SpreadsheetApp.getUi().createMenu was invoked at least once");
ok(menuStub.addItemCalls.length, "SpreadsheetApp.getUi().createMenu().addItem was invoked at least once");
ok(menuStub.addToUiCalled, "menu added to ui")
})
QUnit.testDone(function() {
// set SpreadsheetApp back
SpreadsheetApp = savedSpreadsheetApp;
})
}
function testSetFormattingFor() {
var sheet
var savedSpreadsheetApp
var rule
QUnit.testStart(function() {
savedSpreadsheetApp = SpreadsheetApp
rule = {
setRangesCalls : [],
setRanges: function(arr) {
this.setRangesCalls.push({ args: arr })
return this
},
whenEqualToCalls : [],
whenEqualTo : function(str) {
this.whenEqualToCalls.push({ args: str })
return this
},
setBackgroundCalls : [],
setBackground : function(str) {
this.setBackgroundCalls.push({ args: str })
return this
},
setFontColorCalls : [],
setFontColor : function(str) {
this.setFontColorCalls.push({ args: str })
return this
},
buildCalls : [],
build : function() {
this.buildCalls.push({ args: null })
return this
}
}
SpreadsheetApp = {
newConditionalFormatRuleCalls : [],
newConditionalFormatRule : function() {
this.newConditionalFormatRuleCalls.push({ args : null })
return rule
}
}
sheet = {
rule : [],
setConditionalFormatRulesCalls : [],
setConditionalFormatRules : function(rules) {
this.setConditionalFormatRulesCalls.push({ args: rules })
},
getConditionalFormatRulesCalls : [],
getConditionalFormatRules : function() {
return this.rules
},
range: [],
getRangeCalls: [],
getRange: function(str) {
this.getRangeCalls.push({ args : str })
return this.range;
}
}
})
QUnit.test("test setFormattingFor",
function() {
val = setFormattingFor(sheet)
// sheet.getRange should have been called
ok(sheet.getRangeCalls.length, "sheet.getRange was called at least once")
equal(val, sheet, "setFormattingFor returned something")
})
QUnit.test("test setFormattingFor with no args",
function() {
throws(function() {
setFormattingFor()
})
})
QUnit.testDone(function() {
SpreadsheetApp = savedSpreadsheetApp
})
}
Результат
При введении вторых тестов оба теста теперь не пройдены:
Полный текст:
Died on test #1 at qunit.js (QUnit):1494 (sourceFromStacktrace) at qunit.js (QUnit):461 at gas-qunit (QUnit):191 (test) at tests:54 (testCreateDriverMenu) at tests:6 (tests) at gas-qunit (QUnit):56 (load) at Code:11 (doGet) : Cannot find function getUi in object [object Object].
Source:
at Code:47 (createDriverMenu) at tests:58 at qunit.js (QUnit):213 at qunit.js (QUnit):399 at qunit.js (QUnit):1542 (process) at qunit.js (QUnit):528 at qunit.js (QUnit):1294 at gas-qunit (QUnit):57 (load) at Code:11 (doGet)
Полный текст:
Died on test #1 at qunit.js (QUnit):1494 (sourceFromStacktrace) at qunit.js (QUnit):461 at gas-qunit (QUnit):191 (test) at tests:592 (testSetFormattingFor) at tests:15 (tests) at gas-qunit (QUnit):56 (load) at Code:11 (doGet) : Cannot find function whenTextEqualTo in object [object Object].
Source:
at Code:173 (setFormattingFor) at tests:594 at qunit.js (QUnit):213 at qunit.js (QUnit):399 at qunit.js (QUnit):1542 (process) at qunit.js (QUnit):528 at qunit.js (QUnit):1294 at gas-qunit (QUnit):57 (load) at Code:11 (doGet)
Если бы мне пришлось удалить новый тест, я бы просто прошел все тесты.
Почему в QUnit происходит загрязнение состояния, даже если я установил глобальные объекты на их заглушки в настройках теста, ивернуть состояния обратно в тестовый демонтаж?