Skip to content

Commit

Permalink
fix: better docs with current features
Browse files Browse the repository at this point in the history
adding more dynamic interface for debug nodes
  • Loading branch information
satanTime committed May 10, 2020
1 parent 3872591 commit c76209f
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 148 deletions.
133 changes: 67 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Or you could use this to mock them out and have the ability to assert on their i

```typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { MockComponent, MockedComponent, MockRender } from 'ng-mocks';
import { MockComponent, MockedComponent, MockHelper, MockRender } from 'ng-mocks';

import { DependencyComponent } from './dependency.component';
import { TestedComponent } from './tested.component';

Expand All @@ -47,8 +47,11 @@ describe('MockComponent', () => {
});

it('should send the correct value to the dependency component input', () => {
const mockedComponent = fixture.debugElement.query(By.css('dependency-component-selector'))
.componentInstance as DependencyComponent; // casting to retain type safety
// the same as fixture.debugElement.query(By.css('dependency-component-selector')).componentInstance
const mockedComponent = MockHelper.findOrFail<DependencyComponent>(
fixture.debugElement,
'dependency-component-selector'
).componentInstance;

// let's pretend Dependency Component (unmocked) has 'someInput' as an input
// the input value will be passed into the mocked component so you can assert on it
Expand All @@ -61,8 +64,7 @@ describe('MockComponent', () => {

it('should do something when the dependency component emits on its output', () => {
spyOn(component, 'trigger');
const mockedComponent = fixture.debugElement.query(By.directive(DependencyComponent))
.componentInstance as DependencyComponent; // casting to retain type safety
const mockedComponent = MockHelper.findOrFail(fixture.debugElement, DependencyComponent).componentInstance;

// again, let's pretend DependencyComponent has an output called 'someOutput'
// emit on the output that MockComponent setup when generating the mock of Dependency Component
Expand All @@ -78,36 +80,37 @@ describe('MockComponent', () => {
});

it('should render something inside of the dependency component', () => {
const localFixture = MockRender(`
const localFixture = MockRender<DependencyComponent>(`
<dependency-component-selector>
<p>inside content</p>
</dependency-component-selector>
`);

// because component does not have any @ContentChild we can access html directly.
// assert on some side effect
const mockedNgContent = localFixture.debugElement.query(By.directive(DependencyComponent)).nativeElement.innerHTML;
const mockedNgContent = localFixture.point.nativeElement.innerHTML;
expect(mockedNgContent).toContain('<p>inside content</p>');
});

