Git Product home page Git Product logo

ng-mocks's Introduction

chat on gitter npm version build status coverage status

Mock components, services and more out of annoying dependencies for simplification of Angular testing

ng-mocks facilitates Angular testing and helps to:

  • mock Components, Directives, Pipes, Modules, Services and Tokens
  • reduce boilerplate in tests
  • access declarations via simple interface

The current version of the library has been tested and can be used with:

Angular ng-mocks Jasmine Jest Ivy
17 latest yes yes yes
16 latest yes yes yes
15 latest yes yes yes
14 latest yes yes yes
13 latest yes yes yes
12 latest yes yes yes
11 latest yes yes yes
10 latest yes yes yes
9 latest yes yes yes
8 latest yes yes
7 latest yes yes
6 latest yes yes
5 latest yes yes

Important links

Very short introduction

Global configuration for mocks in src/test.ts. In case of jest, src/setup-jest.ts / src/test-setup.ts should be used.

// All methods in mock declarations and providers
// will be automatically spied on their creation.
// https://ng-mocks.sudo.eu/extra/auto-spy
ngMocks.autoSpy('jasmine'); // or jest

// ngMocks.defaultMock helps to customize mocks
// globally. Therefore, we can avoid copy-pasting
// among tests.
// https://ng-mocks.sudo.eu/api/ngMocks/defaultMock
ngMocks.defaultMock(AuthService, () => ({
  isLoggedIn$: EMPTY,
  currentUser$: EMPTY,
}));

An example of a spec for a profile edit component.

// Let's imagine that there is a ProfileComponent
// and it has 3 text fields: email, firstName,
// lastName, and a user can edit them.
// In the following test suite, we would like to
// cover behavior of the component.
describe('profile:builder', () => {
  // Helps to reset customizations after each test.
  // Alternatively, you can enable
  // automatic resetting in test.ts.
  MockInstance.scope();

  // Let's configure TestBed via MockBuilder.
  // The code below says to mock everything in
  // ProfileModule except ProfileComponent and
  // ReactiveFormsModule.
  beforeEach(() => {
    // The result of MockBuilder should be returned.
    // https://ng-mocks.sudo.eu/api/MockBuilder
    return MockBuilder(
      ProfileComponent,
      ProfileModule,
    ).keep(ReactiveFormsModule);
    // // or old fashion way
    // return TestBed.configureTestingModule({
    //   imports: [
    //     MockModule(SharedModule), // mock
    //     ReactiveFormsModule, // real
    //   ],
    //   declarations: [
    //     ProfileComponent, // real
    //     MockPipe(CurrencyPipe), // mock
    //     MockDirective(HoverDirective), // mock
    //   ],
    //   providers: [
    //     MockProvider(AuthService), // mock
    //   ],
    // }).compileComponents();
  });

  // A test to ensure that ProfileComponent
  // can be created.
  it('should be created', () => {
    // MockRender is an advanced version of
    // TestBed.createComponent.
    // It respects all lifecycle hooks,
    // onPush change detection, and creates a
    // wrapper component with a template like
    // <app-root ...allInputs></profile>
    // and renders it.
    // It also respects all lifecycle hooks.
    // https://ng-mocks.sudo.eu/api/MockRender
    const fixture = MockRender(ProfileComponent);

    expect(
      fixture.point.componentInstance,
    ).toEqual(assertion.any(ProfileComponent));
  });

  // A test to ensure that the component listens
  // on ctrl+s hotkey.
  it('saves on ctrl+s hot key', () => {
    // A fake profile.
    const profile = {
      email: '[email protected]',
      firstName: 'testFirst2',
      lastName: 'testLast2',
    };

    // A spy to track save calls.
    // MockInstance helps to configure mock
    // providers, declarations and modules
    // before their initialization and usage.
    // https://ng-mocks.sudo.eu/api/MockInstance
    const spySave = MockInstance(
      StorageService,
      'save',
      jasmine.createSpy(), // or jest.fn()
    );

    // Renders <profile [profile]="params.profile">
    // </profile>.
    // https://ng-mocks.sudo.eu/api/MockRender
    const { point } = MockRender(
      ProfileComponent,
      { profile }, // bindings
    );

    // Let's change the value of the form control
    // for email addresses with a random value.
    // ngMocks.change finds a related control
    // value accessor and updates it properly.
    // https://ng-mocks.sudo.eu/api/ngMocks/change
    ngMocks.change(
      '[name=email]', // css selector
      '[email protected]', // an email address
    );

    // Let's ensure that nothing has been called.
    expect(spySave).not.toHaveBeenCalled();

    // Let's assume that there is a host listener
    // for a keyboard combination of ctrl+s,
    // and we want to trigger it.
    // ngMocks.trigger helps to emit events via
    // simple interface.
    // https://ng-mocks.sudo.eu/api/ngMocks/trigger
    ngMocks.trigger(point, 'keyup.control.s');

    // The spy should be called with the user
    // and the random email address.
    expect(spySave).toHaveBeenCalledWith({
      email: '[email protected]',
      firstName: profile.firstName,
      lastName: profile.lastName,
    });
  });
});

