View Javadoc

1   /*
2    * Copyright 2010-2011 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.callbackparams.junit4;
18  
19  import java.lang.reflect.InvocationTargetException;
20  import java.lang.reflect.Method;
21  import java.util.ArrayList;
22  import java.util.List;
23  import org.callbackparams.combine.reflect.CallbackRecordsFactory;
24  import org.junit.runner.Description;
25  import org.junit.runner.notification.RunNotifier;
26  import org.junit.runners.BlockJUnit4ClassRunner;
27  import org.junit.runners.ParentRunner;
28  import org.junit.runners.model.FrameworkMethod;
29  import org.junit.runners.model.InitializationError;
30  
31  /**
32   * The very simpliest use-case for callback-params is when the parameters are
33   * self-contained, i.e. they have at least one regular JUnit4 test-method
34   * (takes zero arguments) which can perform its test all on its own without
35   * input from any containing callback-params test-method. For this case it
36   * could be over-kill to have the parameters go the detour
37   * through some customized CallbackParams test-class. However, by using this
38   * test-runner class for the enum that provides these parameters it is possible
39   * to run the self-contained test-methods without writing any additional code.
40   * <br/><br/>
41   * It is not absolutly mandatory for the test-class to be an enum. - The
42   * important thing is that it has a static method "values()" that returns the
43   * test-instances. I.e. for an enum-class the test-instances would then turn out
44   * to be the enum-constants.
45   * <br/><br/>
46   * The implementation is pretty much the simpliest possible by taking advantage
47   * of features available in the runner-class BlockJUnit4ClassRunner. The
48   * downside of this is that it demands JUnit version 4.5 or later ...
49   *
50   * @author Henrik Kaipe
51   */
52  public class EnumRunner extends ParentRunner<BlockJUnit4ClassRunner> {
53  
54      private final List<BlockJUnit4ClassRunner> children;
55      
56      public EnumRunner(Class<? extends Enum> testClass)
57      throws InitializationError {
58          super(testClass);
59          Object[] enumValues = getValues(testClass);
60          this.children = new ArrayList<BlockJUnit4ClassRunner>(enumValues.length);
61          for (Object testInstance : enumValues) {
62              this.children.add(new SingleEnumValueRunner(testClass, testInstance));
63          }
64      }
65  
66      private Object[] getValues(Class<? extends Enum> testClass) {
67          try {
68              Method valuesMethod = CallbackRecordsFactory
69                      .valuesMethodFor(testClass);
70              try {
71                  valuesMethod.setAccessible(true);
72              } catch (SecurityException ignoreAndContinueAnyway) {}
73              return (Object[]) valuesMethod.invoke(null);
74  
75          } catch (NoSuchMethodException ex) {
76              throw new Error("When using EnumRunner for a non-enum class, the "
77                      + "class must have a method 'values()' that provides the "
78                      + "test instances!", ex);
79          } catch (IllegalAccessException ex) {
80              throw new Error(ex);
81          } catch (InvocationTargetException ite) {
82              throw new Error(ite.getTargetException());
83          }
84      }
85  
86      @Override
87      protected List<BlockJUnit4ClassRunner> getChildren() {
88          return this.children;
89      }
90  
91      @Override
92      protected Description describeChild(BlockJUnit4ClassRunner child) {
93          return child.getDescription();
94      }
95  
96      @Override
97      protected void runChild(
98              BlockJUnit4ClassRunner child, RunNotifier notifier) {
99          child.run(notifier);
100     }
101 
102     private static class SingleEnumValueRunner
103     extends BlockJUnit4ClassRunner {
104         
105         private final Object testValue;
106 
107         public SingleEnumValueRunner(Class<?> testClass, Object testValue)
108         throws InitializationError {
109             super(testClass);
110             this.testValue = testValue;
111         }
112 
113         @Override
114         protected Object createTest() {
115             return testValue;
116         }
117 
118         @Override
119         protected String testName(FrameworkMethod method) {
120             return super.testName(method) + '[' + testValue + ']';
121         }
122 
123         @Override
124         /**
125          * Overridden to make sure that no constructor validations are performed.
126          */
127         protected void collectInitializationErrors(List<Throwable> errors) {
128             validateInstanceMethods(errors);
129         }
130     }
131 }