it('should render something inside of the dependency component', () => {
const localFixture = MockRender(`
const localFixture = MockRender<MockedComponent<DependencyComponent>>(`
<dependency-component-selector>
<ng-template #something><p>inside template</p></ng-template>
<p>inside content</p>
</dependency-component-selector>
`);

// injected ng-content says as it was.
const mockedNgContent = localFixture.debugElement.query(By.directive(DependencyComponent)).nativeElement.innerHTML;
const mockedNgContent = localFixture.point.nativeElement.innerHTML;
expect(mockedNgContent).toContain('<p>inside content</p>');

// because component does have @ContentChild we need to render them first with proper context.
const mockedElement = localFixture.debugElement.query(By.directive(DependencyComponent));
const mockedComponent: MockedComponent<DependencyComponent> = mockedElement.componentInstance;
const mockedComponent = localFixture.point.componentInstance;
mockedComponent.__render('something');
localFixture.detectChanges();

const mockedNgTemplate = mockedElement.query(By.css('[data-key="something"]')).nativeElement.innerHTML;
const mockedNgTemplate = MockHelper.findOrFail(localFixture.debugElement, '[data-key="something"]').nativeElement
.innerHTML;
expect(mockedNgTemplate).toContain('<p>inside template</p>');
});
});
Expand All @@ -124,8 +127,8 @@ describe('MockComponent', () => {

```typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { MockDirective, MockHelper } from 'ng-mocks';

import { DependencyDirective } from './dependency.directive';
import { TestedComponent } from './tested.component';

Expand All @@ -149,14 +152,12 @@ describe('MockDirective', () => {

// let's pretend Dependency Directive (unmocked) has 'someInput' as an input
// the input value will be passed into the mocked directive so you can assert on it
const mockedDirectiveInstance = MockHelper.getDirective(
fixture.debugElement.query(By.css('span')),
const mockedDirectiveInstance = MockHelper.getDirectiveOrFail(
MockHelper.findOrFail(fixture.debugElement, 'span'),
DependencyDirective
);
expect(mockedDirectiveInstance).toBeTruthy();
if (mockedDirectiveInstance) {
expect(mockedDirectiveInstance.someInput).toEqual('foo');
}

expect(mockedDirectiveInstance.someInput).toEqual('foo');
// assert on some side effect
});

Expand All @@ -166,16 +167,13 @@ describe('MockDirective', () => {

// again, let's pretend DependencyDirective has an output called 'someOutput'
// emit on the output that MockDirective setup when generating the mock of Dependency Directive
const mockedDirectiveInstance = MockHelper.getDirective(
fixture.debugElement.query(By.css('span')),
const mockedDirectiveInstance = MockHelper.getDirectiveOrFail(
MockHelper.findOrFail(fixture.debugElement, 'span'),
DependencyDirective
);
expect(mockedDirectiveInstance).toBeTruthy();
if (mockedDirectiveInstance) {
mockedDirectiveInstance.someOutput.emit({
payload: 'foo',
}); // if you casted mockedDirective as the original component type then this is type safe
}
mockedDirectiveInstance.someOutput.emit({
payload: 'foo',
}); // if you casted mockedDirective as the original component type then this is type safe
// assert on some side effect
});
});
Expand All @@ -189,6 +187,7 @@ when assertions should be done on its nested elements.
```typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MockDirective, MockedDirective, MockHelper } from 'ng-mocks';

import { DependencyDirective } from './dependency.directive';
import { TestedComponent } from './tested.component';

Expand All @@ -213,7 +212,7 @@ describe('MockDirective', () => {
// IMPORTANT: by default structural directives aren't rendered.
// Because we can't automatically detect when and with which context they should be rendered.
// Usually developer knows context and can render it manually with proper setup.
const mockedDirectiveInstance = MockHelper.findDirective(
const mockedDirectiveInstance = MockHelper.findDirectiveOrFail(
fixture.debugElement,
DependencyDirective
) as MockedDirective<DependencyDirective>;
Expand All @@ -227,10 +226,7 @@ describe('MockDirective', () => {

// let's pretend Dependency Directive (unmocked) has 'someInput' as an input
// the input value will be passed into the mocked directive so you can assert on it
expect(mockedDirectiveInstance).toBeTruthy();
if (mockedDirectiveInstance) {
expect(mockedDirectiveInstance.someInput).toEqual('foo');
}
expect(mockedDirectiveInstance.someInput).toEqual('foo');
// assert on some side effect
});
});
Expand All @@ -248,8 +244,8 @@ Personally, I found the best thing to do for assertions is to override the trans

```typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { MockPipe } from 'ng-mocks';
import { MockHelper, MockPipe } from 'ng-mocks';

import { DependencyPipe } from './dependency.pipe';
import { TestedComponent } from './tested.component';

Expand All @@ -262,7 +258,7 @@ describe('MockPipe', () => {
TestedComponent,

// alternatively you can use MockPipes to mock multiple but you lose the ability to override
MockPipe(DependencyPipe, (...args) => JSON.stringify(args)),
MockPipe(DependencyPipe, (...args: string[]) => JSON.stringify(args)),
],
});

Expand All @@ -272,7 +268,8 @@ describe('MockPipe', () => {

describe('with transform override', () => {
it('should return the result of the provided transform function', () => {
expect(fixture.debugElement.query(By.css('span')).nativeElement.innerHTML).toEqual('["foo"]');
const pipeElement = MockHelper.findOrFail(fixture.debugElement, 'span');
expect(pipeElement.nativeElement.innerHTML).toEqual('["foo"]');
});
});
});
Expand All @@ -289,8 +286,8 @@ describe('MockPipe', () => {
```typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { MockComponent, MockedComponent } from 'ng-mocks';
import { MockComponent, MockedComponent, MockHelper } from 'ng-mocks';

import { DependencyComponent } from './dependency.component';
import { TestedComponent } from './tested.component';

Expand All @@ -310,8 +307,10 @@ describe('MockReactiveForms', () => {
});

it('should send the correct value to the dependency component input', () => {
const mockedReactiveFormComponent = fixture.debugElement.query(By.css('dependency-component-selector'))
.componentInstance as MockedComponent<DependencyComponent>; // casting to retain type safety
const mockedReactiveFormComponent = MockHelper.findOrFail<MockedComponent<DependencyComponent>>(
fixture.debugElement,
'dependency-component-selector'
).componentInstance;

mockedReactiveFormComponent.__simulateChange('foo');
expect(component.formControl.value).toBe('foo');
Expand All @@ -336,6 +335,7 @@ For providers I typically will use TestBed.get(SomeProvider) and extend it using
```typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MockModule } from 'ng-mocks';

import { DependencyModule } from './dependency.module';
import { TestedComponent } from './tested.component';

Expand Down Expand Up @@ -379,7 +379,6 @@ It is useful if you want to mock system tokens / services such as APP_INITIALIZE

```typescript
import { TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { MockModule, MockRender } from 'ng-mocks';

import { DependencyModule } from './dependency.module';
Expand Down Expand Up @@ -411,8 +410,7 @@ describe('MockRender', () => {
);

// assert on some side effect
const componentInstance = fixture.debugElement.query(By.directive(TestedComponent))
.componentInstance as TestedComponent;
const componentInstance = fixture.point.componentInstance as TestedComponent;
componentInstance.trigger.emit('foo1');
expect(componentInstance.value1).toEqual('something1');
expect(componentInstance.value2).toEqual('check');
Expand Down Expand Up @@ -504,28 +502,31 @@ const spySet: Spy = MockHelper.mockService(instance, propertyName, 'set');

```typescript
// The example below uses auto spy.
it('mocks getters, setters and methods in a way that jasmine can mock them w/o an issue', () => {
const mock: GetterSetterMethodHuetod = MockService(GetterSetterMethodHuetod);
expect(mock).toBeDefined();

// Creating a mock on the getter.
MockHelper.mockService<Spy>(mock, 'name', 'get').and.returnValue('mock');
expect(mock.name).toEqual('mock');

// Creating a mock on the setter.
MockHelper.mockService(mock, 'name', 'set');
mock.name = 'mock';
expect(MockHelper.mockService(mock, 'name', 'set')).toHaveBeenCalledWith('mock');

// Creating a mock on the method.
MockHelper.mockService<Spy>(mock, 'nameMethod').and.returnValue('mock');
expect(mock.nameMethod('mock')).toEqual('mock');
expect(MockHelper.mockService(mock, 'nameMethod')).toHaveBeenCalledWith('mock');

// Creating a mock on the method that doesn't exist.
MockHelper.mockService<Spy>(mock, 'fakeMethod').and.returnValue('mock');
expect((mock as any).fakeMethod('mock')).toEqual('mock');
expect(MockHelper.mockService(mock, 'fakeMethod')).toHaveBeenCalledWith('mock');
describe('MockService', () => {
it('mocks getters, setters and methods in a way that jasmine can mock them w/o an issue', () => {
// please note that auto spy should be enabled for this test.
const mock: GetterSetterMethodHuetod = MockService(GetterSetterMethodHuetod);
expect(mock).toBeDefined();

// Creating a mock on the getter.
MockHelper.mockService<jasmine.Spy>(mock, 'name', 'get').and.returnValue('mock');
expect(mock.name).toEqual('mock');

// Creating a mock on the setter.
MockHelper.mockService(mock, 'name', 'set');
mock.name = 'mock';
expect(MockHelper.mockService(mock, 'name', 'set')).toHaveBeenCalledWith('mock');

// Creating a mock on the method.
MockHelper.mockService<jasmine.Spy>(mock, 'nameMethod').and.returnValue('mock');
expect(mock.nameMethod('mock')).toEqual('mock');
expect(MockHelper.mockService(mock, 'nameMethod')).toHaveBeenCalledWith('mock');

// Creating a mock on the method that doesn't exist.
MockHelper.mockService<jasmine.Spy>(mock, 'fakeMethod').and.returnValue('mock');
expect((mock as any).fakeMethod('mock')).toEqual('mock');
expect(MockHelper.mockService(mock, 'fakeMethod')).toHaveBeenCalledWith('mock');
});
});
```

Expand Down
29 changes: 16 additions & 13 deletions examples/MockComponent/MockComponent.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { MockComponent, MockedComponent, MockRender } from 'ng-mocks';
import { MockComponent, MockedComponent, MockHelper, MockRender } from 'ng-mocks';

import { DependencyComponent } from './dependency.component';
import { TestedComponent } from './tested.component';

Expand All @@ -19,8 +19,11 @@ describe('MockComponent', () => {
});

it('should send the correct value to the dependency component input', () => {
const mockedComponent = fixture.debugElement.query(By.css('dependency-component-selector'))
.componentInstance as DependencyComponent; // casting to retain type safety
// the same as fixture.debugElement.query(By.css('dependency-component-selector')).componentInstance
const mockedComponent = MockHelper.findOrFail<DependencyComponent>(
fixture.debugElement,
'dependency-component-selector'
).componentInstance;

// let's pretend Dependency Component (unmocked) has 'someInput' as an input
// the input value will be passed into the mocked component so you can assert on it
Expand All @@ -33,8 +36,7 @@ describe('MockComponent', () => {

it('should do something when the dependency component emits on its output', () => {
spyOn(component, 'trigger');
const mockedComponent = fixture.debugElement.query(By.directive(DependencyComponent))
.componentInstance as DependencyComponent; // casting to retain type safety
const mockedComponent = MockHelper.findOrFail(fixture.debugElement, DependencyComponent).componentInstance;

// again, let's pretend DependencyComponent has an output called 'someOutput'
// emit on the output that MockComponent setup when generating the mock of Dependency Component
Expand All @@ -50,36 +52,37 @@ describe('MockComponent', () => {
});

it('should render something inside of the dependency component', () => {
const localFixture = MockRender(`
const localFixture = MockRender<DependencyComponent>(`
<dependency-component-selector>
<p>inside content</p>
</dependency-component-selector>
`);

// because component does not have any @ContentChild we can access html directly.
// assert on some side effect
const mockedNgContent = localFixture.debugElement.query(By.directive(DependencyComponent)).nativeElement.innerHTML;
const mockedNgContent = localFixture.point.nativeElement.innerHTML;
expect(mockedNgContent).toContain('<p>inside content</p>');
});

it('should render something inside of the dependency component', () => {
const localFixture = MockRender(`
const localFixture = MockRender<MockedComponent<DependencyComponent>>(`
<dependency-component-selector>
<ng-template #something><p>inside template</p></ng-template>
<p>inside content</p>
</dependency-component-selector>
`);

// injected ng-content says as it was.
const mockedNgContent = localFixture.debugElement.query(By.directive(DependencyComponent)).nativeElement.innerHTML;
const mockedNgContent = localFixture.point.nativeElement.innerHTML;
expect(mockedNgContent).toContain('<p>inside content</p>');

// because component does have @ContentChild we need to render them first with proper context.
const mockedElement = localFixture.debugElement.query(By.directive(DependencyComponent));
const mockedComponent: MockedComponent<DependencyComponent> = mockedElement.componentInstance;
const mockedComponent = localFixture.point.componentInstance;
mockedComponent.__render('something');
localFixture.detectChanges();

const mockedNgTemplate = mockedElement.query(By.css('[data-key="something"]')).nativeElement.innerHTML;
const mockedNgTemplate = MockHelper.findOrFail(localFixture.debugElement, '[data-key="something"]').nativeElement
.innerHTML;
expect(mockedNgTemplate).toContain('<p>inside template</p>');
});
});
Loading

0 comments on commit c76209f

Please sign in to comment.