-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for test pre-processors #766
Comments
Is this something, which cannot be done with a TestRule? |
I do not see how it can be don wit TestRules. |
@vasiliygagin, you can pass the test instance into a TestRule on creation, and get the method annotations from the Description object. There are ways this could be made better / easier, but I don't yet see that a new concept is needed. |
@dsaff, thank you for information about TestRules.May be it can be done with Test rules. But in reality think about it from the position of developer.I literally write thousands test cases a year. If I need to use Spring feature I specify RunWith. If I use Mockito, I specify another RunWith. Personally, I think, those Plugins would be the best thing that happened to JUnit in years. But who knows. |
@vasiliygagin just to make sure we understand, are you saying that this:
...is less cluttered than this:
Both are just one line of code. Admittedly, your solution allows you to specify multiple processors in one line, but it also doesn't provide a way for the test author to specify parameters. |
Thank you for example. With small corrections you are right.
vs:
Additional configuration can be passed to "processor/plugin" via another annotations. like @ContextConfiguration in Spring. Frankly, I'm not concerned about number of lines. I do about having extra fields. I can't speak for the developers of Spring and other frameworks. I can merely hope they will do provide SpringJUnitTestProcessor. I know I would have implemented it If Processors were available. Please do not take my words as a criticism against Rules in general. They just do not seem natural for plugging frameworks into a test case. |
Correct
There are limitations as to what values can be passed via an annotation (String, int, long, Class constants, Enum values, etc). Rules are actual classes, so they are more flexible (for example, you could use a Builder pattern to make a DSL for building the rule instance).
Why is having an extra field a problem?
Runners predate Rules, so this could be simply a matter of them not migrating to the new API, or them wanting to support older versions of JUnit. Both of these would also be an issue if we introduced a different API. Side note: I don't often see the need to use Mockito and Spring in the same test. I use Mockito to mock dependencies of pure JUnit tests. I use Spring for integration tests.
If your Rule only needs to be used with
Unfortunately,
I'm not taking your words as criticism. I'm trying to understand why the existing APIs ( |
BTW, I just found an page that documents how to use Rules for Mockito and Spring: http://www.alexecollins.com/content/tutorial-junit-rule/ If you did this, then you could use the Spring Runner and the Mockito Rule |
I would agree, with not mixing Mockito and Spring. I'm pretty sure that that was used as an example only. As for fields, they do not add value for the test itself, they serve some test wiring purpose (here is the need for learning curve). I personally prefer my test be focused on actual testing. It is easier to understand what test is doing, when there is no extra fields and methods. |
@vasiliygagin Thanks for the explanation. I have to admit I still don't understand the problem with fields. In addition to the limitations about what you can specify in an annotation, you can't get dynamic data out of an annotation (if, for example, you needed direct access to the Spring context, there wouldn't be a clean way to do that). But you aren't the first to want to use annotations to add custom behavior. I have a counter-proposal. Usage:
In this case
Note that this allows you to pass additional data that your extension point needs, and that data is clearly associated with the annotation. That seems better than this:
The
Note that this returns a rule, so we aren't creating a new interface for doing something we can do with existing APIs. See https://docs.google.com/document/d/1B6Z45BSGsY08rZqe2KUZTJ3RVsnUE1-HcXjl5MFm9ls/edit?pli=1 for a related discussion. |
@kcooney thanks for you interest in this discussion. Your proposal is pretty exciting. But first let me try to explain fields "problem". I'm programming in Java for 20+ years. In old times we were writing code starting from main and calling constructors and reading property files manually. Then frameworks like Spring came along and we pushed some low-level stuff to context / configuration files. Then with addition of annotations we are getting rid of those configuration files too. You know all of that. Just as FYI, If I need something from plugin / rule one way to get it to declare a field and annotate it with something like @resource (this annotation plugin specific). and let plugin/rule to inject it. (No low level code here :) Back to really important thing. I like the annotated annotation. I'm assuming that 2nd parameter "annotation" is passed so factory can extract annotation parameters.
What do you think? |
Fields can be used for all sorts of things. If you use dependency injection, I can use fields to store state, references to services used by the class or even configuration values. Users of JUnit have a large variety of coding styles and preferences. We unfortunately cannot cater to all of them. In the particular case of rules, some of the built-in rules do have state (for example, I've heard some people strongly argue against solutions that put data in annotations vs in simple Java classes. Whatever solution we choose, someone will be unhappy.
Exactly. We don't really need to pass in the annotation, since the factory is passed an instance of the class, and so it could fetch the annotations itself. The factory class would have to have a public zero-argument constructor. One drawback of this approach is if you need to define a factory, you need a factory class and define an annotation, which seems like one step too many. But from the user of the factories, the API is easy to understand.
Not all tests are associated with Java methods. Did you realize that the For Before we move forward on this idea, we would have to iron out a lot of details. For instance, do we allow something similar for class rules? Are the rules that are created via this mechanism run around rules that are defined via fields, or are the rules defined via fields run around the rules defined by annotations? What if the class has a base class that is annotated? What if it has multiple interfaces that are annotated? |
It feels like we are beating a dead horse here discussing coding preferences. I'll just say that sure they can be used to inject some services or logic. Lets move away from this part of discussion. No problem with rules having state, which can come from Annotation or explicitly constructed via @rule. When we are talking about API, I think we are on the same page. But just in case I will state this: When I was proposing this I was mostly thinking about BlockJunit4ClassRunner. Am Hearing you correctly, when you are talking about test cases not associated with Java methods, that you considering to implement this on a ParentRunner level? That would make total sense. About Descriptions. What annotations would it have in case of non-method base test cases? Probably none? I think that processors are to be created once per class. And at creation time JUnit should be able to construct them in any order. Though final user should be able to control the order in which TestRules are executed at run time. With respect to ClassRule which is static, Processors which are not static should be constructed later and probably their rules executed later too. With respect to method rules, If final user writes a rule should have access to the sate processor initialized, For example, processor can inject a datasource and user can write a rule which will use it to init a database. We can assume that processor will not need anything from customer rule implemented by user. So I think processors run 1st and rules after. As far as annotation inheritance goes, it seems to be a valuable thing. You do not want to run same processor twice. But you probably can aggregate all the annotations from superclasses and interfaces. I'm conserned about the order of procssor execution though. Not sure if Java before version 8 guarantees an annotation ordering when you call getAnnotations(). If not, then we might need something like
|
I'm swimming in this discussion a bit. Would it be possible to boil down to a 50-word paragraph that describes the problem that JUnit isn't currently solving, and what should be added in order to solve that problem? |
@vasiliygagin Lots to respond to there :-)
Possibly yes, or possibly we put the code in So my first question to you is this: can you give me a concrete example of behavior you want to change that couldn't be done via the
Why once per class, and not one per instance? Almost everything else (except the runner) is created once per test instance. The reason for that is we want to minimize the possibility of having the result of one test method depending on the outcome of a previous test method.
How would the test writer indicate the order? Currently, for I think getting a good answer to this would be required before we can go forward with a proposal to declare rules (or rule-like constructs) on tests via annotations.
The Javadoc for JDK 7 is sadly silent about the ordering. Do you see anything in JDK 8 that specifies an ordering?
That doesn't provide a clean way for the rule factories to get whatever data they might need. If we had the above API, the only way to get data to those factories would be to require more annotations on the class, and then the person reading the code would be left figuring out which annotations applied to which factories. Sorry for all the questions, but getting APIs like these correct is difficult. We have to think beyond the simple cases to the more complicated possible usages and how the APIs interact with existing APIs. |
I'll try to summarize rationale for proposed change. One can say that the problem is not with JUnit, but with framework developers. There was a thought that it just a matter of time, before framework developers will implement rules. They probably will. But I'm facing this problem for several years now, and I'm trying to facilitate the solution with this proposal. I also believe that not everything that can be done, must be done. Before runners and Rules we were doing tests. That can be done. We were producing way more unnecessary code though. It is better now. And it can be even better. For me as a test coder and occasional junit "extender" ideal solution would be a class level annotation:
There were variations on this . but these are for later discussions. |
@vasiliygagin, have you tried asking one or more of the teams you mention (Spring or Mockito or Unitils) why they haven't provided Rules yet? If it's just that they don't have enough time, then they won't suddenly get more time if we add yet a third extension point. If they've looked at it, and there's technical problems with Rules that prevent their use, then I'd love to either fix Rules, or consider additional extension points like yours, in order to solve those problems. |
Just found a Spring Jira related to this: https://jira.springsource.org/browse/SPR-7731
I'm not getting lucky, Am I? |
Even more relevant is spring-projects/spring-framework#222, which proposes a Rule for Spring. Doesn't look like anything's happened on it for 10 months. Would it work for your purposes? I honestly don't remember what happened with Neal's JUnit patches, I'm sorry to say. |
As far as I understand, this has been solved in JUnit 5 with the Extention API (see http://junit.org/junit5/docs/current/user-guide/#extensions). So I propose that the issue be closed. |
Spring does now provide Rules: Moreover, the JUnit Jupiter API was designed with composability of such pre-processors as one of its main design goals. So, indeed, I think this issue can be closed! Thanks, @bangnab for bringing it to my attention! 👍 |
Currently in order to apply any pre-processing, common approach is to override Runner (or subclass of it).
For example to add Spring Framework injection I'd use:
And for Unitils support I'd use:
But what happens when you need support for both?
Unitils actually is trying to solve this problem by supporting some (but not all) of Spring annotations.
It would benefit Java world if JUnit would support something like this:
In this case default BlockJUnit4ClassRunner will be used but listed processors will take care of their own responsibilities and would not have to know about each other.
Each of the processors would have to implement so common interface with a method like this:
If there is an interest in this features, I'd like to contribute.
The text was updated successfully, but these errors were encountered: