首页 > 解决方案 > 如何在 AngularJS 测试中应用动态语言环境更改?

问题描述

在使用 AngularJS 应用程序中angular-dynamic-locale,我想在不同的语言环境中测试依赖于语言环境的代码。

我正在尝试在异步设置中更改语言环境。在以前的 Jasmine 版本中,这是通过闩锁功能完成的;这些现在已弃用并替换为done回调。

beforeEach(function (done) {
  inject(function ($injector) {
    tmhDynamicLocale = $injector.get('tmhDynamicLocale');

    console.log('setting locale to French...');
    tmhDynamicLocale
      .set('fr-fr')
      .then(function () {
        console.log('locale set.');
        done();
      })
      .catch(function (err) {
        done.fail('failed to set locale:', err);
      });
  });
});

由返回的承诺将tmhDynamicLocale.set永远挂起,并且设置超时。

查看显示语言环境更改的内部结构,但从未实际应用:tmhDynamicLocale

// This line runs
$rootScope.$applyAsync(function() {
  // But this callback doesn't
  storage[storagePut](storageKey, localeId);
  $rootScope.$broadcast('$localeChangeSuccess', localeId, $locale);
  deferred.resolve($locale);
});

我已经尝试调用$browser.defer.flush和消化/应用根范围,但仍然没有执行语言环境更改回调。

如何更改此测试套件中的语言环境?

Plunker,为清楚起见添加了日志tmhDynamicLocale

标签: javascriptangularjsjasminekarma-jasmine

解决方案


似乎没有一种干净的方法可以做到这一点,但我找到了一个骇人听闻的解决方法。

为什么承诺没有解决

tmhDynamicLocale 通过<script>元素加载语言环境文件。load此元素的事件触发时,它会安排事件$localeChangeSuccess并承诺解决。

在生产中,这很好用:

  1. 我的代码调用tmhDynamicLocale.set('fr-fr')
  2. <script>元素已创建
  3. 加载语言环境文件;load火灾
  4. load事件回调中,一个新的回调被安排异步应用
  5. 完成请求事件也会触发一个 Angular 摘要循环
  6. 摘要循环运行预定的回调
  7. 此回调解决了语言环境更改承诺

在测试中,浏览器事件不会触发摘要循环,所以会发生这种情况:

  1. 我的代码调用tmhDynamicLocale.set('fr-fr')
  2. <script>元素已创建
  3. 我的测试代码中的任何$timeouts 在这里运行,为空$$applyAsyncQueue
  4. 加载语言环境文件;load火灾
  5. load事件回调中,一个新的回调被安排异步应用
  6. 我的代码没有收到通知;它无法触发新的回调,并且承诺永远挂起

因此,在 Angular 摘要周期内无法触发承诺解决方案,或者至少我不知道如何。

解决方法

由于我们无法判断回调何时安排,因此轮询直到安排。与$interval,不同的是,setInterval它不会在测试中被模拟出来并允许真正的轮询。

一旦回调被安排,$browser.defer.flush将运行它,并触发承诺解决。如果没有要刷新的延迟任务,它就会抛出,所以只有在队列非空时才调用它。

_forceFlushInterval = setInterval(function () {
  if ($rootScope.$$applyAsyncQueue.length > 0) {
    $browser.defer.flush();
  }
});

可以想象,可以安排除区域设置更改之外的任务。所以我们一直在轮询,直到承诺得到解决。

tmhDynamicLocale.set('fr-fr')
  .then(function () {
    clearInterval(_forceFlushInterval);
    done();
  })
  .catch(function () {
    clearInterval(_forceFlushInterval);
    done.fail();
  });

但是有很多缺点:

  1. 访问 $$private Angular 内部不是一个好主意,并且可能会在版本之间中断。
  2. 不断刷新可能会干扰其他异步设置。
  3. 如果由于某种原因没有清除间隔,它将对以后的测试造成各种破坏。

普朗克


推荐阅读