hirezio / auto-spies

Create automatic spies from classes
MIT License
180 stars 30 forks source link

Creating a spy from class with Testbed.inject does give a ts error #23

Closed LingVuDev closed 4 years ago

LingVuDev commented 4 years ago

I used the jasmine-auto-spies in the past with Angular 8. After updating to Angular 9 (and also 10.0.2) the Testbed function Testbed.get() got deprecated and it was recommended to use Testbed.inject which hasn't effect for my tests that I wrote.

However it does the effect the use of this tool.

A simple setup:

describe('AppComponent', () => {
  let component;
  let serviceSpy: Spy<AppService>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        AppComponent,
        { provide: AppService, useValue: createSpyFromClass(AppService) },
      ]
    });

    component = TestBed.inject(AppComponent);
    serviceSpy = TestBed.inject(AppService);
  });

  it('should create the app', () => {
    expect(component).toBeTruthy();
  });
});

The AppService has just an empty method and attribute.

The problematic line is serviceSpy = TestBed.inject(AppService); which for some reasons throws a ts error

Type 'AppService' is not assignable to type 'Spy<AppService>'.
  Types of property 'someMethod' are incompatible.
    Type '() => string' is not assignable to type 'AddSpyOnFunction<() => string>'.
      Type '() => string' is missing the following properties from type 'SpyMethod': calledWith, mustBeCalledWithts(2322)

Can you update the library to work with the newer Angular?

shairez commented 4 years ago

Thanks @LingVuDev !

The issue is not really an issue of jasmine-auto-spies (because it doesn't depend on Angular).

The solution for Angular is to add a generic of <any> to the inject, for example:

serviceSpy = TestBed.inject<any>(AppService);

And that should quiet down TypeScript 😀

Check it out and let me know if it did the trick

batbrain9392 commented 4 years ago

Thanks @LingVuDev !

The issue is not really an issue of jasmine-auto-spies (because it doesn't depend on Angular).

The solution for Angular is to add a generic of <any> to the inject, for example:

serviceSpy = TestBed.inject<any>(AppService);

And that should quiet down TypeScript 😀

Check it out and let me know if it did the trick

Although it works, it's still something we have to do every time. Moreover, the strict linting rule in Angular 10 screams at us with the following error:

Type declaration of 'any' loses type-safety. Consider replacing it with a more precise type.

shairez commented 4 years ago

You're right @batbrain9392 , you need to do it, but it becomes easier with code snippets (I show it in the "in action" course).

Again, this is not an issue that can be fixed with jasmine-auto-spies (I tried looking into it). because let someServiceSpy: Spy<SomeService> = TestBed.inject(SomeService) fails because inject returns a SomeService and not any like get() used to do.

So it's the responsibility of TestBed.inject and it's return type configuration and that's why I the workaround is inevitable from what I can see.

One solution is to write a wrapper to TestBed.inject' that hides the need to add the`, but again, because this library (jasmine-auto-spies) is not related to Angular specifically, it would need to be created independently or as another "helper" open source library.

Regarding the lint warning - you could disable that rule just for the spec files and that should calm tslint down as well.

Hope it helps

shairez commented 4 years ago

@LingVuDev I'm going to close this issue

Feel free to reopen it if you run into any problems regarding this topic

Brandon-Ritchie commented 2 years ago

A quick update if you don't want to remove the rule from your linter, you can also cast it afterwards as the correct type.

serviceSpy = TestBed.inject(AppService) as Spy<AppService>;