Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow precise flag on defaultMock #455

Closed
GerkinDev opened this issue Apr 28, 2021 · 10 comments · Fixed by #468
Closed

Allow precise flag on defaultMock #455

GerkinDev opened this issue Apr 28, 2021 · 10 comments · Fixed by #468

Comments

@GerkinDev
Copy link

GerkinDev commented Apr 28, 2021

Yo ! It's me again ! And here I am with a hacky angular DI use case, that works in some cases with ng-mocks.

So, first, the intro !

The goal

I'm trying to inject something that have both a call signature and properties (yep, a function), but without using the @Inject decorator.

My interface is something like this:

interface InjectedFn {
  (): string;
  hello: () => number;
}

I've found patterns using abstract classes that I tried to mimic by attaching the Injectable decorator on the function declaration.

function InjectedFn(_err: any): void {}
Injectable({
  providedIn: "root",
  useFactory: () => {
    const fn: InjectedFn = jasmine.createSpy("Base") as any;
    fn.hello = jasmine.createSpy("Inner");
    return fn;
  }
})(InjectedFn);

Then, usage is like this:

@Component({ template: "" })
export class TestComponent {
  // Note that there is no need to `provide` `InjectedFn` in your NgModule
  public constructor(public myInjectedFn: InjectedFn) {}
}

This works great in real applications. But it seems that auto-mocking behavior of ngMocks clashes with this: I suppose it tries to mock methods by replacing the function altogether, thus loosing the call signature.

Demo

https://stackblitz.com/edit/ng-mocks-examples-f57jx1?file=app/injected-fn.ts


I would understand this could be considered as a wontfix since this is quite a hack. Moreover, I'm not certain this will always work.
But this works in Angular, including when using Ivy & AOT.
Yet, I suppose the solution of the issue would be quite simple: as demonstrated in the demo above, using the precise flag on MockBuilder.mock made it work.

@satanTime
Copy link
Member

satanTime commented Apr 30, 2021

Hi there,

might you share source code of tests which work on your side?

I tried to create one, but it fails with StaticInjectorError(DynamicTestModule)[TestWithoutDecoratorComponent -> Function], and the problem there is that Angular detects Function (ctor for any function) instead of InjectedFn.

This is how I tried to test it in pure Angular, but it still fails.

  describe('without inject decorator', () => {
    describe('using TestBed', () => {
      beforeEach(() => TestBed.configureTestingModule({
        declarations: [TestWithoutDecoratorComponent],
      }));

      fit('should build properly but fails', () => {
        expect(() => TestBed.createComponent(TestWithoutDecoratorComponent)).not.toThrow();
      });
    });
  });

The issue here is that a function is provided instead of a class and Angular itself fails to detect it properly.
Also this is why @Injectable() as a decorator call doesn't work here and the only possible way is to call Injectable as a function.

I would say, for such a case a token would be a better option, they are intended to represent literals and functions.
Then everything works as expected: https://stackblitz.com/edit/ng-mocks-examples-cbizfx?file=app%2Fapp.spec.ts

@GerkinDev
Copy link
Author

GerkinDev commented Apr 30, 2021

Well, it does work even in production setup in my case using angular 11.2x.xx.

Test with success case is in the stackblitz above.

The goal is to get rid of the@Inject

@satanTime
Copy link
Member

satanTime commented May 1, 2021

Sorry, that's not what I meant. I meant, I couldn't find a TestBed example in pure Angular without ng-mocks on stackblitz: https://stackblitz.com/edit/ng-mocks-examples-f57jx1?file=app/injected-fn.ts.

If you could provide one, it would be great. Tests I tried failed the same way as ng-mocks does.


In the example with abstract classes, they use classes and their instances, whereas in your example you are trying to inject a function itself.

Nevertheless, If I am changing your example to an abstract class injection - it still fails with pure angular and with ng-mocks.

I would say - let's take a look at your production test which doesn't fail. Maybe it will bring light on the issue.

UPD1: Aha, actually with abstract classes only ng-mocks fails.

@satanTime
Copy link
Member

Could you check if this version works for you?
ng-mocks.zip

It fixes issues with abstract classes, and, I think, it should solve the issue you describe too.

@GerkinDev
Copy link
Author

Gonna try this ASAP

@GerkinDev
Copy link
Author

Actually, TestBed works: https://stackblitz.com/edit/ng-mocks-examples-f57jx1?file=app/app.spec.ts

I guess this is related to a version of Angular of some sort.

FYI, my angular-related packages:

{
  "dependencies": {
    "@angular/animations": "~11.2.10",
    "@angular/common": "~11.2.10",
    "@angular/compiler": "~11.2.10",
    "@angular/core": "~11.2.10",
    "@angular/forms": "~11.2.10",
    "@angular/platform-browser": "~11.2.10",
    "@angular/platform-browser-dynamic": "~11.2.10"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^0.1102.9"
  }
}

And, the zip you gave me works 🎉

@satanTime
Copy link
Member

Aha, cool!

Yes, I was thinking that ng-mocks test env isn't the same as angular has, and that caused differences between my and your results.

The most important part is that the zip works. Then it will be released with the next version.
I plan to do it in 1 week so I have time to fix if something comes into my head as an edge case.

@GerkinDev
Copy link
Author

Yup, I suspect this change of internal behavior to have potential tricky side effects too. But I hope I'm wrong.

Well, so now I'm waiting for 2 things:

  • the release
  • and your patreon or something 😄

@satanTime
Copy link
Member

Ahaha, deal! :)

satanTime added a commit that referenced this issue May 1, 2021
fix: overrides are always precise if they are functions #455
@satanTime
Copy link
Member

v11.11.0 has been released and contains a fix for the issue. Feel free to reopen the issue or to submit a new one if you meet any problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants