GithubHelp home page GithubHelp logo

hirezio / auto-spies Goto Github PK

View Code? Open in Web Editor NEW
178.0 7.0 30.0 1.85 MB

Create automatic spies from classes

License: MIT License

TypeScript 98.50% JavaScript 1.39% Shell 0.11%
spy auto-spy jasmine-tests typescript jest jasmine javascript testing javascript-testing typescript-test typescript-testing angular-testing auto-spies

auto-spies's Introduction

auto-spies monorepo

All Contributors

Easy and type safe way to write spies for tests, for both sync and async (promises, Observables) returning methods.

This is the monorepo of:

Project Status Description
jasmine-auto-spies jasmine-auto-spies-status Easy and type safe way to write spies for jasmine tests
jest-auto-spies jest-auto-spies-status Easy and type safe way to write spies for jest tests


Contributing

Want to contribute? Yayy! 🎉

Please read and follow our Contributing Guidelines to learn what are the right steps to take before contributing your time, effort and code.

Thanks 🙏

Code Of Conduct

Be kind to each other and please read our code of conduct.

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Shai Reznik
Shai Reznik

💻 📖 🤔 🚇 🚧 🧑‍🏫 👀 ⚠️
Bnaya Peretz
Bnaya Peretz

💻 🤔 🔧
shuebner
shuebner

💻 🤔 ⚠️
Meksi
Meksi

💻 ⚠️
Taylor Ben
Taylor Ben

🤔
Yonatan Kra
Yonatan Kra

💻
Martin Baum
Martin Baum

💻 ⚠️
Guille Eneas Timón Grau
Guille Eneas Timón Grau

💻 ⚠️
Rainer Hahnekamp
Rainer Hahnekamp

🚧
WynieCronje
WynieCronje

💻 🐛 🚧
Johannes Gest
Johannes Gest

🚧
mopinsk
mopinsk

🚧
strawberry-choco
strawberry-choco

🚧
Jan Philipp Gölz
Jan Philipp Gölz

🚧

This project follows the all-contributors specification. Contributions of any kind welcome!

License

MIT

Want to learn more?

auto-spies's People

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

auto-spies's Issues

createSpyFromClass Broken with Typescript version 3.4.3 and Angular version 8.1.1

I keep getting errors like cannot read property calls of undefined and similar. Upon inspection of the code when I try to view the object created from createSpyFromClass I see that it is just an empty object.

package.json below:

{
  "dependencies": {
    "@angular/animations": "^8.1.1",
    "@angular/cdk": "^8.1.0",
    "@angular/common": "~8.1.1",
    "@angular/compiler": "~8.1.1",
    "@angular/core": "~8.1.1",
    "@angular/forms": "~8.1.1",
    "@angular/material": "^8.1.0",
    "@angular/platform-browser": "~8.1.1",
    "@angular/platform-browser-dynamic": "~8.1.1",
    "@angular/router": "~8.1.1",
    "@ng-bootstrap/ng-bootstrap": "^5.0.0",
    "bootstrap": "^4.3.1",
    "font-awesome": "^4.7.0",
    "jasmine-auto-spies": "^5.0.0",
    "ng-mocks": "^8.0.0",
    "ngx-toastr": "^10.0.4",
    "rxjs": "~6.4.0",
    "tslib": "^1.9.0",
    "zone.js": "~0.9.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.801.1",
    "@angular/cli": "~8.1.1",
    "@angular/compiler-cli": "~8.1.1",
    "@angular/language-service": "~8.1.1",
    "@types/jasmine": "~3.3.8",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "^8.9.5",
    "codelyzer": "^5.0.0",
    "jasmine-core": "~3.4.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.1.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "~2.0.1",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.4.0",
    "protractor": "~5.4.0",
    "ts-node": "~7.0.0",
    "tslint": "~5.15.0",
    "typescript": "~3.4.3"
  }
}

Type-safe method names for createSpyForClass

Hi,
I am using your library in a project and noticed the following:
the parameters for the names of methods returning Observables and Promises are just typed string[] which is error-prone.
To make full use of TypeScript I suggest typing them in a way that lets the user autocomplete the valid method names and cause compiler errors when methods are renamed without the created spies being updated.

I took the liberty of creating a PR for this.

The git precommit hook failed, though, which is why I commited without verification. So please check for any conventions I ignored.

Spying on Subject example in documentation service can't be imported

I am using jasmine-auto-spies v 6.9.x

I wrote a simple service

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class SecurityService {
  codeChange: Subject<number> = new Subject<number>();
  constructor() { }
}

Then I created a Test for a component that consumes the service: This creates the following error

TypeError: Cannot read property 'subscribe' of undefined
at OverlayComponent.initSecurityCodeChange (projects/my/src/app/security/overlay/overlay.component.ts:21:8)
at OverlayComponent.ngOnInit (projects/my/src/app/security/overlay/overlay.component.ts:16:10)
at callHook (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:2486:1)
at callHooks (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:2457:1)
at executeInitAndCheckHooks (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:2408:1)
at refreshView (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:9207:1)
at renderComponentOrTemplate (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:9306:1)
at tickRootContext (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:10532:1)
at detectChangesInRootView (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:10557:1)
at RootViewRef.detectChanges (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:22569:1)

The test is as follows:

  let securitySpy: Spy<SecurityService>;
  Given(() => {
    securitySpy = createSpyFromClass(SecurityService, {
      observablePropsToSpyOn: ['codeChange']
    });
    component = new OverlayComponent(securitySpy);
  });
  describe('METHOD:  ngOnInit()', () => {
    Given(() => {
      component.ngOnInit();
    });
    When(() => {
      securitySpy.codeChange.nextWith(5);
    });
    Then(() => {
      expect(component.componentCode).toEqual(5)
    });
  });
});

@Injectable({
  providedIn: 'root'
})
class SecurityService {
  codeChange = new Subject<number>();
}

When I move the service code to the test file the test passes. any thoughts?

Angular 8

Would it be possible to get the peer dependencies updated if Typescript 3.x is supported so we can lose the peer dependency warnings on Angular 8?

Appears to work fine in the projects I have tried it with Angular 8 / Typescript 3.4.5.

Cannot read property '_isRejectedPromise' of null

When using calledWith, i get the error:

image

Spec file:

import { HttpClient } from '@angular/common/http';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { async, TestBed } from '@angular/core/testing';
import { StorageType } from 'app/models/storage-types';
import { StorageService } from 'app/shared/services/storage.service';
import { de } from 'assets/i18n/de';
import { en } from 'assets/i18n/en';
import { createSpyFromClass, Spy } from 'jasmine-auto-spies';
import { TranslateTestingModule } from 'ngx-translate-testing';
import { SmsRecipientsCostHelperService } from '../sms-recipients-cost-dialog/sms-recipients-cost-helper.service';

const SmsRecipientsCostDialogStorageKey = 'sms-recipients-cost-dialog';

describe('SmsRecipientsCostHelperService', () => {
    let serviceUnderTest: SmsRecipientsCostHelperService;

    let storageServiceSpy: Spy<StorageService>;

    const mocksInjected: Array<{ provide: any, useValue: any }> = [
        { provide: StorageService, useValue: createSpyFromClass(StorageService) },
    ];

    const configureTestBed = () => {
        TestBed.configureTestingModule({
            imports: [
                TranslateTestingModule.withTranslations(
                    { en: en, de: de }
                )
            ],
            providers: [...mocksInjected, SmsRecipientsCostHelperService],
            schemas: [NO_ERRORS_SCHEMA]
        })
            .compileComponents();
    };

    const setupDefaultMockBehaviour = () => {
    };

    const setFixtureAndComponentUnderTest = () => {
        serviceUnderTest = TestBed.inject(SmsRecipientsCostHelperService);
    };

    describe('when it gets initialized', () => {
        beforeEach(async(configureTestBed));
        beforeEach(setupDefaultMockBehaviour);
        beforeEach(setFixtureAndComponentUnderTest);

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

    describe('when local storage is empty', () => {
        beforeEach(async(configureTestBed));
        beforeEach(setupDefaultMockBehaviour);
        beforeEach(setFixtureAndComponentUnderTest);

        beforeEach(() => {
            storageServiceSpy = TestBed.inject<any>(StorageService);

            storageServiceSpy
                .getValue
                .calledWith(SmsRecipientsCostDialogStorageKey, StorageType.Local)
                .returnValue(null);
        });

        it('#isCostDialogNeededToBeShown should return true', () => {
            expect(serviceUnderTest.isCostDialogNeededToBeShown()).toEqual(true);
        });
    });

});

Service to test

import { Injectable } from '@angular/core';
import { StorageType } from '../../models/storage-types';
@Injectable({
  providedIn: 'root'
})
export class StorageService {
  constructor() { }

...

  public getValue(
    key: string,
    storageType: StorageType = StorageType.Session
  ): string | null {
    if (storageType === StorageType.Local) {
      return window.localStorage.getItem(key);
    } else {
      if (this.isSessionStorageSupport) {
        return window.sessionStorage.getItem(key);
      } else {
        return window.localStorage.getItem(key);
      }
    }
  }
}

As soon as I replace .calledWith(SmsRecipientsCostDialogStorageKey, StorageType.Local) with a simple and it works...

What am I doing wrong?

Working tests fail after upgrade to Angular 13 with strict typing

Describe the bug

Error: projects/admin/src/app/services/event-runner.service.spec.ts:178:33 - error TS2339: Property 'nextWith' does not exist on type 'SpyAnd | (SpyAnd & AddPromiseSpyMethods) | (SpyAnd & AddObservableSpyMethods)'.
Property 'nextWith' does not exist on type 'SpyAnd'.

178 apiServiceSpy.fetch.and.nextWith(fakeResponse);

To Reproduce
Sample code :

const setPayload = (payload:any) => {
      const fakeResponse = payload;
      apiServiceSpy.fetch.and.nextWith(fakeResponse);
    };

Expected behavior
This test sample ran under 11. As a work around, I have blanked it out. In doing so, I have lost most of my existing tests

add support for non calls

Is your feature request related to a problem? Please describe.
When configuring mustBeCalledWith and not calling the method, there isn't any exception being thrown.

Describe the solution you'd like
I'd like it to behave as toHaveBeenCalledWith
Maybe add a toHaveBeenCalled somehow in a defered way.

Describe alternatives you've considered
I tried adding toHaveBeenCalled to the mustBeCalledWith function but it returned false positives for other use cases

jasmine-auto-spies@>=6.9.2 breaks the test suite in Internet Explorer

Describe the bug
Starting in version 6.9.2 of jasmine-auto-spies I receive an error in Internet Explorer (see screenshot 1) that causes the test suite to break.

If I downgrade the library to 6.9.1 the tests will pass as expected.

Version:

  • <=6.9.1 works fine in IE and other browsers
  • >=6.9.2 works fine in non-IE browsers.

To Reproduce
Steps to reproduce the behavior:

  1. Upgrade jasmine-auto-spies to >=6.9.2.
  2. Use jasmine-auto-spies in a test. The readme's addTwoNumbers example (snippet below) will exhibits the same behavior.
  3. Run your tests in Internet Explorer.
  4. Open the console and see the console error.

Example test:

describe('Test Spec', () => {
  function addTwoNumbers(a: number, b: number): number {
    return a + b;
  }
  const functionSpy = createFunctionSpy<typeof addTwoNumbers>('addTwoNumbers');

  it('should be defined', () => {
    expect(functionSpy).toBeDefined();
  });
});

Expected behavior
I expected the tests to work across all browsers.

Screenshots
Screenshot 1
image

Desktop:

  • OS: Windows 10
  • Browser: Internet Explorer
  • Version: 11

Additional context
6.9.1 vs 6.9.2 changeset

  • I noticed code-stringify was added in the 6.9.2 release which is also the same portion that the bundle blows up at in Screenshot 1.
  • image

Spying on observable property getter is failing

Describe the bug
While following the example at https://www.npmjs.com/package/jasmine-auto-spies?activeTab=readme#-spying-on-observable-properties, I did created a spy for the userData$ observable property of the OidcSecurityService of angular-auth-oidc-client.

I set up the spy like the example and attempted to set up the nextWith and got the error nextWith is not a function

I have stepped thru the code and at https://github.com/hirezio/auto-spies/blob/master/packages/auto-spies-core/src/create-auto-spy-from-class.ts line 74 before the methodNames are iterated, the spy behaves correctly and it begins to fail after the method names are iterated and it appears to be caused because the observable property, userData$ is contained in the methodNames variable and so it is overwriting the property set up that occurred on line 68:
autoSpy[observablePropName] = createObservablePropSpy();

To Reproduce
Steps to reproduce the behavior:

  1. Create a class that depends on OidcSecurityService from angular-auth-oidc-client
  2. Create a jasmine unit test for the class that setups the spy like so:
    const oidcSecurityServiceSpy = createSpyFromClass(OidcSecurityService, {
      observablePropsToSpyOn: ['userData$']
    });
    oidcSecurityServiceSpy.userData$.nextWith({
      preferred_username: 'fakeUser'
    });
  1. Run the test and see the error "oidcSecurityServiceSpy.userData$.nextWith is not a function''

Expected behavior
No error when try to assign nextWith result

Desktop (please complete the following information):

  • win 10
  • Browser -chrome
  • Version: 87.0.4280.88 (Official Build) (64-bit)
  • jasmine auto spies: tried with 6.6.0 and 6.9.1

Additional context
My workaround is to re-assign the prop spy after the call to createSpyFromClass by re-using the same code that is used to set it previously on line 68:
autoSpy[observablePropName] = createObservablePropSpy();

It seems the problem lies in the population of methodNames. Using Object.getOwnPropertyNames will retrieve these observable property getters along with all the function properties. Instead you could use getOwnPropertyDescriptors and use that to only retrieve the functions or else find the ones that are getters so they can be removed.

Bump Jest Version Supported to Jest v28

Is your feature request related to a problem? Please describe.
Dependency on Jest < 28.x.

Describe the solution you'd like
Update the dependencies to allow for Jest v28.x.

Describe alternatives you've considered
Downgrading Jest to v27, but package appears to work under v28.

Can't compile and run code that uses "resolveWith" function.

Describe the bug
VSCode does not recognize the resolveWith function mentioned in the docs:

To Reproduce
Steps to reproduce the behavior:

  1. Try using the code in the Readme file:
let storeSpy: Spy<Store>;
storeSpy = createSpyFromClass(Store);
storeSpy.selectSnapshot.and.resolveWith({});

Expected behavior
Code should compile and work

Screenshots
image

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: desktop
  • OS: Windows 10
  • Browser N/A
  • Version N/A

Additional context
Using angular 9

Must be called with should support specifying only some parameters.

When trying to test a HttpClient Spy I would like to use e.g:
httpSpy.get.mustBeCalledWith(MyBasePath + 'api/myEndpoint', $$$AnyOptions$$$)

$$$AnyOptions$$$ can be any value. (Headers, Creadentials, ReportProgress etc.)
If I use:
httpSpy.get.mustBeCalledWith(MyBasePath + 'api/myEndpoint')
It gives me an error because my service has default values for the options which are used when I do not specify options.

To Spy on Subjects

When I go to spy on a subject such as in this case

https://stackblitz.com/edit/subjecttest?file=src%2Fapp%2Fapp.component.spec.ts

I get an error

Error: An error was thrown in When(): selectMember@https://subjecttest.stackblitz.io/~/src/app/app.component.ts:24:49 @https://subjecttest.stackblitz.io/~/src/app/app.component.spec.ts:29:23 promisify@https://subjecttest.stackblitz.io/turbo_modules/@hirez_io/[email protected]/dist/jasmine-given.js:37:33 itCallbackInsideOfThen/</_temp<@https://subjecttest.stackblitz.io/turbo_modules/@hirez_io/[email protected]/dist/jasmine-given.js:283:36 _catch@https://subjecttest.stackblitz.io/turbo_modules/@hirez_io/[email protected]/dist/jasmine-given.js:4:20 itCallbackInsideOfThen/<@https://subjecttest.stackblitz.io/turbo_modules/@hirez_io/[email protected]/dist/jasmine-given.js:282:29 _cycle2@https://subjecttest.stackblitz.io/turbo_modules/@hirez_io/[email protected]/dist/jasmine-given.js:164:22 _forOf@https://subjecttest.stackblitz.io/turbo_modules/@hirez_io/[email protected]/dist/jasmine-given.js:191:7 itCallbackInsideOfThen@https://subjecttest.stackblitz.io/turbo_modules/@hirez_io/[email protected]/dist/jasmine-given.js:277:32 ZoneDelegate.prototype.invoke@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:400:30 ProxyZoneSpec.prototype.onInvoke@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/proxy.js:126:43 ZoneDelegate.prototype.invoke@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:399:56 Zone.prototype.run@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:160:47 runInTestZone@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/jasmine-patch.js:204:38 wrapTestInZone/<@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/jasmine-patch.js:219:24 attempt@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/lib/jasmine-core/jasmine.js:7063:44 QueueRunner.prototype.run@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/lib/jasmine-core/jasmine.js:7104:25 runNext@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/lib/jasmine-core/jasmine.js:7023:18 next@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/lib/jasmine-core/jasmine.js:7030:11 once/<@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/lib/jasmine-core/jasmine.js:6924:11 ZoneDelegate.prototype.invoke@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:400:30 Zone.prototype.run@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:160:47 scheduleResolveOrReject/<@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:1318:38 ZoneDelegate.prototype.invokeTask@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:434:35 Zone.prototype.runTask@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:205:51 drainMicroTaskQueue@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:620:39 ZoneTask.invokeTask@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:520:25 ZoneTask/this.invoke@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:505:52 scheduleTask/data.args[0]@https://subjecttest.stackblitz.io/turbo_modules/[email protected]/dist/zone.js:3063:36 u/<@https://c.staticblitz.com/d/webcontainer.3a09fea6d6af8fa50aecb2140d2b6d2039950e89.js:15:173530 u@https://c.staticblitz.com/d/webcontainer.3a09fea6d6af8fa50aecb2140d2b6d2039950e89.js:15:173640 r@https://c.staticblitz.com/d/webcontainer.3a09fea6d6af8fa50aecb2140d2b6d2039950e89.js:15:172591 in https://c.staticblitz.com/d/webcontainer.3a09fea6d6af8fa50aecb2140d2b6d2039950e89.js line 15 > eval (line 326)

So what I am looking for is a SpyOnSuject Function. that would allow me to see if "next" has been called.

my current work around is to mock the service.

Code inside async method with 'await [...].toPromise' stops execution

Describe the bug
When .toPromise is called on a spied on method with a .nextWith the code stops execution

To Reproduce
Steps to reproduce the behavior:

  1. In the test call nextWith on a spied on method
  2. The spied on method is called with as await <<spy>>.<<method>>.toPromise() inside an async method
  3. Step through
  4. The next line in the method never gets hit and no errors are caught

Expected behavior
When called as toPromise() the observable should be cast as a Promise and when awaited should return the current value. Then jest debugger should not stop execution of the method and continue stepping through the code on the next line.

Screenshots
Screen Shot 2022-07-14 at 10 21 36 AM

Desktop (please complete the following information):

  • OS: OSX Mojave
  • Browser Jest Test Runner running in VS Code
  • Version

Additional context
Package json version info:

"@angular/core": "13.2.2",
"jest-auto-spies": "^1.6.6",

Add function name to "mustBeCalledWith" errors

Is your feature request related to a problem? Please describe.
When you have 2 different mustBeCalledWith error messages in the same spec it's unclear which error belong to which function

Describe the solution you'd like
Adding the function name to the error message will solve this.

Observable spy is always undefined

Describe the bug
A service with a method returning an Observable is always 'undefined' when testing it with jest-auto-spies (same test passes with a jasmine-auto-spies configuration).

To Reproduce
Steps to reproduce the behavior:

  1. Create an Injectable service (let's say DummyService) with a method returning an Observable
  2. Inject this service in AppComponent and consume the service method by subscribing to its result
  3. Test AppComponent (providing an AutoSpy for DummyService) only checking that componentUnderTest is truthy
  4. Two errors are raised An error was thrown in Given(): Cannot read property 'subscribe' of undefined and An error was thrown in Then(): expect(received).toBeTruthy() Received: undefined

Expected behavior
DummyService is injected in TestBed thus having a value and the test passes

Additional context

DummyService

import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay, map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class DummyService {

  getDummyData(): Observable<{ id: number, label: string }[]> {
    return of(null).pipe(delay(500)).pipe(map(() => {
      return [
        { id: 1, label: 'some data' },
        { id: 2, label: 'some other data' }
      ];
    }));
  }
}

AppComponent

import { Component } from '@angular/core';
import { DummyService } from './services/dummy.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  dummyData: any;

  constructor(
    private dummyService: DummyService
  ) {
    this.initDummyData();
  }

  private initDummyData(): void {
    this.dummyService.getDummyData().subscribe(res => {
      this.dummyData = res;
    });
  }
}

AppComponent spec

import { TestBed } from '@angular/core/testing';
import { provideAutoSpy, Spy } from 'jest-auto-spies';
import { AppComponent } from './app.component';
import { DummyService } from './services/dummy.service';

describe(`AppComponent`, () => {
  let componentUnderTest: AppComponent;
  let dummyServiceSpy: Spy<DummyService>;

  Given(() => {
    TestBed.configureTestingModule({
      providers: [
        AppComponent,
        provideAutoSpy(DummyService)
      ]
    });

    componentUnderTest = TestBed.inject(AppComponent);
    dummyServiceSpy = TestBed.inject<any>(DummyService);
  });

  describe('EVENT: init', () => {
    Then(() => {
      expect(componentUnderTest).toBeTruthy();
    });
  });
});

Result Error

 PASS  src/app/services/dummy.service.spec.ts
 FAIL  src/app/app.component.spec.ts
  ● AppComponent › EVENT: init › 

    An error was thrown in Given(): 
      TypeError: Cannot read property 'subscribe' of undefined

      19 |
      20 |   private initDummyData(): void {
    > 21 |     this.dummyService.getDummyData().subscribe(res => {
         |     ^
      22 |       this.dummyData = res;
      23 |     });
      24 |   }

      at AppComponent.initDummyData (src/app/app.component.ts:21:5)
      at new AppComponent (src/app/app.component.ts:17:10)
      at Object.AppComponent_Factory [as factory] (ng:/AppComponent/ɵfac.js:5:10)
      at R3Injector.Object.<anonymous>.R3Injector.hydrate (../packages/core/src/di/r3_injector.ts:412:29)
      at R3Injector.Object.<anonymous>.R3Injector.get (../packages/core/src/di/r3_injector.ts:209:23)
      at NgModuleRef$1.Object.<anonymous>.NgModuleRef$1.get (../packages/core/src/render3/ng_module_ref.ts:76:29)
      at TestBedRender3.Object.<anonymous>.TestBedRender3.inject (../packages/core/testing/src/r3_test_bed.ts:278:48)
      at Function.Object.<anonymous>.TestBedRender3.inject (../packages/core/testing/src/r3_test_bed.ts:167:33)
      at Given (src/app/app.component.spec.ts:18:34)

  ● AppComponent › EVENT: init › 

    An error was thrown in Then(): 
      Error: expect(received).toBeTruthy()

    Received: undefined

      22 |   describe('EVENT: init', () => {
      23 |     Then(() => {
    > 24 |       expect(componentUnderTest).toBeTruthy();
         |                                  ^
      25 |     });
      26 |   });
      27 |

      at Then (src/app/app.component.spec.ts:24:34)

accessorSpies in jest-auto-spies + nx workspace not mocking return value

Describe the bug
When creating a service with a getter and trying to mock it in a test the returned value is undefined.

To Reproduce
Steps to reproduce the behavior:

  1. npx create-nx-workspace
  2. cd to your project
  3. nx g s --name=one --project=app --skipTests
  4. then check the changes in this commit
  5. ng test --watch

and boom

 FAIL  apps/app/src/app/app.component.spec.ts
  ● AppComponent › whatever

    expect(received).toStrictEqual(expected) // deep equality

    Expected: {"fake": "value"}
    Received: undefined

      32 |     const value = componentUnderTest.getGetter();
      33 |
    > 34 |     expect(value).toStrictEqual({
         |                   ^
      35 |       fake: 'value'
      36 |     });
      37 |   });

      at src/app/app.component.spec.ts:34:19
      at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (../../node_modules/zone.js/dist/zone.js:386:30)
      at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (../../node_modules/zone.js/dist/proxy.js:117:43)
      at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (../../node_modules/zone.js/dist/zone.js:385:36)
      at Zone.Object.<anonymous>.Zone.run (../../node_modules/zone.js/dist/zone.js:143:47)

Test Suites: 1 failed, 1 passed, 2 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        3.522s

Expected behavior
the test should pass

Desktop (please complete the following information):

  • OS: Mac OSX
  • Browser chrome
  • Version 88.0.4324.146

You can also clone this repo where the issue occurs https://github.com/GuilleEneas/jest-auto-spies-issue

add support for "bufferedNextWith" and "bufferedNextOneTimeWith

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Readme should explain benefits over jasmine.createSpyObj

I came here from a post by the maintainer on an ngx-translate issue. I came here looking for an explanation of the concern in the title.

I still don't see what benefits this has, other than perhaps mocking all properties by default (not actually sure).

In any event, please improve the documentation to cover items such as this.

auto detect getters/setters just like methods

Hi I ran into the issue where I have a getter on my component that returns an observable, but getters are not automatically picked up like methods are. So I was wondering if it would be doable to also add auto detection for getters. I also state setters in the title just to be sure, but I cannot imagine why you would need to incept a setter.

I am also wondering if https://github.com/hirezio/auto-spies/blob/e433d8d3d012c8c38dfec53b13487214b47322fa/packages/auto-spies-core/src/create-auto-spy-from-class.ts#L95C1-L104C2 is possibly bugged, based on reading the code I'd think this function returns all properties from a class, not just methods, wouldn't you need to check if the descriptor's value is of type function?

I don't mind making a PR for this, but I first wanted to open a feature request to discuss this. As you might very well have a good reason for not including them in the first place

createSpyFromClass for chaining functions or provide example for that

I'm using firestore for my angular app and my service looks like this:

@Injectable({
  providedIn: 'root',
})
export class Service {
  private readonly plansRef: AngularFirestoreCollection<any>;

  constructor(private readonly db: AngularFirestore) {
    this.ref = db.collection('/path');
  }

  addPlan(newObject: any): Promise<DocumentReference> {
    return this.ref.add(newObject);
  }

  deletePlan(id: Id): Promise<void> {
    return this.ref.doc(id).delete();
  }

  updatePlan(id: Id, updatedObject: any): Promise<void> {
    return this.ref.doc(id).update(updatedObject);
  }
}

So I should spy somehow chained functions like:
collection -> add
collection -> doc -> delete
collection -> doc -> update

I did not find nice solution for this case and it would be awesome if I can use a solution like this:

mockAngularFirestore = createSpyFromClass(AngularFirestore, ['collection', 'collection.add', 'collection.add.doc', ....]);

Now I can use only complex stubs.

If there is a nice solution that I did not find yet it is very welcome.
Thanks

Creating a spy from class with Testbed.inject does give a ts error

I used the jasmine-auto-spies in the past with Angular 8. After updating to Angular 9 (and also 10.0.2) the Testbed function Testbed.get() got deprecated and it was recommended to use Testbed.inject which hasn't effect for my tests that I wrote.

However it does the effect the use of this tool.

A simple setup:

describe('AppComponent', () => {
  let component;
  let serviceSpy: Spy<AppService>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        AppComponent,
        { provide: AppService, useValue: createSpyFromClass(AppService) },
      ]
    });

    component = TestBed.inject(AppComponent);
    serviceSpy = TestBed.inject(AppService);
  });

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

The AppService has just an empty method and attribute.

The problematic line is serviceSpy = TestBed.inject(AppService); which for some reasons throws a ts error

Type 'AppService' is not assignable to type 'Spy<AppService>'.
  Types of property 'someMethod' are incompatible.
    Type '() => string' is not assignable to type 'AddSpyOnFunction<() => string>'.
      Type '() => string' is missing the following properties from type 'SpyMethod': calledWith, mustBeCalledWithts(2322)

Can you update the library to work with the newer Angular?

Support for RxJs 7

Is it possible to set the peerDependencies of RxJs to > 6? I am currently using it successfully with RxJs 7.4 but always get a warning when running npm install.

add 'nextWith' when testing Getters which are returning an Observable

Is your feature request related to a problem? Please describe.
I have a private BehaviorSubject in a service, which I am returning as an Observable. Something like:
image

I followed the approach to test a getter, but my IDE did not display the usual functions for testing Observables (e.g. nextWith)
image

Describe the solution you'd like
Although I was able to complete my test without them, it'd make things easier if the 'getters' approach took the return type of the getter itself into account.

Describe alternatives you've considered
I was able to work around the problem by using the function returnValue, which was suggested by the IDE.

Angular 12 breaks [email protected] (at least for me)

After updating my project to Angular 12 I get this during startup of the tests (with ng test).

./node_modules/err-object/src/index.js:1:0-23 - Error: Module not found: Error: Can't resolve 'util' in '/home/vsts/work/1/s/node_modules/err-object/src'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "util": require.resolve("util/") }'
	- install 'util'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "util": false }

npm ls err-object gives me this

└─┬ [email protected]
  └─┬ @hirez_io/[email protected]
    └─┬ [email protected]
      └── [email protected] 

I didn't try out an entirely new project yet. But after removing jasmine-auto-spies from the project completely tests run again.

I guess its because of the CLI updates due to Angular 12. They updated to webpack 5.

I gave it also a try with installing util manually but then very strange errors occurred so I quickly reverted that change.

I didn't dive much deeper into the topic since removing jasmine-auto-spies fixes it.

=> https://github.com/angular/angular-cli/releases/tag/v12.0.0

Unable to override "returnValue" with usage of "calledWith"

(simplified scenario)
I have the following ExampleService :

import { Injectable } from '@angular/core';
import { ValueService } from './value.service';

@Injectable({
    providedIn: 'root'
})
export class ExampleService {
    constructor(private valueService: ValueService) { }

    public callValueService(): string {
        return this.valueService.getValue(true);
    }
}

The ValueService that is called looks like this:

import { Injectable } from '@angular/core';

@Injectable({
    providedIn: 'root'
})
export class ValueService {
    constructor() { }

    public getValue(value: boolean): string {
        return 'some random return value';
    }
}

In the spec file I want to "override" the returnValue after the calledWith function. Note that the parameters passed to the calledWith function are the same both times. Also see the comments for more clarification:

import { createSpyFromClass, Spy } from 'jasmine-auto-spies';
import { ValueService } from '../example/value.service';
import { ExampleService } from './example.service';

fdescribe('exampleService behavior', () => {
    let serviceUnderTest: ExampleService;
    let valueServiceSpy: Spy<ValueService>;

    beforeEach(() => {
        valueServiceSpy = createSpyFromClass(ValueService);
        serviceUnderTest = new ExampleService(valueServiceSpy);

        // sets the returnValue for every call underneath(?)
        valueServiceSpy.getValue.calledWith(true).returnValue('value 1');
    });

    describe('the one behavior', () => {
        // if line 14 is commented out and this is commented in, it works as expected
        // beforeEach(() => {
        //     valueServiceSpy.getValue.calledWith(true).returnValue('value 1');
        // });

        it('should return the "value 1"', () => {
            expect(serviceUnderTest.callValueService()).toEqual('value 1');
        });
    });

    describe('the other behavior', () => {
        beforeEach(() => {
            valueServiceSpy.getValue.calledWith(true).returnValue('value 2'); // <------ has no effect
        });

        it('should return the "value 2"', () => {
            expect(serviceUnderTest.callValueService()).toEqual('value 2'); // test breaks, "value 1" is returned
        });
    });
});

The result of the tests look like the following:
image

Whatever I enter as returnValue in the first beforeEach (Line 14) is used in all the functions underneath.

I don't know if that's the desired behavior and if I'm using this stuff wrong. But it would be great for me, if it would work like I would need it in the scenario above.

Thanks in advance for your help! 🙂

Type 'HttpClient' is not assignable to type 'AddAutoSpies<HttpClient, Spy<Func>>'.

Describe the bug
Cannot create spy fromm HTTPClient

To Reproduce

...
let httpSpy: Spy;
....
providers:
{ provide: HttpClient, useValue: createSpyFromClass(HttpClient) }
....
httpSpy = TestBed.inject(HttpClient);

Desktop (please complete the following information):

"@angular/core": "~10.0.0",
"jasmine-auto-spies": "^6.3.4",

Additional context

error TS2322: Type 'HttpClient' is not assignable to type 'Spy<HttpClient>'.
  Type 'HttpClient' is not assignable to type 'AddAutoSpies<HttpClient, Spy<Func>>'.
    Types of property 'request' are incompatible.

Action required: Greenkeeper could not be activated 🚨

🚨 You need to enable Continuous Integration on all branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.

Since we didn’t receive a CI status on the greenkeeper/initial branch, it’s possible that you don’t have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/.

Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please delete the greenkeeper/initial branch in this repository, and then remove and re-add this repository to the Greenkeeper App’s white list on Github. You'll find this list on your repo or organization’s settings page, under Installed GitHub Apps.

Support for Cypress

Is your feature request related to a problem? Please describe.
So I am using the following library: https://github.com/jscutlery/devkit to write component tests for Angular components.
A test is defined with the mount function like so:

mount(MyComponent, {
 providers: [],
...
})

Describe the solution you'd like
I would like to be able to make use of auto-spies to be able to mock out return values on services to verify different rendered states of the component visually.

So using this:


    let myServiceSpy: Spy<MyService>;
    provideAutoSpy(MyService),
    myServiceSpy = TestBed.inject<any>(MyService);

inside the

mount(MyComponent, {
 providers: [provideAutoSpy(MyService),],
...
})

and mock the return value later in the test

Describe alternatives you've considered
None

Additional context
When providing that auto-spy an error is thrown indicating that jasmine or jest is not defined. I am assuming here that we might need to make use of cypress mocking framework, which is Sinon under the hood.

Using nextWithValues with Observable properties does not work

Describe the bug
I encountered this while trying to fix a unit tests that still fails in an Angular 11 update attempt, where a component has an EventEmitter property. However Investigating further it boils down to the combination Observable + nextWithValues not working.

