Autospy - gparlakov/scuri GitHub Wiki

Home

Autospy

The goal of autospy is to spare you the boilerplate of creating a mock (spy in jasmine parlance).

It takes a TS class and creates mocks for all methods in its prototype. It then returns an object with the type that allows it to be used as the original type as well as in .hasBeenCalledWith assertions that require a different shape object.

Example

In this example our UserComponent depends on UserService and we'd like to test that when ngOnInit is called it calls UserService.current() method once.

// user.ts
class UserService {
    current(): User {
        //...
    }
}

// user.component.ts
class UserComponent {
    constructor(private user: User) {}

    ngOnInit(){
        this.current = this.user.current();
    }
}

For that, we would need to instantiate the dependency UserService, the component-under-test UserComponent and call the ngOnInit method in our test file:

//user.component.spec.ts

it('calls current', () => {
    // arrange
    const userSpy = autoSpy(UserService);
    // used as the UserService type here     ๐Ÿ‘‡
    const userComponent = new UserComponent(userSpy);
    // act
    userComponent.ngOnInit();
    // assert
    //used as the spy-able type here ๐Ÿ‘‡
    expect(userSpy.current).toHaveBeenCalledTimes(1);
    // i.e.๐Ÿ‘†userSpy has๐Ÿ‘†current property that can be spied upon
})

Properties

Autospy does not, and (currently) can not create spies for the properties of a class. That's because properties are not part of the prototype and run-time autospy code has no way of detecting them. What to do then? We can assign values to these properties just as we would in our code.

Continuing the example from above:

// user.ts
class UserService {
    // ๐Ÿ‘‡ our dependency has a property - not visible in the prototype, though
    isAuthenticated: boolean;

    current(): User {
        //...
    }
}

// user.component.ts
class UserComponent {
    constructor(private user: User) {}

    ngOnInit(){
        this.current = this.user.current();
        if(this.user.isAuthenticated) {
            this.content = userAuthenticatedContent;
        }
    }
}

For that, we would need to instantiate the dependency UserService, the component-under-test UserComponent and call the ngOnInit method in our test file:

//user.component.spec.ts

it('shows the authenticated user content', () => {
    // arrange
    const userSpy = autoSpy(UserService);
    // set the property ๐Ÿ‘‡ here
    userSpy.isAuthenticated = true;
    const userComponent = new UserComponent(userSpy);
    const authContent = 'This user is indeed authenticated!'
    // act
    userComponent.ngOnInit();
    // assert
    expect(userComponent.content).toEqual(authContent);
})