Open amrsalem1 opened 3 years ago
Yes, there are some ways, but they are poorly documented and should be improved (refactored a bit). I'm planning to do this, probably by the end of this week (docs/readme update & example & misc refactor for simplifying the API).
Currently, you can use the @RunWith decorator and implement your own TestRunner
. Here is it's interface to implement:
interface ITestRunner {
registerMocks(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void;
registerAutoCleared(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void;
registerLazyModules(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void;
registerMockFnsAndSpies(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void;
registerHooks(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void;
registerTestsInJest(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void;
}
ATM, you must implement ALL of the methods, and add more functionality after. So, the initial implementation will look like this:
class MyTestRunner implements ITestRunner {
public constructor(protected readonly defaultTestsRunner: ITestRunner) {}
public registerMocks(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void {
this.defaultTestsRunner.registerMocks(describeRunner, parentDescribeRunner);
}
public registerAutoCleared(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void {
this.defaultTestsRunner.registerAutoCleared(describeRunner, parentDescribeRunner);
}
public registerLazyModules(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void {
this.defaultTestsRunner.registerLazyModules(describeRunner, parentDescribeRunner);
}
public registerMockFnsAndSpies(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void {
this.defaultTestsRunner.registerMockFnsAndSpies(describeRunner, parentDescribeRunner);
}
public registerHooks(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void {
this.defaultTestsRunner.registerHooks(describeRunner, parentDescribeRunner);
}
public registerTestsInJest(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void {
this.defaultTestsRunner.registerTestsInJest(describeRunner, parentDescribeRunner);
}
}
And then, you can starting doing your own things:
class MyTestRunner implements ITestRunner {
// ...
public registerTestsInJest(describeRunner: IDescribeRunner, parentDescribeRunner?: IDescribeRunner): void {
this.processTests(describeRunner); // this line is executed before tests are passed to jest
this.defaultTestsRunner.registerTestsInJest(describeRunner, parentDescribeRunner); // this line is registering tests in jest
}
protected processTests(describeRunner: IDescribeRunner): void {
const testsService = describeRunner.getTestsService();
// log info about tests, set your own metadata, change anything, etc.
for (const testEntity of testsService.getTests()) {
console.log(testEntity); // TestEntity { name, description, timeout, metadata, dataProviders, testType }
testEntity.description += " my label"; // modify test's description
testEntity.setMetadata("myKey", { myVal: "foo" }); // add some custom data to test entity
testEntity.setTestType("skip"); // or skip test at all, if you want
}
testsService.registerPreProcessor(
// callback to be executed for each test (during the jest tests execution), before running the test itself
(preProcessorData: PreProcessorData) => {
// here you can do any side effects, but you shouldn't change any info about the test itself (name, description, type)
const { testEntity, args, clazzInstance } = preProcessorData;
// you can also override this data, by returning the same object
return preProcessorData;
},
// order of the callback in the callbacks chain, if it matters
1_000,
);
testsService.registerPostProcessor(
// callback to be executed for each test (during the jest tests execution), after running the test itself
(preProcessorResult: unknown, testError?: Error) => {
// preProcessorResult is an entity, returned by your test-method (usually test returns nothing, and the value is undefined)
console.log(preProcessorResult);
// testError is a test error; regardless of the post-processor result, this function error will be thrown (test has failed)
console.log(testError);
},
// order of the callback in the callbacks chain, if it matters
1_000,
);
}
}
When you're ready - start using your runner:
@Describe()
@RunWith(MyTestRunner)
class MyTest {
// ...
}
Typings can be found in the @jest-decorated/shared
package:
import type { ITestRunner, PreProcessorData, IDescribeRunner, TestEntity } from "@jest-decorated/shared";
Docs updated.
Please, refer to writing custom test runner and writing custom decorators guides.
@vitalishapovalov thanks for sharing this, but could you provide a sample for a snippet on how exactly I can run one single test that will run in dry mode and get a list of names of specific annotation values such as @Test/@Describe...etc values for other existing tests
I'm not 100% sure that I've understood the issue, maybe you can provide some more details or examples of what exactly you want to achieve? Regarding the Dry mode - you mean, bypass all of the additional functionality and simply execute existing tests/hooks?
is there some way that can be used so I can extract some info from test spec files, like name of the test suites, test case titles...etc without executing any test?