0

Say I have a component:

export class Component {
  constructor(@injectable(IDependency) dep: IDependency) {}

  // other methods/properties do not matter
}

and the dependency it requires is defined as:

export const IDependency = Symbol('IDependency');
export interface IDependency { /* does not matter */ }

Now I want to unit-test the component, but because number of dependencies can be significant (not like 20, but 5 is already significant to me), I do not want to manually create all required mocks. Instead I want an IoC container to automatically mock anything that is not explicitly registered. So far so good?

I got to the point where I receive the symbol (IDependency) and need to create a mock of the interface (IDependency). But it is at all possible to map a symbol to an interface with the same name in typescript? I guess not, or else there would not have been a need for symbols in the first place.

And if not, can any clever head suggest a workaround? Like storing information about an interface somewhere at compile time and its mapping to a symbol, so it can be mocked at runtime? Or, perhaps, using Proxies that are so generic that they do not care what type they are mocking?

1 Answer 1

0

I went with the second approach (which only came to my mind as I was posting the question), and my tests now look like this:

testSuiteFor(TestExplorer, function() {
  this.test('should send init request when ILanguageClient is ready', function() {
    this.mock<IExtensionContext>(IExtensionContext, { subscriptions: [] });

    const client = this.mock<ILanguageClient>(ILanguageClient);
    const readyCallback = captor<Function>();

    this.component();

    expect(client.onReady).toHaveBeenCalledWith(readyCallback);
    expect(client.sendRequest).not.toHaveBeenCalled();

    readyCallback.value();

    expect(client.sendRequest).toHaveBeenCalledWith("ut/explorer/init");
  });

  this.test('should register Run profile', function() {
    this.mock<IExtensionContext>(IExtensionContext, { subscriptions: [] });

    const controller = this.mock<ITestController>(ITestController);
    const explorer = this.component();

    expect(controller.createRunProfile).toHaveBeenCalledWith(
      "Run", 
      vscode.TestRunProfileKind.Run, 
      functionBoundTo(explorer.runTests)
    );
  });
});

Today I also stumbled upon typescript-rtti. I think I will try that next.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.