3

I can't quite figure out how to unit test some methods of a support class in Angular with Jasmine. The class under test is a singleton, plain typescript class which is initialized by one of the services when it starts, and then is consumable by various components over the applications' lifecycle by calling AccountsManager.getInstance().

Here is the constructor and initializer for this class, called AccountsManager:

export class AccountsManager {
    private static instance: AccountsManager;

    static initialize(accountService: AccountService) {
        if (!this.instance) { this.instance = new AccountsManager(accountService) }

    }

    static getInstance(): AccountsManager {
        if (!this.instance || !this.instance.isInitialized) {
            throw new Error("AccountsManager.getInstance(): AccountManager has not been initialized");
        }
        return this.instance;
    }

    //methods...
 }

I tried this set up for a Jasmine test based on what I could find in various reference documentation, but the docs I have seen show how to test an Angular component not an arbitrary class.

The problem is getting an instance of the AccountService with which to initialize the AccountsManager. It's complicated to instantiate the real AccountService, so I wanted to use a mock service, since the unit tests I want to run don't rely on the AccountService anyway.

I found an example of how to test a component with a mock service, but I"m guessing they are the wrong setup for testing a POTC.

Anyway here is what I tried, just to see.

fdescribe('AccountsManager', () => {

    let fixture: ComponentFixture<AccountsManager>
    let mockAccountService = {  }
    let component : AccountsManager;
    beforeEach(() =>{
        TestBed.configureTestingModule({
            declarations: [AccountsManager],
            providers: [{provide: AccountService, useValue:  mockAccountService}]
        })
        fixture= TestBed.createComponent(AccountsManager);
        component = fixture.componentInstance;
    })
});

I'm guessing this would never work since "AccountsManager" is just a class, its not an Angular component, but in any case, it's failing now on TestBed.createComponent(AccountsManager) with an error message that AccountsManager's constructor is private (by design) and createComponent expects there to be a public constructor. But I'm guessing there is a more straightforward way to do what I want anyway.

If all I want to do is to be able initialize this class with a dummy AccountsService instance, what is easiest way to do that so I can run my tests?

GGizmos
  • 3,443
  • 4
  • 27
  • 72
  • Just to be clear: do you have a component and a plain class with the same name, `AccountsManager`? If this is the case, there's something wrong with your project. If `AccountsManager` isn't a component, you shouldn't be instantiating it using `createComponent`. Testbed exists basically to instantiate angular components and provide the injector + change detection mechanisms. – julianobrasil Mar 30 '20 at 03:01
  • I'll try to answer your question considering `AccountsManager` is not an Angular component. – julianobrasil Mar 30 '20 at 03:08

1 Answers1

1

If I got what you are asking for, you need something like this:

describe('AccountsManager', () => {
    let mockAccountService = {};
    let accountsManager: AccountsManager;

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [{provide: AccountService, useValue:  mockAccountService}]
        });

        AccountsManager.initialize(TestBed.inject(AccountService));
        accountsManager = AccountsManager.getInstance();
    });

    it('should be a singleton', () => {
       AccountsManager.initialize(TestBed.inject(AccountService));
       const am = AccountsManager.getInstance();

       expect(am === accountsManager)
         .withContext('AccountsManager is not a singleton')
         .toBeTrue();
    });
});
julianobrasil
  • 8,954
  • 2
  • 33
  • 55