To Reproduce
Steps to reproduce the behavior:

  1. Add the following tests in packages/jasmine-auto-spies/src/tests/observable-spy-utils.spec.ts under the "GIVEN observable spy is configured to emit" test (which ought to be renamed to "GIVEN observable spy is configured to emit a single value").
      fdescribe(`GIVEN observable spy is configured to emit multiple values`, () => {
        Given(() => {
          fakeClassSpy.observableProp.nextWithValues([
            { value: FAKE_VALUE + '1' },
            { value: FAKE_VALUE + '2' },
            { value: FAKE_VALUE + '3' },
            { complete: true },
          ]);
        });

        Then('return value should be the fake values', () => {
          expect(observerSpy.getValues()).toBe([
            FAKE_VALUE + '1',
            FAKE_VALUE + '2',
            FAKE_VALUE + '3',
          ]);
        });
      });
  1. Run yarn test:full.

Expected behavior

The test should not fail like the following:

FAILED TESTS:
        GIVEN observable spy is configured to emit multiple values
          ×
   -> Then return value should be the fake values
            Chrome Headless 90.0.4430.85 (Windows 10)
          Error: Expected [  ] to be [ [ 'FAKE EMITTED VALUE1', 'FAKE EMITTED VALUE2', 'FAKE EMITTED VALUE3' ]. Tip: To check for deep equality, use .toEqual() instead of .toBe() ].
              at <Jasmine>
              at UserContext.<anonymous> (webpack:///src/tests/observable-spy-utils.spec.ts:565:43 <- test.js:23:96914)
              at promisify (C:/git/github/auto-spies/node_modules/@hirez_io/jasmine-given/dist/jasmine-given.js:37:33)
              at C:/git/github/auto-spies/node_modules/@hirez_io/jasmine-given/dist/jasmine-given.js:283:36

TOTAL: 1 FAILED, 0 SUCCESS

mockReturnValue is not typesafe

This compiles, but it shouldn't:

import { createSpyFromClass } from 'jest-auto-spies';

class Foo {
  bar(param: string): string {
    return '...';
  }
}

const foo = createSpyFromClass(Foo);
foo.bar.mockReturnValue(1);
foo.bar.calledWith(1).mockReturnValue(1);

When there is a strong type system we shouldn't write tests for invalid cases.

typescript version: 4.2.4
jest-auto-spies version: 1.6.5

Angular 13 Breaks createSpyFromClass and ProvideAutoSpy

Describe the bug
Added { provide: C247HttpClientService, useValue: createSpyFromClass(C247HttpClientService)} then replaced with
provideAutoSpiy(C247HttpClientService). ran ng test at got the error

Chrome 96.0.4664.110 (Linux x86_64) ERROR
  An error was thrown in afterAll
  Uncaught ReferenceError: global is not defined
  ReferenceError: global is not defined
      at Object.1186 (http://localhost:9876/_karma_webpack_/webpack:/node_modules/randombytes/browser.js:16:1)
      at require (http://localhost:9876/_karma_webpack_/webpack:/webpack/bootstrap:19:1)
      at Object.7172 (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@hirez_io/auto-spies-core/node_modules/serialize-javascript/index.js:9:19)
      at __webpack_require__ (http://localhost:9876/_karma_webpack_/webpack:/webpack/bootstrap:19:1)
      at Object.5260 (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@hirez_io/auto-spies-core/dist/errors/error-handler.js:5:17)
      at __webpack_require__ (http://localhost:9876/_karma_webpack_/webpack:/webpack/bootstrap:19:1)
      at Object.7346 (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@hirez_io/auto-spies-core/dist/create-function-auto-spy.js:5:23)
      at __webpack_require__ (http://localhost:9876/_karma_webpack_/webpack:/webpack/bootstrap:19:1)
      at Object.7841 (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@hirez_io/auto-spies-core/dist/index.js:13:14)
      at __webpack_require__ (http://localhost:9876/_karma_webpack_/webpack:/webpack/bootstrap:19:1)

The actual Test Code is as follows:

import { TestBed } from '@angular/core/testing';
import { C247HttpClientService } from '@c247-common/c247-http-client';
import { createSpyFromClass } from 'jasmine-auto-spies';

import { SelectTournamentService } from './select-tournament.service';

describe('SelectTournamentService', () => {
  let service: SelectTournamentService;

  beforeEach(() => {
    TestBed.configureTestingModule({      
     providers:[
       SelectTournamentService,
       { provide: C247HttpClientService, useValue: createSpyFromClass(C247HttpClientService)}

      ]
    });
    service = TestBed.inject<any>(SelectTournamentService);
  });

  it('should be created', () => {
     expect(service).toBeTruthy();
  });
});

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this 💪.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here is some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


Invalid npm token.

The npm token configured in the NPM_TOKEN environment variable must be a valid token allowing to publish to the registry https://registry.npmjs.org/.

If you are using Two-Factor Authentication, make configure the auth-only level is supported. semantic-release cannot publish with the default auth-and-writes level.

Please make sure to set the NPM_TOKEN environment variable in your CI with the exact value of the npm token.


Good luck with your project ✨

Your semantic-release bot 📦🚀

Using proxies for mocks

Instead of the current mustBeCalledWith behavior, it might be beneficial to use proxies instead and wrap the it() with a behavior that adds expectations with toHaveBeenCalledWith to the and of the test, so it won't break in the middle of the test...

Any opinions are welcomed

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.