Состояние глобальных объектов QUnit, загрязненных в модульных тестах - PullRequest
0 голосов
/ 01 октября 2018

Проблема в этом вопросе продолжает возникать: каким-то образом состояния, устанавливаемые некоторыми методами тестирования, каким-то образом переносятся в другие тесты.

Я кодирую в 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
  })
}

Результат

При введении вторых тестов оба теста теперь не пройдены:

Test against createDriverMenu

Полный текст:

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) 

Test against setFormattingFor

Полный текст:

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 происходит загрязнение состояния, даже если я установил глобальные объекты на их заглушки в настройках теста, ивернуть состояния обратно в тестовый демонтаж?

...