2017-10-20 113 views
2

JUnit 5 API中有几个扩展点可用。如何测试扩展实现

例如:

几乎所有这些扩展采取某种形式的ExtensionContext,其提供了访问test classtest methodtest instance,能力publish report entriesstore values和其他功能。它们中的很多直接在类实例上运行

大多数实现可能会使用reflection using ReflectionSupportannotation discovery using AnnotationSupport的某些组合,这些都是静态方法,这使得它们很难模拟。

现在,假设我已经编写了ExecutionCondition的实现,或者实现了BeforeEachCallbackAfterEachCallback的扩展 - 我如何有效地测试它们?

我想我可以模拟ExtensionContext,但这似乎不是一种有效行使不同代码路径的好方法。

JUnit内部有ExecutionEventRecorder,它与JupiterTestEngine implementation for execution (which is @API(status=INTERNAL))一起用于选择和执行测试,然后perform assertions on the resulting events

我可以使用相同的模式,因为JupiterTestEngine上的public可见性。


这里有几个具体的例子来证明:

  1. ExecutionCondition,使基于系统属性测试。这可以通过一些思考来测试,并使用TestInfo中的@BeforeEach@AfterEach风格进行测试,但在并行测试执行到来时,处理和可能出现的问题似乎更加复杂。这个例子展示了一个例子,“我如何提供真实生活ExtensionContext并对结果进行断言”。

    @Target(ElementType.METHOD) 
    @Retention(RetentionPolicy.RUNTIME) 
    @ExtendWith(SystemPropertyCondition.class) 
    public @interface SystemProperty { 
        String key(); 
        String value(); 
    } 
    
    public class SystemPropertyCondition implements ExecutionCondition { 
        @Override 
        public ConditionEvaluationResult evaluateExecutionCondition(final ExtensionContext context) { 
        return context.getTestMethod() 
         .flatMap(element -> AnnotationSupport.findAnnotation(element, SystemProperty.class)) 
         .map(systemProperty -> { 
          if (systemProperty.value().equals(System.getProperty(systemProperty.key()))) { 
          return ConditionEvaluationResult.enabled("Property is available"); 
          } else { 
          return ConditionEvaluationResult.disabled("Property not equal"); 
          } 
         }) 
         .orElseGet(() -> ConditionEvaluationResult.enabled("No annotations")); 
        } 
    } 
    
  2. ExecutionCondition,仅在一周的提供每天运行。如何注入测试时钟?如果条件是预期结果,你将如何测试?我猜想可能会将某些状态/实现置于ExtensionContext.Store之间,从而允许在扩展之间传递某种状态(例如,也可以使用BeforeEachCallback,这可能是正确的路径,但这将取决于扩展的顺序我不相信BeforeEachCallbackExecutionCondition之前被调用,所以路径可能不是正确的。这个例子显示了“我如何注入依赖关系”,并且也显示了与前面提供ExtensionContext示例相同的问题和断言该结果。

    @ExtendWith(RunOnDayCondition.class) 
    public @interface RunOnDay { 
        DayOfWeek[] value(); 
    } 
    
    final class RunOnDayCondition implements ExecutionCondition { 
    
        private static final ConditionEvaluationResult DEFAULT = disabled(
         RunOnDay.class + " is not present" 
    ); 
    
        @Override 
        public ConditionEvaluationResult evaluateExecutionCondition(final ExtensionContext context) { 
        return context.getElement() 
         .flatMap(annotatedElement -> findAnnotation(annotatedElement, RunOnDay.class)) 
         .map(RunOnDay::value) 
         .map(RunOnDayCondition::evaluateIfRunningOnDay) 
         .orElse(DEFAULT); 
        } 
    
        private static ConditionEvaluationResult evaluateIfRunningOnDay(final DayOfWeek[] days) { 
        // TODO: How would you inject a test clock? 
        final DayOfWeek currentDay = LocalDate.now().getDayOfWeek(); 
        final boolean runningInday = Stream.of(days).anyMatch(currentDay::equals); 
    
        if (runningInday) { 
         return enabled("Current day is " + currentDay + ", in the specified days of " + Arrays.toString(days)); 
        } else { 
         return disabled("Current day is " + currentDay + ", not in the specified days of " + Arrays.toString(days)); 
        } 
        } 
    } 
    
  3. ,设置了一个临时目录,提供它作为一个参数,然后在测试后清除它的扩展,这将使用ParameterResolver,BeforeEachCallback,AfterEachCallbackExtensionContext.Store。这个例子表明一个扩展实现可以使用多个扩展点,并且可以利用该商店来跟踪状态。

扩展测试的自定义测试引擎是否是正确的方法?

那么,如何测试各种扩展实现而不依赖于内部API?

回答

0

我知道这不是一个完整的答案(但我现在在手机上,你的问题非常复杂)...尝试但是,如果 this project可以帮助你。

如果你限制一点你的问题,我可以尝试帮助你更好