Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/i5ting/ts-junit
use JUnit 5 Decorator in TypeScript
https://github.com/i5ting/ts-junit
ava jasmine jest junit mocha qunit tape test test-automation testing testng
Last synced: 3 months ago
JSON representation
use JUnit 5 Decorator in TypeScript
- Host: GitHub
- URL: https://github.com/i5ting/ts-junit
- Owner: i5ting
- License: mit
- Created: 2020-04-21T09:44:18.000Z (almost 5 years ago)
- Default Branch: main
- Last Pushed: 2022-11-10T03:59:02.000Z (about 2 years ago)
- Last Synced: 2024-10-01T09:42:59.828Z (4 months ago)
- Topics: ava, jasmine, jest, junit, mocha, qunit, tape, test, test-automation, testing, testng
- Language: TypeScript
- Homepage:
- Size: 428 KB
- Stars: 40
- Watchers: 5
- Forks: 7
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ts-junit
![version](https://img.shields.io/npm/v/ts-junit)
![license](https://img.shields.io/npm/l/ts-junit)
[![TypeScript](https://img.shields.io/badge/lang-typescript-informational)](https://www.typescriptlang.org)
![npm total downloads](https://img.shields.io/npm/dt/ts-junit.svg)
![npm month downloads](https://img.shields.io/npm/dm/ts-junit.svg)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/i5ting/ts-junit/pulls)
[![Github action](https://github.com/i5ting/ts-junit/actions/workflows/ci.yml/badge.svg)](https://github.com/i5ting/ts-junit/actions/workflows/ci.yml)> use JUnit 5 Decorator in TypeScript
在我看来,在 TypeScript 里使用面向对象是很大概率变成最常用的方式的。目前所有的 JavaScript 测试都是面向过程的,比如 qunit、jest、mocha、ava、tape 等测试框架实现,还是围绕在面向过程阶段。我以为这不是 TypeScript 在现实中该有的样式。
我对 Java 还算熟悉,比如使用 JUnit 5 的测试代码就是采用面向对象写法的,代码如下。
```Java
import static org.junit.jupiter.api.Assertions.assertEquals;
import example.util.Calculator;
import org.junit.jupiter.api.Test;class MyFirstJUnitJupiterTests {
private final Calculator calculator = new Calculator();
@Test
void addition() {
assertEquals(2, calculator.add(1, 1));
}
}
```这种写法是非常简单的,这就是 Java 面向的好处。如果换成 TypeScript,几乎可以保持写法一模一样,代码如下。
```ts
import assert from "assert";
import { Test } from "ts-junit";export default class MyFirstJUnitJupiterTests {
calculator = new Calculator();@Test
addition() {
assert.is(2, calculator.add(1, 1));
}
}
```反观前端的测试代码基本上 2 种风格。前端的测试代码风格 1,它是最常用的测试方式,代码实例如下。
```js
test("JSON", () => {
const input = {
foo: "hello",
bar: "world",
};const output = JSON.stringify(input);
assert.snapshot(output, `{"foo":"hello","bar":"world"}`);
assert.equal(JSON.parse(output), input, "matches original");
});
```前端的测试代码风格 2,bdd 风格,它更强调行为对测试用例的影响,代码实例如下。
```js
describe("User", function () {
describe("#save()", function () {
it("should save without error", function (done) {
var user = new User("Luna");
user.save(function (err) {
if (err) throw err;
done();
});
});
});
});
```对比一下 Java 和 JavaScript 测试多个写法之后,你会发现,面向对象在 JavaScript(TypeScript)里根本不是一等公民。于是我就萌发了一个想法,想用 TypeScript 实现一下 JUnit。
## 特性
- ~~jest 支持 ts 需要引入 babel~~
- ~~ts-jest 直接支持 ts,测试语法上是 jest 语法,suite/test 或 describe/it~~
- ts-junit 支持 2 种用法,其中 cli 方式采用增量 ts 编译,效率很高的。
- ts-junit 使用 junit 5 的装饰器进行封装,成熟,使用于熟悉 OO 的开发,尤其对了解 Java 的开发者更友好。
- ts-junit 使用 uvu 作为默认策略,同时也可以实现各个常见测试框架的支持,比如 jest、mocha、ava、tape、qunit、jasmine 等(暂时未实现)。## 示例
```ts
import assert from 'assert'
import { BeforeAll, BeforeEach, Disabled, Test, AfterEach, AfterAll } from 'ts-junit'export default class MyFirstJUnitJupiterTests {
calculator = new Calculator()@BeforeAll
static void initAll() {
}@BeforeEach
void init() {
}@Test
void succeedingTest() {}
@Test
void failingTest() {
assert.fail("a failing test");
}@Test
@Disabled("for demonstration purposes")
void skippedTest() {
// not executed
}@Test
void abortedTest() {
assert.assumeTrue("abc".contains("Z"));
assert.fail("test should have been aborted");
}@AfterEach
void tearDown() {
}@AfterAll
static void tearDownAll() {
}
}
```## Usages
### 方式 1: 使用独立 cli 进行编译
不依赖当前项目的 ts 环境,直接通过 cli 执行,参考源码中 tests 目录下的文件。
```shell
$ npm i --global @ts-junit/cli
$ junit tests
$ junit tests/test.ts
```编写第一个测试用例
```ts
import assert from 'assert'
import { Test } from '@ts-junit/core'export default class MyFirstJUnitJupiterTests {
calculator = new Calculator();
@Test
void addition() {
assert.is(2, calculator.add(1, 1));
}
}
```### 方式 2: 依赖当前项目的 ts 环境进行编译
```shell
$ npm i --save-dev @ts-junit/core
```编写测试入口文件 ts-junit.ts,文件内指定测试文件或测试目录即可。
```ts
import * as path from "node:path";
import { run } from "@ts-junit/core";const folder = path.resolve(process.cwd(), "./tests");
const file = path.resolve(process.cwd(), "./tests/test.ts");run([folder, file]);
// or custom Strategy
// import SomeStrategy from "./SomeStrategy";
// run([folder, file], new SomeStrategy());
```创建编译时的 tsconfig.json 文件
```ts
{
"compileOnSave": true,
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"sourceMap": true,
"outDir": "./build",
"rootDir": "./src",
"typeRoots": [],
"types": [],
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"exclude": ["node_modules"],
"include": ["./src/**/*.ts", "./test/**/*.ts"]
}
```编辑 package.json 的启动和编译脚本
```ts
{
"scripts": {
"test": "NODE_ENV=dev ts-node --project tsconfig.json --files ts-junit.ts",
"build": "tsc"
}
}
```启动服务
```
$ npm test
> NODE_ENV=dev ts-node --project tsconfig.json --files ts-junit.ts
[2020-9-1 19:52:12] [debug] [init] [router] get - /
```## 装饰器
- 参考 junit5 的文档 https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations
- 进度 `7/20`
Annotation
Description
isSupported
@Test
Denotes that a method is a test method. Unlike JUnit 4’s
@Test
annotation, this annotation does not declare any attributes, since test extensions in JUnit Jupiter
operate based on their own dedicated annotations. Such methods are inherited unless they
are overridden.
✅
@ParameterizedTest
Denotes that a method is a parameterized test. Such methods are
inherited unless they are overridden.
✅
@RepeatedTest
Denotes that a method is a test template for a repeated test. Such methods are inherited
unless they are overridden.
❌
@TestFactory
Denotes that a method is a test factory for dynamic tests. Such methods are inherited
unless they are overridden.
❌
@TestTemplate
Denotes that a method is a template for
test cases designed to be invoked multiple times depending on the number of invocation
contexts returned by the registered providers. Such methods
are inherited unless they are overridden.
❌
@TestMethodOrder
Used to configure the test method
execution order for the annotated test class; similar to JUnit 4’s
@FixMethodOrder
. Such annotations are inherited.
❌
@TestInstance
Used to configure the test
instance lifecycle for the annotated test class. Such annotations are inherited.
❌
@DisplayName
Declares a custom display name for the
test class or test method. Such annotations are not inherited.
✅
@DisplayNameGeneration
Declares a custom display name
generator for the test class. Such annotations are inherited.
❌
@BeforeEach
Denotes that the annotated method should be executed before
each@Test
,@RepeatedTest
,
@ParameterizedTest
, or@TestFactory
method in the current class; analogous
to JUnit 4’s@Before
. Such methods are inherited unless they are
overridden.
✅
@AfterEach
Denotes that the annotated method should be executed after
each@Test
,@RepeatedTest
,
@ParameterizedTest
, or@TestFactory
method in the current class; analogous
to JUnit 4’s@After
. Such methods are inherited unless they are
overridden.
✅
@BeforeAll
Denotes that the annotated method should be executed before
all@Test
,@RepeatedTest
,
@ParameterizedTest
, and@TestFactory
methods in the current class;
analogous to JUnit 4’s@BeforeClass
. Such methods are inherited (unless they
are hidden or overridden) and must bestatic
(unless the "per-class"
test instance lifecycle is used).
✅
@AfterAll
Denotes that the annotated method should be executed after
all@Test
,@RepeatedTest
,
@ParameterizedTest
, and@TestFactory
methods in the current class;
analogous to JUnit 4’s@AfterClass
. Such methods are inherited (unless they
are hidden or overridden) and must bestatic
(unless the "per-class"
test instance lifecycle is used).
✅
@Nested
Denotes that the annotated class is a non-static nested test class.
@BeforeAll
and
@AfterAll
methods cannot be used directly in a@Nested
test class unless
the "per-class" test instance lifecycle is
used. Such annotations are not inherited.
❌
@Tag
Used to declare tags for filtering
tests, either at the class or method level; analogous to test groups in TestNG or Categories
in JUnit 4. Such annotations are inherited at the class level but not at the method level.
❌
@Disabled
Used to disable a test class or test
method; analogous to JUnit 4’s@Ignore
. Such annotations are not inherited.
✅
@Timeout
Used to fail a test, test factory, test template, or lifecycle method if its
execution exceeds a given duration. Such annotations are inherited.
❌
@ExtendWith
Used to register extensions
declaratively. Such annotations are inherited.
❌
@RegisterExtension
Used to register extensions
programmatically via fields. Such fields are inherited unless they are
shadowed.
❌
@TempDir
Used to supply a temporary directory via field
injection or parameter injection in a lifecycle method or test method; located in the
org.junit.jupiter.api.io
package.
❌
## TODO
1. 结合 https://github.com/midwayjs/injection 更简单(暂未实现)
```ts
class Test {
@Inject()
helloTest: IHelloTest;
@Inject()
helloService: IHelloService;@Before()
before() {
mock(helloTest, "sayhello", () => {
return "mocked";
});
}@Test()
async test() {
expect(this.helloTest.sayhello()).eq("mocked");expect(this.helloService.sayhello("test")).eq("hello test");
}
}
```2. use vm2 with require from memfs