Profit.

Extra

If you like ng-mocks, please support it:

Thank you!

P.S. Feel free to contact us if you need help.

ng-mocks's People

Contributors

ae1ns avatar alexbjorlig avatar askarby avatar bantramfidian avatar bobbyg603 avatar dependabot[bot] avatar dji75 avatar dpraul avatar guansunyata avatar ike18t avatar jsantha avatar jsantosdk avatar kenjiqq avatar morad-m11 avatar rafehi avatar renovate-bot avatar renovate[bot] avatar rsinha9 avatar satantime avatar vespertilian avatar zachary-svoboda-accesso avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ng-mocks's Issues

Mocking internal package components

Hi,

This may be two issues, but I'm trying to solve 1 problem.
Out project is split into several repos and they all consume modules from a core repo, which is build as an Angular package.

The problem is that this core repo only exposes modules, but not components.
Therefore I cannot do MockComponent(SomeComponent).

When I try to mock the whole module, I get weird errors like this:

Failed: Cannot read property 'reference' of null
TypeError: Cannot read property 'reference' of null
at singleProviderDef (node_modules/@angular/compiler/fesm5/compiler.js:19777:1)
at providerDef (node_modules/@angular/compiler/fesm5/compiler.js:19729:1)
at ViewBuilder.push../node_modules/@angular/compiler/fesm5/compiler.js.ViewBuilder._visitProviderOrDirective (node_modules/@angular/compiler/fesm5/compiler.js:22060:1)
at ViewBuilder.push../node_modules/@angular/compiler/fesm5/compiler.js.ViewBuilder._visitDirective (node_modules/@angular/compiler/fesm5/compiler.js:21954:1)
at node_modules/@angular/compiler/fesm5/compiler.js:21879:1

I don't see anything special about this module. Some of the other modules can be mocked.

My suggestion would be to allow MockComponents to be created from selector.

MockModule: template parse errors

Hello, I have just updated library in version 7.1.0.

I mocked MatDialogModule by this way imports: [MockModule(MatDialogModule)],
But have got this error :

Error: Template parse errors:
  Unexpected character "EOF" (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.) ("tter"]();
      return _this;
    }"><template [ngTemplateOutlet]="_portalOutlet"></template></div>[ERROR ->]"): ng:///MockedModule/MockOfMatDialogContainer.html@10:69
  Invalid ICU message. Missing '}'. ("tter"]();
      return _this;
    }"><template [ngTemplateOutlet]="_portalOutlet"></template></div>[ERROR ->]"): ng:///MockedModule/MockOfMatDialogContainer.html@10:69
    at syntaxError (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:2547:1)
    at DirectiveNormalizer.push../node_modules/@angular/compiler/fesm5/compiler.js.DirectiveNormalizer._preparseLoadedTemplate (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:14102:1)
    at http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:14094:62
    at Object.then (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:2538:33)
    at DirectiveNormalizer.push../node_modules/@angular/compiler/fesm5/compiler.js.DirectiveNormalizer._preParseTemplate (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:14094:1)
    at DirectiveNormalizer.push../node_modules/@angular/compiler/fesm5/compiler.js.DirectiveNormalizer.normalizeTemplate (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:14080:1)
    at CompileMetadataResolver.push../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver.loadDirectiveMetadata (http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:17282:1)
    at http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:24906:1
    at Array.forEach (<anonymous>)
    at http://localhost:9876/node_modules/@angular/compiler/fesm5/compiler.js?:24905:1

I think, it comes from the MatDialogContainer template, as mentioned in error message.

I do not have this error if I only mock what I need:

