In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly analyzes how to analyze the relevant knowledge points of the Extension Model extension model in JUnit 5, the content is detailed and easy to understand, the operation details are reasonable, and has a certain reference value. If you are interested, you might as well follow the editor and learn more about how to analyze the Extension Model extension model in JUnit 5.
Extended Model of JUnit 4
Let's first take a look at how extensions are implemented in JUnit 4. Extensions are implemented in JUnit 4 mainly through two, sometimes overlapping extension mechanisms: the Runners and the Rules.
Operator (Runners)
The test runner is responsible for managing the life cycle of many tests, including their instantiation, setup/teardown method calls, test runs, exception handling, sending messages, and so on. In the runner implementation provided by JUnit 4, it is responsible for all of this.
In JUnit 4, the only way to extend JUnit is to create a new runner and use it to mark your new test class: @ Runwith (MyRunner.class). In this way, JUnit will recognize and use it to run tests instead of its default implementation.
This approach is heavy and inconvenient for small customization and small extensions. At the same time, it has a very stringent limitation: a test class can only run with one runner, which means that you cannot combine different runners. In other words, you can't enjoy the features provided by more than two runners at the same time, such as runners that can't use Mockito and Spring at the same time.
Rules (Rules)
To overcome this limitation, JUnit 4.7 introduces the concept of rules, which refer to special annotation fields in the test class. JUnit 4 wraps the test method (along with some other behaviors) and passes it to the rules. The rules can therefore be inserted to execute some code before and after the test code is executed. In many cases, the method on the rule class will also be called directly in the test method.
Here is an example that shows the temporary folder (temporary folder) rule:
Public static class HasTempFolder {@ Rule public TemporaryFolder folder= new TemporaryFolder (); @ Test public void testUsingTempFolder () throws IOException {File createdFile= folder.newFile ("myfile.txt"); File createdFolder= folder.newFolder ("subfolder"); / /...}}
Because of the @ Rule annotation, JUnit first wraps the test method testUsingTempFolder into an executable block of code and passes it to the folder rule. The purpose of this rule is that when executed, folder creates a temporary directory, executes the test, and deletes the temporary directory when the test is complete. Therefore, you can safely create files and folders in a temporary directory inside the test.
Of course, there are other rules, such as rules that allow you to run tests in the Swing event distribution thread, rules that are responsible for connecting and disconnecting databases, and rules that time out long-running tests directly.
The rule feature is actually a big improvement, but it still has limitations and can only be customized before or after the test run. If you want to expand outside of this point in time, there is nothing you can do about it.
Current situation
All in all, there are two different extension mechanisms in JUnit 4, both of which have their own limitations and overlap in functions. It's hard to write clean extensions under JUnit 4. In addition, even if you try to combine two different extensions, it will not always be plain sailing, and sometimes it may not work the way developers expect it to.
Extended Model of JUnit 5
There are several core principles from the beginning of the Junit Lambda project, one of which is that "extension points are better than new features". This guideline is actually the most important extension mechanism in the new version of JUnit-- not the only one, but certainly one of the most important.
Extension point
The JUnit 5 extension can declare which part of the test lifecycle it is primarily concerned with. As the JUnit 5 engine processes the tests, it examines these extension points in turn and invokes each registered extension. In general, these extension points appear in the following order:
Post-processing of test class instance
BeforeAll callback
Test and container execution condition check
BeforeEach callback
Parameter analysis
Before test execution
After the test is executed
Exception handling
AfterEach callback
AfterAll callback
(if there are points above that you don't think are clear or understood, please don't worry, we'll pick some of them to explain.)
Each extension point corresponds to an interface. The interface method accepts some parameters and some context information about the lifecycle of the extension point. For example, the instance and method under test, the name of the test, parameters, notes and other information.
An extension can implement more than any interface methods, and the engine will pass in the corresponding context information as parameters when calling them. With this information, the extension can safely achieve the desired functionality.
Stateless
Here we need to consider an important detail: the engine does not make any specification and guarantee on the initialization time and survival time of the extension instance, so the extension must be stateless. If an extension needs to maintain any state information, it must use a store provided by JUnit to read and write information.
There are several reasons for this:
The timing and manner of initialization of the extension is unknown to the engine (instantiated once per test? Instantiate each class once? Or is it instantiated once per run?).
JUnit does not want to maintain and manage additional instances created by each extension.
If extensions want to communicate with each other, JUnit must provide a mechanism for data exchange anyway.
Application expansion
After you create the extension, all you need to do is tell JUnit that it exists. This can be done simply by adding a @ ExtendWith (MyExtension.class) to the test class or test method that needs to use the extension.
In fact, there is another more concise way. To understand that approach, however, we must first take a look at what else is in JUnit's extension model.
Custom annotation
JUnit 5's API is mostly based on annotations, and the engine does some extra work when checking annotations: it looks for not only annotations applied on fields, classes, and parameters, but also annotations on annotations. The engine applies all annotations found to the annotated elements. Another annotation can be done through so-called meta-annotations, and the cool thing is that all the annotations provided by Junit are meta-annotations.
What it means is that in JUnit 5 we will be able to create and combine different annotations, and they have the ability to combine multiple annotation features:
/ * * We define a custom annotation that: *-stands in for'@ Test' so that the method gets executed *-has the tag "integration" so we can filter by that, * e.g. When running tests from the command line * / @ Target ({ElementType.TYPE, ElementType.METHOD}) @ Retention (RetentionPolicy.RUNTIME) @ Test @ Tag ("integration") public @ interface IntegrationTest {}
The custom Integration Test annotation @ IntegrationTest can be used as follows:
@ IntegrationTest void runsWithCustomAnnotation () {/ / this gets executed / / even though `@ IntegrationTest` is not defined by JUnit}
Further, we can use more concise comments for the extension:
Target ({ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @ Retention (RetentionPolicy.RUNTIME) @ ExtendWith (ExternalDatabaseExtension.class) public @ interface Database {}
Now we can directly use the @ Database annotation without declaring that the test applies a specific extension @ ExtendWith (ExternalDatabaseExtension.class). And because we have added the annotation type ElementType.ANNOTATION_TYPE to the target types supported by the extension, the annotation can be further used and combined by us or others.
Examples
Suppose there is a scenario where I want to quantify the time it takes to run the test. First, you can create the annotation we want:
Target ({TYPE, METHOD, ANNOTATION_TYPE}) @ Retention (RetentionPolicy.RUNTIME) @ ExtendWith (BenchmarkExtension.class) public @ interface Benchmark {}
The annotations declare that they apply the BenchmarkExtension extension, which is what we are going to implement next. TODOLIST is as follows:
Calculate the run time of all test classes and save their start time before all tests are executed
Calculate the run time of each test method and save its start time before each test method is executed
After the execution of each test method, get its end time, calculate and output the run time of the test method
After all the test classes have been executed, get their end time, calculate and output the run time of all the tests
The above actions are valid only for all test classes or test methods annotated with @ BenchMark
The last need may not be found at a glance. If a method is not annotated with the @ Benchmark annotation, how could it be handled by our extension? One grammatical reason is that if an extension is applied to a class, it applies to all methods in the class by default. Therefore, if our requirement is to calculate the run time of the entire test class, but do not need to be specific to the run time of each individual method in the class, the test methods in the class must be manually excluded. We can do this by individually checking whether annotations are applied to each method.
Interestingly, the first four points of the requirement correspond to four of the extension points: BeforeAll, BeforeTestExecution, AfterTestExecution, and AfterAll. So what we need to do is to implement these four corresponding interfaces. The specific implementation is very simple, the translation of the above into code is:
Public class BenchmarkExtension implements BeforeAllExtensionPoint, BeforeTestExecutionCallback, AfterTestExecutionCallback, AfterAllExtensionPoint {private static final Namespace NAMESPACE = Namespace.of ("BenchmarkExtension"); @ Override public void beforeAll (ContainerExtensionContext context) {if (! shouldBeBenchmarked (context)) return; writeCurrentTime (context, LaunchTimeKey.CLASS) } @ Override public void beforeTestExecution (TestExtensionContext context) {if (! shouldBeBenchmarked (context)) return; writeCurrentTime (context, LaunchTimeKey.TEST);} @ Override public void afterTestExecution (TestExtensionContext context) {if (! shouldBeBenchmarked (context)) return; long launchTime = loadLaunchTime (context, LaunchTimeKey.TEST); long runtime = currentTimeMillis ()-launchTime Print ("Test", context.getDisplayName (), runtime);} @ Override public void afterAll (ContainerExtensionContext context) {if (! shouldBeBenchmarked (context)) return; long launchTime = loadLaunchTime (context, LaunchTimeKey.CLASS); long runtime = currentTimeMillis ()-launchTime; print ("Test container", context.getDisplayName (), runtime) } private static boolean shouldBeBenchmarked (ExtensionContext context) {return context.getElement () .map (el-> el.isAnnotationPresent (Benchmark.class)) .orElse (false);} private static void writeCurrentTime (ExtensionContext context, LaunchTimeKey key) {context.getStore (NAMESPACE) .put (key, currentTimeMillis ()) } private static long loadLaunchTime (ExtensionContext context, LaunchTimeKey key) {return (Long) context.getStore (NAMESPACE) .remove (key);} private static void print (String unit, String displayName, long runtime) {System.out.printf ("% s'% s'took% d ms.%n", unit, displayName, runtime) } private enum LaunchTimeKey {CLASS, TEST}} translator: Ah, this code is refreshing. "
There are several things to pay attention to in the above code. The first is the shouldBeBenchmarked method, which uses JUnit's API to get whether the current element is annotated (meta) with @ Benchmark annotations; second, the writeCurrentTime / loadLaunchTime method uses the store provided by Junit to write and read runtime.
This is the end of the introduction on "how to analyze the Extension Model extension model in JUnit 5". More related content can be searched for previous articles, hoping to help you answer questions and questions, please support the website!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.