GithubHelp home page GithubHelp logo

justjavac / ts-junit Goto Github PK

View Code? Open in Web Editor NEW

This project forked from i5ting/ts-junit

0.0 1.0 0.0 252 KB

use JUnit 5 Decrator with TypeScript

License: MIT License

JavaScript 0.52% TypeScript 99.35% Shell 0.13%

ts-junit's Introduction

ts-junit

version license TypeScript npm total downloads npm month downloads PRs Welcome Github action

use JUnit 5 Decorator in TypeScript

在我看来,在 TypeScript 里使用面向对象是很大概率变成最常用的方式的。目前所有的 JavaScript 测试都是面向过程的,比如 qunit、jest、mocha、ava、tape 等测试框架实现,还是围绕在面向过程阶段。我以为这不是 TypeScript 在现实中该有的样式。

我对 Java 还算熟悉,比如使用 JUnit 5 的测试代码就是采用面向对象写法的,代码如下。

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,几乎可以保持写法一模一样,代码如下。

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,它是最常用的测试方式,代码实例如下。

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 风格,它更强调行为对测试用例的影响,代码实例如下。

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 等(暂时未实现)。

示例

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 目录下的文件。

$ npm i --global ts-juint
$ junit tests
$ junit tests/test.ts

编写第一个测试用例

import assert from 'assert'
import { Test } from 'ts-junit'

export default class MyFirstJUnitJupiterTests  {

    calculator = new Calculator();

    @Test
    void addition() {
        assert.is(2, calculator.add(1, 1));
    }
}

方式 2: 依赖当前项目的 ts 环境进行编译

$ npm i --save-dev ts-juint

编写测试入口文件 ts-junit.ts,文件内指定测试文件或测试目录即可。

import * as path from "node:path";
import { run } from "ts-junit";

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 文件

{
  "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 的启动和编译脚本

{
  "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 - /

装饰器

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 be static (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 be static (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 更简单(暂未实现)

    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

  3. 目前 ts 编译成 js 之后,是通过 ast 去解析的 js 提取装饰器信息的,

    提取方式是

    __decorate([index_1.BeforeAll], MyFirstJUnitJupiterTests.prototype, 'initAll');

    这里面可以试试能否直接反射出来,用一个类似探针的方式,运行时去提取装饰器,应该也是可行的。

    var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
        var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
        if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
        else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
        return c > 3 && r && Object.defineProperty(target, key, r), r;
    };

ts-junit's People

Contributors

cgqaq avatar i5ting avatar justjavac avatar liruifengv avatar shulandmimi avatar tangjinjian avatar zhengxs2018 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.