declarations: [
  ...
  MockDirectives(MatDialogTitle, MatDialogClose),
  MockComponents(MatDialogContent, MatDialogActions),
],

An additional element is being created for ng-container

Seems like an issue introduced with #26. In v6.2.3 when I mocked an element that had ng-container elements, that element was not being render, but as v7.1.1, that element is creating a div[data-key="ng-content"] before the content of the container.

This is making my template test fail, because the template now have this additional element that is actually not available in runtime, so is a test only element.

Allow mocking an internal vs external module

My thought around this was that there would be two types of modules:

Internal module:
The module where your declaration lives. In this case we don't really care about exports, we primarily care about imports. The goal is to be able to provide a list of declarations to not mock to give it a quick mocked ecosystem. Internal module imports would all be made up of external module mocks.

External module:
This module would be more about mocking exports.

Giving this separation should resolve the issue listed here:
https://github.com/ike18t/ng-mocks/blob/master/lib/mock-module/mock-module.ts#L159

This could be accomplished by either making two separate methods or by allowing an optional options object param.

@satanTime Thoughts?

Mocking modules with entry components

I'm trying to mock a Module which contains an entry component (used for a MatDialog), but MockModule seems to erase in some way the original entryComponents definition.
Is it intended behaviour?

@NgModule({
  declarations: [MyComponent],
  entryComponents: [MyComponent]
})
class TestModule {}

// From https://github.com/NetanelBasal/spectator
const spectator = createService({
    service: MyService,
    imports: [NoopAnimationsModule, MatDialogModule, MockModule(TestModule)]
  });

I get No component factory found for MyComponent. Did you add it to @NgModule.entryComponents?

Mocking Angular Material modules do not work

I'm trying to mock Angular Material modules to avoid using NO_ERRORS_SCHEMA when doing shallow components testing, but it's not working for me.

At first, I tried mocking my material.module.ts which is just a module that re-exports all material individual modules, and it's not working at all as I get "unknown element <mat-card" and such.

Then I tried mocking all material modules individually with modules.map((module: any) => MockModule(module)); and use that in my shared.module.mock.ts, a module that I import in all my unit tests for convenience. This module imports & exports the mocked material modules.
This works better as I don't get "unknown element" errors, but this time I'm stuck on this error:

 TypeError: Cannot read property 'indexOf' of undefined
            at HammerGesturesPlugin.isCustomEvent (./node_modules/@angular/platform-browser/fesm5/platform-browser.js?:1639:102)
            at HammerGesturesPlugin.supports (./node_modules/@angular/platform-browser/fesm5/platform-browser.js?:1577:75)
            at EventManager._findPluginFor (./node_modules/@angular/platform-browser/fesm5/platform-browser.js?:911:26)
            at EventManager.addEventListener (./node_modules/@angular/platform-browser/fesm5/platform-browser.js?:882:27)
            at EmulatedEncapsulationDomRenderer2.DefaultDomRenderer2.listen (./node_modules/@angular/platform-browser/fesm5/platform-browser.js?:1190:34)

Obviously, this error is not there when I use the real material modules.

Do you have any idea or workaround for this?

Thanks!

Mocking @Input() set functions

Somewhat related to #58

Sometimes you want to have a some code in a component that you will mock like:

@Input()
set something(value: SomeType) {
   //Do something with value
}

Currently ng-mocks seems to mock these as an anonymous function, for testing it would probably be better to mock them as:

@Input
something: SomeType

Then you could at least test something like:

  let fakeValue = 'my fake value';
   //do something that results in something being set to fakeValue...

  let mockedComponent: MockedComponent<ComponentType> = debugElement.query(By.css('.qa-my-tag')).componentInstance;

  expect(mockedComponent.something).toEqual(fakeValue);

This seems easier to solve then #58

... although perhaps #58 that can be solved with something similar? Or perhaps having something like __simulateChange but adding the output you want to change... so something like __simulateGetter(param: string, value: any)

I'd be pretty happy to put together some sort of PR for the getter case as detailed above if its likely to be accepted, but I don't want to start work on it if it'll just be rejected as not the direction desired :)

Issue with Animations Library

I'm building a project using Ionic v3 and I'm trying to write tests for some code in my Tabs page. To do this I have to mock the page components.

