错误:<spyOn>:fromEvent未声明为可写或没有setter

Raj*_*aju 10 rxjs karma-jasmine angular rxjs6

旧代码使用rxjs v5.5.12,我们将相同的代码复制到了使用rxjs v6.4.0的新项目中。当我们尝试运行测试用例时,我们得到了这个错误。

旧代码:

import * as ObservableEvents from 'rxjs/Observable/fromEvent';
spyOn(ObservableEvents, 'fromEvent').and.returnValue(new Subject<any>().asObservable());
Run Code Online (Sandbox Code Playgroud)

新代码:

import * as rxjs from 'rxjs';
spyOn(rxjs, 'fromEvent').and.returnValue(new Subject<any>().asObservable());
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,我们都会收到此错误:

错误:: fromEvent未声明为可写或没有设置器

我们找不到有效的资源来解决此问题。

更新#1

我们尝试使用

import * as rxjs from 'rxjs';
spyOn(jasmine.createSpyObj(rxjs), 'fromEvent').and.returnValue(new Subject<any>().asObservable());
Run Code Online (Sandbox Code Playgroud)

但是这次,我们得到了

createSpyObj需要一个非空数组或方法名称对象来创建用于抛出的间谍

更新#2:

我们使用了@ Omair-Nabiel的代码,现在收到一个新错误

      TypeError: Object(...) is not a function
          at XxxPopoverDirective.fromEvent [as createPopover] (http://xxx:xxxx/src/app/shared/xxx/xxx.directive.ts?:113:37)
          at XxxPopoverDirective.createPopover [as mouseClick] (http://xxx:xxxx/src/app/shared/xxx/xxx.directive.ts?:70:14)
          at runTest (http://xxx:xxxx/src/app/shared/xxx/xxx.directive.spec.ts?:181:19)
Run Code Online (Sandbox Code Playgroud)

xxx.directive.ts

line 113-> this.componentRef && this.componentRef.destroy();
this.componentRef = null;

line 70-> constructor(
...
private resolver: ComponentFactoryResolver,
...
  ) { }
Run Code Online (Sandbox Code Playgroud)

更新#3

嗨,Omair Nabiel,请找到我们正在使用的以下代码,请让我知道解决方案,

file =“ popover.directive.ts”代码:

import { fromEvent } from 'rxjs/Observable/fromEvent';

this.clickOutSub = fromEvent(this.documentRef.getDocument(), 'click').subscribe(this.clickOut.bind(this));

file="popover.directive.spec.ts"
Code:
import * as ObservableEvents from 'rxjs/Observable/fromEvent';

function runTest() {

spyOn(ObservableEvents, 'fromEvent').and.returnValue(new Subject<any>().asObservable());

 }

it('...', () => {
expect(ObservableEvents.fromEvent).toHaveBeenCalled();
});
Run Code Online (Sandbox Code Playgroud)

Oma*_*iel 8

您需要监视 rxjs 的一个属性。使用 spyOnProperty 将解决该错误。尝试这个

 import * as rxjs from 'rxjs'
 import { of, fromEvent } from 'rxjs';  

spyOnProperty(rxjs, 'fromEvent').and.returnValue(of({}))
Run Code Online (Sandbox Code Playgroud)

您还可以使用这样的方法添加到 getter/setter

spyOnProperty(rxjs, 'fromEvent', 'get').and.returnValue(false)
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助


Val*_*kov 5

问题是模块命名空间对象import * as rxjs具有特定的行为,并且在许多情况下不允许自身发生变异。这是一些相关的链接:

正如您所看到的,这是一个众所周知的问题,但目前没有适用于所有情况的最终解决方案。最常见的解决方法是spyOnPropertyAlejandro Barone 的回答所建议的那样使用,我已经在 Angular 8 / TypeScript 3.4 设置中尝试过这个解决方案并且它运行良好,但在 Angular 10 / TypeScript 4 中不起作用并给出以下错误:

fromEvent 未声明为可配置的

但让我们换个角度看问题。作为一个例子,你可以想象一个类订阅窗口的resize事件并在事件被触发时增加一些计数器。该类可以用于fromEvent订阅事件,也可以直接通过window.addEventListener. 在这两种情况下,类的行为都相同——当事件发生时,计数器将增加。通过监视fromEvent你假设该类使用该函数,但是该类给你的唯一契约是它的接口。将来有人可能会决定使用window.addEventListener而不是,fromEvent尽管类的工作方式相同,但测试将被破坏。所以测试这样一个类的正确方法是触发窗口的resize事件并检查计数器是否增加。在不对其实现做任何假设的情况下测试像黑盒这样的类是一种很好的做法。

如果监视fromEvent函数对您来说仍然很重要,您可以在其上创建一个包装器并在测试中模拟它,例如:

import { fromEvent, Observable, of } from 'rxjs';
import { FromEventTarget } from 'rxjs/internal/observable/fromEvent';

class EventObserver {
  observe<T>(target: FromEventTarget<T>, eventName: string): Observable<T> {
    return fromEvent(target, eventName);
  }
}

class MyClass {
  constructor(
    private eventObserver = new EventObserver()
  ) {}

  doSomething() {
    this.eventObserver.observe(window, 'resize').subscribe(() => {
      // do something
    });
  }
}

it("#doSomething should subscribe to window's resize event", () => {
  const eventObserver = jasmine.createSpyObj<EventObserver>(EventObserver.name, ['observe']);
  eventObserver.observe.and.returnValue(of({}));

  const myClass = new MyClass(eventObserver);
  myClass.doSomething();

  expect(eventObserver.observe).toHaveBeenCalledTimes(1);
  expect(eventObserver.observe).toHaveBeenCalledWith(window, 'resize');
});
Run Code Online (Sandbox Code Playgroud)


Ale*_*one 5

补充奥马尔的回答。就我而言,我需要在returnValue语句上有一个函数。

const fromEventSpy = spyOnProperty(rxjs, 'fromEvent').and.returnValue(() => rxjs.of({}));

干杯!