The old code uses rxjs v5.5.12, We copied the same code to our new project which uses rxjs v6.4.0. We are getting this error when we tried to run the test case.
Old Code:
import * as ObservableEvents from 'rxjs/Observable/fromEvent'; spyOn(ObservableEvents, 'fromEvent').and.returnValue(new Subject<any>().asObservable());
New Code:
import * as rxjs from 'rxjs'; spyOn(rxjs, 'fromEvent').and.returnValue(new Subject<any>().asObservable());
In both cases we are getting this error:
Error: : fromEvent is not declared writable or has no setter
We couldn't find a valid resource to solve this issue.
Update #1
We tried using
import * as rxjs from 'rxjs'; spyOn(jasmine.createSpyObj(rxjs), 'fromEvent').and.returnValue(new Subject<any>().asObservable());
but this time, we got
createSpyObj requires a non-empty array or object of method names to create spies for thrown
Update #2:
We used the code from @Omair-Nabiel, now getting a new error
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)
xxx.directive.ts
line 113-> this.componentRef && this.componentRef.destroy(); this.componentRef = null; line 70-> constructor( ... private resolver: ComponentFactoryResolver, ... ) { }
Update #3
Hi Omair Nabiel, Please find the below code we are using, please let me know the solution,
file="popover.directive.ts" Code:
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(); });
Error: <spyOn> : assign is not declared writable or has no setter Here’s the typical Jasmine error, if you try to do it this way. Instead of manipulation location directly we need to inject it our component with Dependency injection mechanism.
Bean property is not writable or has an invalid setter method. Stuck with the error. Any suggestion. Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'name' of bean class: Bean property 'name' is not writable or has an invalid setter method.
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'name' of bean class: Bean property 'name' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter? It seems that you injecting dependencies of a class.
You need to spy on a property of rxjs. Using spyOnProperty will solve the error. Try this
import * as rxjs from 'rxjs' import { of, fromEvent } from 'rxjs'; spyOnProperty(rxjs, 'fromEvent').and.returnValue(of({}))
you can also add to getter/setters using this like
spyOnProperty(rxjs, 'fromEvent', 'get').and.returnValue(false)
Hope this helps
The problem is that a module namespace object like import * as rxjs
has a specific behavior and doesn't allow to mutate itself in many cases. Here's a few relevant links:
As you can see it's a well known issue but at the moment there's no ultimate solution that works in all cases. The most common workaround is to use spyOnProperty
like Alejandro Barone's answer suggests, I've tried this solution in Angular 8 / TypeScript 3.4 setup and it works well, but in doesn't work in Angular 10 / TypeScript 4 and gives the following error:
fromEvent is not declared configurable
But let's look at the problem from a different angle. As an example you can imagine a class that subscribes to window's resize
event and increments some counter when the event is triggered. The class can use fromEvent
to subscribe to the event or can subscribe directly via window.addEventListener
. In both cases the class will behave the same - the counter will be incremented when the event happens. By spying on fromEvent
you make an assumption that the class uses that function, however the only contract the class gives you is its interface. In the future someone might decide to use window.addEventListener
instead of fromEvent
and the tests will be broken despite the class works the same way. So the right way to test such a class is to trigger window's resize
event and check that the counter is incremented. It's a good practice to test classes like black boxes without any assumptions about its implementation.
If it's still important to you to spy on fromEvent
function, you can create a wrapper on it and mock it in your tests, for example:
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'); });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With