1

How do I list all the dependency injectors of a component in angular?

This requirement is for unit testing.

I have an instance of a component created as follows :

component = fixture.componentInstance();

So in my test case, I need something like

console.log(component.constructor.params) // should print all the constructor parameteres

For ex in the below code :

constructor(private _logger: LoggerService, private _router: Router,
                private _adminService: AdminService) {
}

I need to fetch array of DI's

0 -> LoggerService
1 -> Router
2 -> AdminService
Amit Chigadani
  • 28,482
  • 13
  • 80
  • 98

3 Answers3

0

You should be able to take advantage of Javascript's built in arguments object.

So if you just need the names of the classes of each dependency, you can put this in your constructor:

constructor(
    private _logger: LoggerService, 
    private _router: Router,
    private _adminService: AdminService
) {
    let dependencies = Array.from(arguments).map((item) => item.constructor.name);
}

Logging the dependencies object to the console should output:

0: "LoggerService"
1: "Router"
2: "AdminService"

If your array needs to contain the full object instead of the name of the class, just remove the .map() function from the constructor code.

jtate
  • 2,612
  • 7
  • 25
  • 35
  • But how do I do that outside of `constructor` function? – Amit Chigadani Aug 06 '18 at 07:07
  • @AmitChigadani you can just declare a property called `dependencies` in the class itself: `dependencies = any[];` then in the constructor, set `this.dependencies = ` instead of `let dependencies =`. Then you can always refer to `this.dependencies` whenever you need access to the array. – jtate Aug 06 '18 at 12:59
  • Sorry, but I don't want to make any changes to my existing code. Also In that case, I could directly add this line in my component file like `this.dependencies = ['LoggerService', 'Router', 'AdminService'];` because I know what are my DI's in my constructor. – Amit Chigadani Aug 06 '18 at 13:02
  • Then you're going to need an instance of your class and iterate over all of the keys of this instance, to check if thos keys are instances of a specific class with either `instanceof` or `constructor.name`. –  Aug 06 '18 at 14:04
  • @AmitChigadani then I'm unclear what you're trying to accomplish. In your question you say, "in the below code I need to fetch array of DI's". I gave you a way to fetch the DI's in the constructor, so what else do you need? If you don't want to change your code then there is no way to access the injections from outside the class, since they are private (at least from a TypeScript point of view). trichetriche is right, you'd have to manually enumerate over the keys, and that would only work when you know exactly what you're looking for. – jtate Aug 06 '18 at 15:18
  • @trichetriche I had actually ended doing that. But I thought, this could be somehow possible using component constructor prototype. – Amit Chigadani Aug 06 '18 at 17:56
0

You could monkey patch your constructor before the dependency injection:

function keepConstructorDependenciesOf(MyClass) {
  // keep references to your class and your constructor
  const originalPrototype = MyClass.prototype;
  const originalConstructor = MyClass;

  // add the logic to retrieve the type of dependencies 
  MyClass = function() { 
    this.dependencies = Array.from(arguments).map(a => a.constructor.name); 
    originalConstructor.apply(this, arguments)  
  }

  // re-apply the original prototype
  MyClass.prototype = originalPrototype;

  return MyClass;
}

Component = keepConstructorDependenciesOf(Component);

// run the dependency injection
...

component = fixture.componentInstance();

expect(component.dependencies).toEqual([
  'LoggerService', 
  'Router', 
  'AdminService'
])

Some references about redefining a constuctor

Bastien Caudan
  • 4,482
  • 1
  • 17
  • 29
-1

Use this code to print out the constructor parameter. where you have to Provider to mention the services and router.

//import module services and router her 


 describe('MyComponent', () => {
  let _logger: LoggerService;
  let _router : Router;
  let _adminService :AdminService;
beforeEach(async(() => {

  TestBed.configureTestingModule({
    declarations: [
        MyComponent,
    ],
    imports: [ 
        CommonModule,
        FormsModule,
        HttpModule
  ],
  providers: [

  {provide:AdminService,  useValue: adminService},     
  {provide:Router, useValue:routerSpy} ,     
  {provide:LoggerService, useValue: logService } ,     

  ],
  }).compileComponents();

  fixture = TestBed.createComponent(MyComponent);
 component = fixture.componentInstance;
 _adminService = new AdminService(http);
  _logger = new LoggerService(http); 
}));  
    it('should create the app', async(() => {
      const fixture = TestBed.createComponent(MyComponent);
      component = fixture.componentInstance;
      const app = fixture.debugElement.componentInstance;
     console.log(component.constructor); 
    // should print all the constructor parameteres 
      expect(app).toBeTruthy();
    }));
}));
hazan kazim
  • 938
  • 9
  • 11