TestBed.configureTestingModule({
      declarations: [
        TabsPage,
        MiniPlayerStubComponent,
        MockComponent(ShowsPage)
      ],

When I do this, I started getting the error: Cannot find module '@angular/animations'. I don't even use that but all my googling suggested I needed to import it. I did that and now when I run my test, I get this issue:

TypeError: Cannot read property 'annotations' of undefined.

at MockDirectiveResolver.Object.<anonymous>.DirectiveResolver.resolve (node_modules/@angular/compiler/@angular/compiler.es5.js:14349:56)
      at MockDirectiveResolver.Object.<anonymous>.MockDirectiveResolver.resolve (packages/compiler/testing/src/directive_resolver_mock.ts:49:1)
      at Object.MockComponent (node_modules/ng-mocks/lib/mock-component/mock-component.ts:24:9)

This happens when the mock component tries to mock my ShowsPage. It looks like it happens when it tries to reflect the class. I'm not sure how to get past the error at this point, especially because this animations thing is not even something I use. I didn't have to import it until I needed to try mocking this page for testing.

Any help is super appreciated!

New npm release

Hi there,

#55 fixes an important bug/problem. Can you please cut a new (bugfix-) release?

Many thanks and keep up this gread lib!

MockComponents typescript signature

Hello,
I have an issue when i trie to use MockComponents function.

MockComponents(MyComponent, MySecondComponent, MyThirdComponent),

I get :

TS2345: Argument of type 'typeof MySecondComponent' is not assignable to parameter of type 'Type<MyComponent>'.
Type 'MySecondComponent' is not assignable to type 'MyComponent'.
    Property 'xxxx' is missing in type 'MySecondComponent'.

I think the signature is not correct but I can not find anything to propose.

To workaround, i write MockComponents<any>(MapComponent, ViewComponent, LoaderComponent) but it's dirty.

Thank you for your help.

Mockcomponent cannot be used as an entry component.

I am using the ng-inline-svg library, whose module I import in my shared module. Now when I do MockModule(SharedModule), I get this error message:

Error: MockOfInlineSVGComponent cannot be used as an entry component.

I saw in a different issue, that entry components were added to version 7.8.0, and I am on version 8.1.0.

Any idea what's wrong here or how I can solve this?

@ContentChild wrapping fails with selectors that contain hyphens

When mocking a component that has @ContentChild/@ContentChildren, if the selector for the content child contains a hyphen, which is part of standard kebab casing for Angular components, running tests with this mock component will cause an error similar to the following: Object({ ngSyntaxError: true, ngParseErrors: [ "-" is not allowed in reference names ("v *ngIf="mockRender_some-component" data-key="some-component"> (assuming the selector for ContentChild is 'some-component')

@ContentChild mocking fails with html attribute selection

When mocking a component that has @ContentChild projection, the generated mock will be invalid if the @ContentChild uses an HTML attribute selector.

e.g. @ContentChild('[projectedContent]'... with <div projectedContent>This is not mockable</div>

This does not appear to be an issue when using Angular template reference selection

e.g. @ContentChild('projectedContent'... with <div #projectedContent>This is mockable</div>

When running the tests, they fail with the error TypeError: Cannot read property 'undefined' of undefined

I've created a repo to demonstrate the issue at https://github.com/joebell1329/contentProjectionDemo

When running the tests in the demo, the issue seems to be with the generated HTML in ng://DynamicTestModule/MockOfNotMockableComponent.html

The error is thrown on the line <div *ngIf="mockRender_[projectedContent]" data-key="[projectedContent]">

MockComponent does not work with ViewChild

Very neat library that's helping us find errors that were being hidden by NO_ERRORS_SCHEMA.

However, I've found that we're unable to use MockComponent with ViewChild:

@Component({
  selector: 'child',
  template: '<span>this is a child</span>'
})
export class ChildComponent {
  public someMethod() {    
  }
}

@Component({
  selector: 'parent',
  template: '<child></child>'
})
export class ParentComponent implements AfterViewInit {
  @ViewChild(ChildComponent) childComponent;

  ngAfterViewInit() {
    console.log(this.childComponent);     // this.childComponent is undefined here
    this.childComponent.someMethod();
  }
}

describe('test', () => {
  let component: ParentComponent;
  let fixture: ComponentFixture<ParentComponent>;  
  beforeEach(async(() => {        
    TestBed.configureTestingModule({
      declarations: [
        ParentComponent,
        MockComponent(ChildComponent)
      ], 
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(ParentComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('test', () => {    
  });
});

It would be great if we could:

  1. Use MockComponent in conjunction with ViewChild
  2. Spy on component functions through MockComponent

MockRender fixture.detectChanges() call

Hey, we encounter an issue when we using MockRender.
We want to check stuff in ngAfterViewInit of tested child component.
But in MockRender you call fixture.detectChanges();, it's impossible to spy what's going on inside.

It's possible to remove this or make it parametrizable ?
Thanks.

Add support to inject a library-related service mocker

The idea is to provide support ts-mockery as peerDependency and to allow user to mock everything ng-mocks mock with a specified library.

For example adding in a setup script of karma or testing framework

import 'ng-mocks/mockery/jasmine';

or

import 'ng-mocks/mockery/jest';

and in that case we use specified library to mock methods instead of setting () => {} everywhere.

Angular 4: Could not load summary for directive

Heys, so currently we are unfortunately stuck on Angular 4 for awhile so not sure if its due to the version of Angular we are using but when running a test for a custom form control component that contains child components we seem to be getting the common error:

Error: Illegal state: Could not load the summary for directive ColourComponent.error properties: Object({ ngSyntaxError: true })

We are using the testbed approach and currently use MockComponent() for the two child components the component under test (ColourComponent) has within its template:

  configureTestSuite(() => {
    TestBed.configureTestingModule({
      imports: [FormsModule, ReactiveFormsModule],
      declarations: [ColourComponent, MockComponent(IndicatorComponent), MockComponent(PanelComponent)]
    })
  });

We are intentionally wanting to avoid using the common NO_ERRORS_SCHEMA within the testbed to detect template errors thus this is omitted.

The component ts itself:

@Component({
  selector: 'app-colour',
  templateUrl: './colour.component.html',
  styleUrls: ['./colour.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: ColourComponent,
      multi: true
    }
  ]
})
export class ColourComponent extends BaseComponent {
  @Input()
  form: FormGroup;
  constructor(_injector: Injector) {
    super(_injector);
  }
}

And the template:

<div class="col-xs-12 col-sm-12 col-md-12">
    <app-panel heading="Colours"
        panelIdRef="colours_panel">
        <form [formGroup]="form">
            <div class="row pad-left-right">
                <app-indicator label="Colours"
                    idRef="colours" [required]="true"
                    formControlName="colours" optionOne="Yes"
                    optionTwo="No" [valueOne]="true" [valueTwo]="false"
                    [locked]="form.controls.locked.value"></app-indicator>
            </div>
        </form>
    </app-panel>
</div>

To my understanding this error normally occurs when child components are not declared anywhere in the testbed, its worth knowing that the child components themselves have their own child components too. Also the extended BaseComponent implements the ControlValueAccessor where we have our own implementation of the necessary functions since this is a custom form control component.

Any thoughts guys folks?
Thanks now

Update mock component to allow setting values on a parent reactive form via the component

Hi, Nice Library!

Would you be open to updating your mock component to allow Reactive Form values to be propagated out. I like to drive the reactive form, via the form element component, when I am testing so that I know that it is bound correctly.

Something like this:

Sample Class

export class MockReactiveFormsElement implements ControlValueAccessor {
  propagateChange = (_: any) => {};
  setTouched = (_: any) => {};

  writeValue(value: any) { }

  registerOnChange(fn) {
    this.propagateChange = fn;
  }

  registerOnTouched(fn) {
    this.setTouched = fn;
  }

  setDisabledState(isDisabled: boolean) {}
}

Use in your test like this:

const typeSelect: MockMdSelectComponent = fixture.debugElement.query(By.css('md-select')).componentInstance;
typeSelect.propagateChange('Ninja');
expect(component.warriorForm.value.type).toEqual('Ninja');

Maybe use __propagatesChange or some type prefix so as not to clash with a component that has that method, or it could be a subclass that has a seperate function mockReactiveFormComponent

Happy to submit a pull request if you will merge. Just let me know your preferred approach.

Cannot mock child component's DI services

Currently it's not possible to automock services inside childs as they are ignored
Steps:

Clone https://github.com/Lonli-Lokli/spectator_bug_child

Install dependencies and run
yarn test
All works
Change app.component.spec.ts:
from
ChildComponent
// MockComponent(ChildComponent)
to
// ChildComponent
MockComponent(ChildComponent)
and run yarn test
Test failed:
AppComponent › should create the app

TypeError: Cannot read property 'value' of undefined

Mocked component and host decorator

If you have 2 nested components like this

<outer>
  <inner></inner>
</outer>

And the inner component uses a host decorator to inject the outer component in it's constructor
constructor( @Host() public outer: OuterComponent ) { }

When you want to test the inner component it does not seem like it's possible to mock outer component in the tests as the component declared in the TestBed MockComponent(OuterComponent) is not found and injected into the constructor of the inner component.

Is there any way to make this work?

Is there any way to mock the service, example MockService.

Sorry, I'm a beginner with tests on the Angular and on Jasmine, I figured out a way to perform the Mock of an angular service.

class MockSalesReportService extends SalesReportService {
public getSalesForPeriod(initial: Date, final: Date) {
return null;
}

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SalesReportComponent ],
imports: [ MockModule(MzDatepickerModule) ],
providers: [ { provide: SalesReportService, useClass: MockSalesReportService} ]
})
.compileComponents();
}));

I would like to know if you have something more practical in the library ?, Based on the principle that I did not find a MockService in the packages.

Sorry for my english, I used google translate ✌️

better spy on getters / setters

with MockService we need to set spy on getters and setters correctly because if there is a readonly property it is tough to set its value in the test.

CHANGELOG.md has duplicates

Hi @ike18t,

if you check the file you can find that it has a lot of repetitions. Looks like very time the logs is generated it is adding the whole history instead of the diff.

Unfortunately, I'm not familiar with the tool generating it and can't be helpful here.

Update peer Dependencies to support Angular 7 without warnings

Currently when installed with Angular 7 npm produces three warnings:

npm WARN [email protected] requires a peer of @angular/compiler@>=5.x <=6.x but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of @angular/core@>=5.x <=6.x but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of @angular/forms@>=5.x <=6.x but none is installed. You must install peer dependencies yourself.

I did not encounter any problems when using the current version (6.2.1) with Angular 7

[Enhancement] Provide explicit meta-data

First things first, love the framework... great job!

Next...

I'm currently in a situation where I need to mock Ionic 4's Components, which are based on StencilJs. Since these are not Angular Components, but web components, it seems like ng-mocks is not able to generate EventEmitters from @Output-annotated properties.

So, I'm basically requesting a feature where instead of deriving meta-data from types, you would be able to provide them explicitly, eg.:

MockComponent(IonicSegmentButton, { outputs: ['ionSelect'] });

Documentation / Example for MockService

I'm trying to figure out how to use MockService but I don't get it. An example would be very helpful, as done for the other mocking functions.

Very useful library BTW! Shame Angular doesn't provide something like that out of the box.

Add support for mocking inherited members

It looks like the MockComponent method does not setup methods from inherited methods. The code for setting up mock methods for both MockDirective and MockComponent only gets methods from the class's prototype but doesn't traverse up the prototype chain to handle inheritance.

For example, Angular Material's MatSidenav component extends the MatDrawer component and methods defined on MatDrawer are not setup when mocking MatSidenav. This can cause some confusion when some methods on a type are setup and others are not.

Here's a small stackblitz that reproduces the error

MockModule no Directive annotation found

Hello,
I try to mock a entire module using MockModule(MyModule) function.

But I get :

Failed: No Directive annotation found on [NgClass, NgComponentOutlet, NgForOf, NgIf, NgTemplateOutlet, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgPlural, NgPluralCase]

I think CommonModule is missing somewhere. This may be a link to issue #1.
Thank you for your help.

Error being thrown when using @angular v6.1.8 & v6.1.9

Failed: Can't resolve all parameters for ApplicationModule: (?).
Error: Can't resolve all parameters for ApplicationModule: (?).
    at syntaxError (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:18194:21)
    at CompileMetadataResolver._getDependenciesMetadata (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:28083:39)
    at CompileMetadataResolver._getTypeMetadata (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27976:30)
    at CompileMetadataResolver.getNgModuleMetadata (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27844:28)
    at CompileMetadataResolver.getNgModuleSummary (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27654:39)
    at /tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27768:55
    at Array.forEach (<anonymous>)
    at CompileMetadataResolver.getNgModuleMetadata (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27756:53)
    at CompileMetadataResolver.getNgModuleSummary (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27654:39)
    at /tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27768:55
    at Array.forEach (<anonymous>)
    at CompileMetadataResolver.getNgModuleMetadata (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27756:53)
    at CompileMetadataResolver.getNgModuleSummary (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27654:39)
    at /tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27768:55
    at Array.forEach (<anonymous>)
    at CompileMetadataResolver.getNgModuleMetadata (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27756:53)
    at CompileMetadataResolver.getNgModuleSummary (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27654:39)
    at /tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27741:59
    at Array.forEach (<anonymous>)
    at CompileMetadataResolver.getNgModuleMetadata (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:27719:53)
    at JitCompiler._loadModules (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:41028:55)
    at JitCompiler._compileModuleAndAllComponents (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:41016:40)
    at JitCompiler.compileModuleAndAllComponentsAsync (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:40975:41)
    at CompilerImpl.compileModuleAndAllComponentsAsync (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:10506:35)
    at TestingCompilerImpl.compileModuleAndAllComponentsAsync (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:11305:35)
    at TestBed.compileComponents (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:9894:35)
    at Function.TestBed.compileComponents (/tmp/karma-typescript-bundle-5704DW9kFrAmtZ6Z.js:9753:71)
    at UserContext.<anonymous> (lib/mock-component/mock-component.spec.ts:56:5 <- lib/mock-component/mock-component.spec.js:55:14)
    at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:388:26)
    at AsyncTestZoneSpec.onInvoke (node_modules/zone.js/dist/async-test.js:106:39)
    at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:125:39)
    at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:387:32)
    at Zone.runGuarded (node_modules/zone.js/dist/zone.js:151:47)
    at runInTestZone (node_modules/zone.js/dist/async-test.js:234:29)
    at UserContext.<anonymous> (node_modules/zone.js/dist/async-test.js:168:17)
    at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:388:26)
    at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:128:39)
    at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:387:32)
    at Zone.run (node_modules/zone.js/dist/zone.js:138:43)
    at runInTestZone (node_modules/zone.js/dist/jasmine-patch.js:142:34)
    at UserContext.<anonymous> (node_modules/zone.js/dist/jasmine-patch.js:158:20)
    at ZoneQueueRunner.jasmine.QueueRunner.ZoneQueueRunner.execute (node_modules/zone.js/dist/jasmine-patch.js:250:42)
    at ZoneQueueRunner.jasmine.QueueRunner.ZoneQueueRunner.execute (node_modules/zone.js/dist/jasmine-patch.js:250:42)
    at ZoneDelegate.invokeTask (node_modules/zone.js/dist/zone.js:421:31)
    at Zone.runTask (node_modules/zone.js/dist/zone.js:188:47)
    at drainMicroTaskQueue (node_modules/zone.js/dist/zone.js:595:35)+

getters mocked as simple function

Hello,

I've encountered problem while mocking some angular material module (MatAutocompleteModule in my case). In my component that implements autocomplete, I have to check if panel is opened, and if so I close it. MaAutocompleteTrigger directive has a panelOpen property that returns panel's opened status, if we look at source code, we can see that this property is a getter.
Now in my spec, this.autocompleteTrigger.panelOpen is always evaluated to true, because if I log panelOpen property, I can see that it has been mocked as a function. And so else branch is never covered. I neither can do this.autocompleteTrigger.panelOpen === true, because then if branch is never covered.

The only workaround I found is:

const trigger: MatAutocompleteTrigger = fixture.debugElement.query(By.css('input').injector.get(MatAutocompleteTrigger);
Object.defineProperty(trigger, 'panelOpen', {
  get: () => true, // or false according to the case
  configurable: true,
});

Any thoughts about it?

Log-Messages from ComponentMock() on console when testing with Karma

When I run ng test --watch, I get strange Log-Messages in the console:

 10% building modules 1/1 modules 0 active(node:13084) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
16 08 2018 09:20:43.637:INFO [karma]: Karma v2.0.4 server started at http://0.0.0.0:9888/
16 08 2018 09:20:43.637:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency
16 08 2018 09:20:43.653:INFO [launcher]: Starting browser PhantomJS
16 08 2018 09:21:03.354:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket lxY65evuenL7JAXlAAAA with id 23197298
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 0 of 203 SUCCESS (0 secs / 0 secs)
LOG: 'Attempting to configure 'name' with descriptor '{"value":"MockOfGenericFormComponent"}' on object 'function ComponentMock() {
            var _this = this;
            this.registerOnChange = function (fn) { };
            this.registerOnTouched = function (fn) { };
            this.writeValue = function (value) { };
            (options.outputs || []).forEach(function (output) {
                _this[output.split(':')[0]] = new core_1.EventEmitter();
            });
LOG: 'Attempting to configure 'name' with descriptor '{"value":"MockOfGenericFormComponent"}' on object 'function ComponentMock() {
            var _this = this;
            this.registerOnChange = function (fn) { };
            this.registerOnTouched = function (fn) { };
            this.writeValue = function (value) { };
            (options.outputs || []).forEach(function (output) {
                _this[output.split(':')[0]] = new core_1.EventEmitter();
            });
        }' and got error, giving up: TypeError: Attempting to change value of a readonly property.'
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 0 of 203 SUCCESS (0 secs / 0 secs)
LOG: 'Attempting to configure 'name' with descriptor '{"value":"MockOfGenericFormComponent"}' on object 'function ComponentMock() {
            var _this = this;
            this.registerOnChange = function (fn) { };
            this.registerOnTouched = function (fn) { };
            this.writeValue = function (value) { };
            (options.outputs || []).forEach(function (output) {
                _this[output.split(':')[0]] = new core_1.EventEmitter();
            });
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 203 (skipped 202) SUCCESS (0.397 secs / 0.186 secs)
TOTAL: 1 SUCCESS
TOTAL: 1 SUCCESS
TOTAL: 1 SUCCESS

These strange logs appear on every component in our project. The spec file is just:

fdescribe('EntityEditorComponent', () => {
  let component: EntityEditorComponent;
  let fixture: ComponentFixture<EntityEditorComponent>;
  let store: Spy<ExtendedStore<any>>;

  beforeEach(async(() => {
    store = createSpyFromClass(ExtendedStore);

    // Spy Methods
    store.filterReducers.and.returnValue(Observable.empty());

    // TestBed Config
    TestBed.configureTestingModule({
      declarations: [
        EntityEditorComponent,
        MockComponent(GenericFormComponent)
      ],
      providers: [
        {provide: ExtendedStore, useValue: store}
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(EntityEditorComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

Is there a chance to get rid of them? The tests obviously run nevertheless.

Rendering mocked component ContentChildren broken after upgrade to ng9

I have a number of component tests in my project, that rely on mocking dependant components. Many of them also rely on rendering outlets in these mocked components.

Before upgrading to ng9, everything worked just fine. But now, the outlets aren't rendered anymore. I've tried debugging, and I've found that in this line https://github.com/ike18t/ng-mocks/blob/master/lib/mock-component/mock-component.ts#L118 the viewContainer is always undefined.

If I explicitly disable Ivy ("angularCompilerOptions": { "enableIvy": false } in tsconfig.spec.ts), tests pass. The problem is, I can't easily do this for all my libs.

Have you tried unit testing with Ivy enabled? I don't see it being disabled in your tsconfig

Cache break of MockComponent

Unfortunately, there's an issue with cached component because cache doesn't respect 2nd argument of MockComponent and we have the same problem as MockPipe has when we pass there different transformers.

df56334

A possible solution is to redo this feature in a proper way.

  • It can be redone with the MockBuilder as configuration parameters for the .mock method #44.
  • maybe a call of TestBed.overrideComponent on the cached mock can help there.

@askarby, could you provide a test sample how you use it in your project?

Allow __simulateChange to MockedDirective

Hello,

In my team we try to find the best way to test mocked MatInput directive in FormGroup.
We think the best way should be :

TestBed.configureTestingModule({
  declarations: [
    // ...
      MockDirectives(MatInput),
  ],
  imports: [
    // ...
  ],
});

it('should ...', () => {
   const mockedDirectiveInstance = MockHelper.getDirective(
      fixture.debugElement.query(By.css('input')),
      MatInput,
    );
  mockedDirectiveInstance.__simulateChange('blabla');
  tCtx.detectChanges();
});

Could you please implement __simulateChange to directive or indicate us the best way to achieve this.
Thank in advance.

Add value to MockComponent

Expected behaviour:
Values written into MockComponent using writeValue should be stored inside custom components for verification of binding in unit tests.

Creating component wrappers

When testing onpush component to simulate change detection you need to wrap the component with another one and most of the cases have the same input/outputs. So i propose to provide a method, called WrapComponent which mocks a component with the same inputs and outputs of the original one and also wire up the outputs. The template should contain the component we want to wrap and would bind all properties from the wrapper to the inner component.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.