angular: Unexpected behavior for providedIn services in TestBed

providedIn has brought a lot of goodies into the Angular world, especially for lazy loaded modules configurations.

BUT…

On the testing side, it’s current behavior is confusing and unwanted for isolation.

I’m submitting a…


[x] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Performance issue
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
[ ] Other... Please describe:

Current behavior

providedIn providers are being loaded automatically by TestBed 's Testing Module.

Expected behavior

I test all of my units in isolation.

Whilte testing, I would like the providers to not be configured automatically so I could see an error if I forgot to stub / mock a certain provider.

If we’re not going to change this behavior, at least let’s add a FLAG that gives people the opportunity to configure TestBed to fail when we’re not manually configuring providedIn services.

Minimal reproduction of the problem with instructions

look at the following project:

It’s not clear from the hello.component.spec.ts that an ajax request somewhere down the injection tree is being called

open the console and see the message from the product.service.ts

https://stackblitz.com/edit/angular-testing-providedin-example

What is the motivation / use case for changing the behavior?

Imagine the following (very realistic) scenario:

  1. You write an test for your main component (isolating all of it’s immediate dependencies)

  2. This component has child components

  3. Sometime in the future, another developer adds a dependency to one of the child components which has some side effect (ajax request or an error, or a change to the router, etc)

  4. It will take you quite a while ( in a large scale app) to track down the specific reason

The bad part about this is that it’s very sneaky and not obvious because the failure usually will come later and not as soon as possbie.

So I suggest, to ignore providedIn services in TestBed on purpose (or at least add a FLAG that allows you to achieve this behavior for the sake of our test debugging time)

Environment


Angular version: 6.1.2


Browser:
- [x] Chrome (desktop) version XX
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
 
For Tooling issues:
- Node version: XX  
- Platform:  

Others:

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 29
  • Comments: 20 (13 by maintainers)

Most upvoted comments

We are experiencing this issue as well in a new rather large angular project. We have a lot of services that depend on other services and unless you check all dependencies manually and include them in the TestBed, you end up with integration tests instead of unittests without even noticing. It is almost certain that developers forget to mock dependencies. I’m really surprised that this done on purpose as it seems like a major flaw to me.

Does anyone have a workaround for this or is there any way to check for non-mocked dependencies in an existing code-base?

I’d like to tackle this issue and ensure that testing module is always isolated / empty. I’d say that this behavior should not change / services provided in root should fail to resolve during tests.

EDIT: Managed to reproduce the issue by adding additional test in test_bed specs. Currently tracked down where provider is created and looking for a way to change the behavior in test. Possibly by overwriting provider def and removing providedIn option / or checking that early in custom test injector.

UPDATE TO REMOVE ALL DOUBT:

This will not be integrated into angular-eslint because it is recommending something which goes against the current recommendations of the Angular Team.

This is why I closed the issue requesting this on angular-eslint.

The below is merely intended as helpful guidance for folks who end up wanting to enforce things in their own codebases and incorrectly believe they need to have dedicated rules for forbidding certain syntax.


Creator of typescript-eslint and angular-eslint here 👋

This was flagged up to me via a request to create a dedicated rule for this case. I am hesitant to do that for something which is still evolving as a topic and does not yet have a dedicated solution from the Angular Team.

However, you do not necessarily need to create a dedicated lint rule for this, the built-in ESLint no-restricted-syntax rule could be used to achieve the desired result:

{
  "rules": {
    "no-restricted-syntax": [
      "error",
      {
        "selector": "Decorator[expression.callee.name=Injectable] Property[key.name=providedIn][value.value=root]",
        "message": "Do not use `providedIn: root` in Injectables"
      }
    ]
  }
}

Example of it in action:

https://typescript-eslint.io/play/#ts=4.5.2&sourceType=module&showAST=es&code=JYWwDg9gTgLgBAbzgSQHYCsCmBjGBDAIwBtM4BfOAMyghDgHIABPVAcwFci8oB6baTPQDcAKBGM0WXIRIAKBCLhK4YGgDdgAE0ya0ALgY0IMeiLIBKEdi4BnG3ABiECIjIigA&rules=N4Igdg9gtATgpgZwC4wJYGMlwCZQQTzCQEMAPEALgG0Q4YYIYQAaUBOAGzk0cpABFujYkkZU4pAA7wECVBDAA6dMQ5c4isMQC2cALwBJMACtuJAEZcAugAIbABQaS6SfFQDWcfJp37pEADdUbBwjKyoA1QBXDUiOGL0GCCQrFhBdWWIAczg+fggbSCQbKPYbAAN-IJDsIwobJKRym1QwGyNTTGJLRBAAXys+oA&tsConfig=N4IgpgHgDmBOCWBbMA7ALgQwDYBEwGMB7WDNYgZxAC41YBXMAGnEXjTyJLNgFkxMAJqQzVaDAL5A

image

I do agree with @shairez too, the problem with the proposed solution of #26088 that it would break a lot of tests (at least from my perspective and for a lot of projects I’ve seen) and therefore it should be considered a breaking change. Also in the pr it is correctly stated out that this kind of behavior is quite nice for integration tests. To advance with this in a non-breaking way I would rather create a new method in the TestBed, e.g. TestBed.configureUnitTestingModule.