Force Knockout вычисляется для переоценки после замены наблюдаемого внутри - PullRequest
0 голосов
/ 06 ноября 2018

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

Как мы можем заставить вычисленную переоценить, в которой больше нет первоначальных наблюдаемых?

const ViewModel = function() {
  this.otherComputed = ko.computed(() => true);
  this.computedUnderTest = ko.computed(() => this.otherComputed());
};

const vm = new ViewModel();

function expect(expected) {
  console.log(vm.computedUnderTest() === expected);
}

// Init
expect(true);

// Stub dependent computed
vm.otherComputed = () => false;

// Computed no longer receives updates :(
expect(false);

// Can we force re-evaluation?
// vm.computedUnderTest.forceReEval()
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

1 Ответ

0 голосов
/ 06 ноября 2018

Единственное решение, которое я могу придумать, что не предполагает изменение кода ViewModel, это заглушка ko.computed сначала ...

В приведенном ниже примере я заменяю ko.computed на расширенную версию. Расширение предоставляет свойство .stub, которое позволяет вам написать пользовательскую функцию. Когда эта функция установлена, вычисляемая будет переоценена с использованием предоставленной логики.

В вашем тестовом файле вам понадобится заменить глобальную ссылку на ko.computed в коде подготовки, до создания экземпляра экземпляра ViewModel.

// Extender that allows us to change a computed's main value getter
// method *after* creation
ko.extenders.canBeStubbed = (target, value) => {
  if (!value) return target;
  
  const stub = ko.observable(null);
  const comp =  ko.pureComputed(() => {
    const fn = stub();
    
    return fn ? fn() : target();
  });
  
  comp.stub = stub;
  
  return comp;
}

// Mess with the default to ensure we always extend
const { computed } = ko;
ko.computed = (...args) => 
  computed(...args).extend({ canBeStubbed: true });

// Create the view model with changed computed refs
const ViewModel = function() {
  this.otherComputed = ko.computed(() => true);
  this.computedUnderTest = ko.computed(() => this.otherComputed());
};

const vm = new ViewModel();

function expect(expected) {
  console.log("Test succeeded:", vm.computedUnderTest() === expected);
}


expect(true);

// Replace the `otherComputed`'s code by another function
vm.otherComputed.stub(() => false);

expect(false);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

В моих собственных проектах я склонен использовать совершенно другой подход для тестирования своих вычислений, который фокусируется на отделении логики от зависимостей. Дайте мне знать, если приведенный выше пример не работает для вас. (Я не собираюсь писать другой ответ, если это уже удовлетворяет ваши потребности)

...