Repository: angular
Issue: Feature Request: Automatic Mock Components
Labels: feature,area: testing,freq2: medium,feature: under consideration

# The Problem

In unit testing components, one has to declare all child components (and their children, recursively) and directives in the call to `TestBed.configureTestingModule()`. For simple components which do not have many child components, this is okay.

The problem occurs when testing a component which has many descendant components. Consider the following component dependency graph:

![angular-automock-feature-request](https://user-images.githubusercontent.com/6275952/37463701-5ce8fa52-2856-11e8-9a88-08f7c831eb5e.png)

If I want to write a unit test for `ComponentUnderTest`, I'll have to declare my testing module like this:

```TypeScript
TestBed.configureTestingModule({
  declarations: [
    ComponentUnderTest,
    ListComponent,
    ListItemComponent,
    ButtonComponent,
    InboxComponent,
    ModalComponent,
    ModalHeaderComponent,
    ModalFooterComponent
  ],
  providers: [
    MessageService,
    OverlayService
  ]
})
```
This is already getting out of hand, and in reality things can get much more involved (the transitive dependencies of the services, for example).

# Current Solutions

Generally when I run into this I do one of 2 things, both of which are unsatisfactory:

1. Say "sod it!" and  use `NO_ERRORS_SCHEMA`. 
2. Manually mock the first-order dependencies (`ListComponent` and `InboxComponent` in the above example)

The first solution is not ideal because it renders the tests brittle - e.g. if you rename an input of `InboxComponent` but forget to update the template in the component under test, this will not be picked up.

The second solution can become a pain since you also need to set up all Inputs, Outputs and methods which are being used by the component under test. This leads to inflated spec files, repetition and more brittle tests once again.

# Proposal: Automatic Mocking

A solution which is both developer-friendly and robust would be to expose a feature (a function or decorator from `@angular/core/testing`) which takes a component class and returns a mock class with the correct selector, Inputs, Outputs and stubbed methods. 

Here is how it might be used:

```TypeScript
import {TestBed, mockComponent} from '@angular/core/testing';

class MockListComponent extends mockComponent(ListComponent) {}
class MockInboxComponent extends mockComponent(InboxComponent) {}

describe('ComponentUnderTest', () => {

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        ComponentUnderTest,
        MockListComponent,
        MockInboxComponent
      ]
    });
  });

  it('does stuff with InboxComponent', () => {
    const fixture = TestBed.createComponent(ComponentUnderTest);
    const inboxComponent: MockInboxComponent = fixture.debugElement.query(By.directive(MockInboxComponent)).componentInstance;

    // We can now make assertions on the methods of InboxComponent 
    expect(inboxComponent.someMethod).not.toHaveBeenCalled();
    
    // We can also emit events from InboxComponent and test that ComponentUnderTest
    // reacts to them in the expected way.
    inboxComponent.someEvent.emit('foo');
  });
]);
```

## Advantages

1. No longer need to declare the entire dependency graph, only the first-order dependencies need to be mocked.
2. The mocks created by `mockComponent()` would be type-safe, so you get auto-completion on public properties (inputs, outputs, public methods).
3. The mocks are generated from the real component, so e.g. changes to an input of `InboxComponent` would raise errors when trying to run the test if the template of `ComponentUnderTest`. This mitigates potential brittleness introduced by hand-coded mock component.
4. Much less boilerplate code than either declaring all dependencies or manually mocking.

## Implementation

I wrote a proof-of-concept implementation which can be found here: https://gist.github.com/michaelbromley/bb4291200c25196507d12d2fd948a13e

I also discovered an independently-developed solution which does essentially the same thing: https://github.com/ike18t/mock_component

## Limitations

There are 2 main issues I am aware of with the above implementations:

1. They rely on internal data structures for reading the annotations and property metadata from the target component (e.g. my implementation reads the selector like this: `const selector = (original as any).__annotations__[0].selector;` . This makes the solution unstable and liable to break with changes to Angular internals.
2. I could not yet get this to work when the `ComponentUnderTest` uses a query of the type  `@ViewChild(InboxComponent)`. I'm sure there must be a way to get this to work with more digging.

## Proposal

Due to limitation 1 above, there seems no way to implement such functionality *safely* in user-land. So I would like to see Angular ship with a built-in helper which implements something like `mockComponent()` in a safe, forward-compatible way.

---

Thanks for your time!