{"id":13694456,"url":"https://github.com/moditect/jfrunit","last_synced_at":"2026-01-12T11:36:52.560Z","repository":{"id":37830818,"uuid":"315419446","full_name":"moditect/jfrunit","owner":"moditect","description":"A JUnit extension for asserting JDK Flight Recorder events","archived":false,"fork":false,"pushed_at":"2023-04-04T17:39:56.000Z","size":971,"stargazers_count":317,"open_issues_count":28,"forks_count":22,"subscribers_count":13,"default_branch":"main","last_synced_at":"2025-05-03T03:38:33.143Z","etag":null,"topics":["java-flight-recorder","jdk-flight-recorder","jfr","jvm","jvm-performance"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moditect.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2020-11-23T19:29:13.000Z","updated_at":"2025-04-09T21:02:05.000Z","dependencies_parsed_at":"2023-02-10T19:45:59.580Z","dependency_job_id":"137ddddc-a320-410a-bbd7-ea4041de25e0","html_url":"https://github.com/moditect/jfrunit","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/moditect/jfrunit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moditect%2Fjfrunit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moditect%2Fjfrunit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moditect%2Fjfrunit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moditect%2Fjfrunit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moditect","download_url":"https://codeload.github.com/moditect/jfrunit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moditect%2Fjfrunit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28338971,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T10:58:46.209Z","status":"ssl_error","status_checked_at":"2026-01-12T10:58:42.742Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java-flight-recorder","jdk-flight-recorder","jfr","jvm","jvm-performance"],"created_at":"2024-08-02T17:01:32.637Z","updated_at":"2026-01-12T11:36:52.544Z","avatar_url":"https://github.com/moditect.png","language":"Java","funding_links":[],"categories":["Java","测试"],"sub_categories":[],"readme":"# JfrUnit - A JUnit extension for asserting JDK Flight Recorder events\n\n_Unit testing, for performance_\n\nJfrUnit allows to assert the [JDK Flight Recorder](https://openjdk.java.net/jeps/328) (JFR) events emitted by an application.\n\n## Why JfrUnit?\n\nWhile unit testing of functional requirements is a standard practice, identifying performance regressions (e.g. increased latencies, reduced throughput) through automated tests is much harder: e.g. assertions on specific request runtimes are prone to failures in virtualized/containerized CI environments due to concurrent load of other jobs.\n\nJfrUnit offers a fresh angle to this topic by supporting assertions not on metrics like latency/throughput themselves, but on indirect metrics which may impact those.\nBased on JDK Flight Recorder events, JfrUnit allows you execute assertions e.g. against memory allocation, database IO, and number of executed SQL statements, for a defined workload.\nStarting off from a defined base line, future failures of such assertions are indicators for potential performance regressions in an application, as a code change may have introduced higher GC pressure, the retrieval of unneccessary data from the database, or common SQL problems like N+1 SELECT statements.\n\nJfrUnit provide means of identifying and analysizing such issues in a reliable, environment independent way in standard JUnit tests,\nbefore they manifest as performance regressions in production.\n\n### Learning More\n\nHere are some resources which describe JfrUnit and its approach to performance regression testing:\n\n* [Towards Continuous Performance Regression Testing](https://www.morling.dev/blog/towards-continuous-performance-regression-testing/)\n* [Introducing JfrUnit 1.0.0.Alpha1](https://www.morling.dev/blog/introducing-jfrunit-1-0-0-alpha1/)\n* [Asserting JDK Flight Recorder Events with JfrUnit](https://www.infoq.com/news/2021/09/jfrunit-flight-recorder-events/)\n* [Continuous Performance Regression Testing with JfrUnit](https://www.p99conf.io/session/continuous-performance-regression-testing-with-jfrunit/)\n* [Keep Your SQL in Check With Flight Recorder, JMC Agent and JfrUnit](https://www.javaadvent.com/2021/12/keep-your-sql-in-check-with-flight-recorder-jmc-agent-and-jfrunit.html)\n\n## Usage\n\nThis project requires OpenJDK 16 or later at runtime.\nSupport for JDK 11 is on the roadmap, JfrUnit couldn't rely on JFR event stream in this case though, but would have to read JFR events from a recording persisted to disk.\nA PR contributing this change would be very welcomed.\n\nJfrUnit is available from Maven Central;\nadd the following dependency to your project's _pom.xml_:\n\n```xml\n...\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.moditect.jfrunit\u003c/groupId\u003e\n  \u003cartifactId\u003ejfrunit-core\u003c/artifactId\u003e\n  \u003cversion\u003e1.0.0.Alpha2\u003c/version\u003e\n  \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n...\n```\n\nAlternatively, you can build JfrUnit from source (see below) yourself, so to pull in changes done after the latest release.\n\nThen you can implement tests expecting specific JFR events like so:\n\n```java\nimport org.moditect.jfrunit.*;\n\nimport static org.moditect.jfrunit.JfrEventsAssert.*;\nimport static org.moditect.jfrunit.ExpectedEvent.*;\nimport org.moditect.jfrunit.events.JfrEventTypes;\n\n@JfrEventTest\npublic class JfrTest {\n\n    public JfrEvents jfrEvents = new JfrEvents();\n\n    @Test\n    @EnableEvent(GarbageCollection.EVENT_NAME)\n    @EnableEvent(ThreadSleep.EVENT_NAME)\n    public void shouldHaveGcAndSleepEvents() throws Exception {\n        System.gc();\n        Thread.sleep(1000);\n\n        jfrEvents.awaitEvents();\n\n        assertThat(jfrEvents).contains(JfrEventTypes.GARBAGE_COLLECTION);\n        assertThat(jfrEvents).contains(\n                JfrEventTypes.THREAD_SLEEP.withTime(Duration.ofMillis(1000)));\n    }\n}\n```\n\nNote that when you're writing a test for a Quarkus application using the `@QuarkusTest` annotation, you don't need (and even should not) add the `@JfrEventTest` annotation.\nInstead, the Quarkus test framework will automatically pick up the required callbacks for managing the JFR recording.\n\nThe `@EnableEvent` annotation is used to enable one or more JFR event types which should be captured.\nThe \"*\" character can be used as a wildcard character to match multiple types:\n\n```java\n@Test\n@EnableEvent(\"jdk.GC*\")\n@EnableEvent(\"jdk.G1*\")\npublic void someTest() throws Exception { ... }\n```\n\nThis would capture events like `jdk.GCHeapSummary`, `jdk.GCPhasePause`, `jdk.G1GarbageCollection` etc.\nA complete list of all built-in JFR event types can be found [here](https://bestsolution-at.github.io/jfr-doc/).\n\nAlternatively, you can specify the name of a JFR configuration file, e.g. \"default\" or \"profile\", using the `@EnableConfiguration` annotation:\n\n```java\n@Test\n@EnableConfiguration(\"default\")\npublic void someTest() throws Exception { ... }\n```\n\nJFR configuration files are located in the _$JAVA_HOME/bin/jfr_ directory.\n\n### Using Spock Framework\n\nYou can also write JfrUnit tests using the [Spock Framework](https://spockframework.org/) like this:\n\n```groovy\nimport org.moditect.jfrunit.JfrEvents\nimport spock.lang.Specification\n\nimport java.time.Duration\n\nclass JfrSpec extends Specification {\n\n    JfrEvents jfrEvents = new JfrEvents()\n\n    @EnableEvent('jdk.GarbageCollection')\n    @EnableEvent('jdk.ThreadSleep')\n    def 'should Have GC And Sleep Events'() {\n        when:\n        System.gc()\n        sleep(1000)\n\n        then:\n        jfrEvents['jdk.GarbageCollection']\n        jfrEvents['jdk.ThreadSleep'].withTime(Duration.ofMillis(1000))\n    }\n}\n```\n\nAs you can see you can use custom DSL when checking the expected state.\n * `JfrEvents['event.name']` or `JfrEvents.list('event.name')` gives you a list containing all the events of the requested type.\n * Beyond just list methods as usual you may further narrow it down by using `with`, `having` and `notHaving` dynamic methods\n   (returning list as well). For example:\n     * `withTime(Duration.ofMillis(1000))`\n     * `withCause('System.gc()')`\n     * `withObjectClass(byte[].class)`\n     * `withEventThread(Thread.currentThread())`\n     * `havingStackTrace()`\n     * `notHavingStackTrace()`\n     * `containStackFrame(stackTraceElement)`\n     * ...\n * The `RecordedEvent` itself is extended with dynamic properties so you can just use `event.time` or `event.bytesWritten` etc.\n   This might be handy when you need an aggregation like this `jfrEvents['jdk.FileWrite']*.bytesWritten.sum() == expectedBytes`\n\n## Build\n\nThis project requires OpenJDK 16 or later for its build.\nApache Maven is used for the build.\nRun the following to build the project:\n\n```shell\nmvn verify\n```\n\nRun the following to install the JARs to the local Maven repository:\n\n```shell\nmvn install\n```\n\n## License\n\nThis code base is available under the Apache License, version 2.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoditect%2Fjfrunit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoditect%2Fjfrunit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoditect%2Fjfrunit/lists"}