概览
- 主要理解下面几个东西
- ZeroCodeUnitRunner的实现
- @RunWith
- @Scenario
- Junit
- 后续TODO
- jackson databind
- google guice 参考 https://www.jianshu.com/p/9ac108d14608
Junit 4
- 用Description这个类描述 一个测试,以前是用 TestCase,Testsuite
- Runner 类 有
- getDescription
- run
- testCount 是从Descirption中获取
ParentRunner
- run方法的实现
- RunNotifier 入参,告知进度用,可以添加 监听
- 这里调Statement的evaluate,这里statemment会把 beforeclass, afterclass, 和classRules 注解的satement带进来
```java
public void run(final RunNotifier notifier) {
// …
try {
} catch (AssumptionViolatedException e) {Statement statement = classBlock(notifier);statement.evaluate();
// ….. protected Statement classBlock(final RunNotifier notifier) {
Statement statement = childrenInvoker(notifier); if (!areAllChildrenIgnored()) { statement = withBeforeClasses(statement); statement = withAfterClasses(statement); statement = withClassRules(statement); } return statement;} ```
- Statement 里这里未实现evaluate - childrenInvoker 会用 RunnerScheduler 调用 runChild,通过getFilteredChildren- run方法的实现
�获取的child 依次调用, runChild是抽象方法
- 这里 childrenLock 锁在实现filter 方法和sort 方法,及getFilteredChildren使用
- BlockJUnit4ClassRunner
- it is now the default test class runner, but it should have exactly
the same behavior as the old test class runner ({@code JUnit4ClassRunner}). - 但有些变化
- 再执行流程插入新操作有更简单的实现
- 重要实现runChild
- 获得Description 实例,describeChild-》Description.createTestDescription
```java
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description = describeChild(method);
if (isIgnored(method)) {
} else {notifier.fireTestIgnored(description); // 忽略掉ignore注解的方法
} }runLeaf(methodBlock(method), description, notifier);
- 获得Description 实例,describeChild-》Description.createTestDescription
```java
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description = describeChild(method);
if (isIgnored(method)) {
//……. protected Description describeChild(FrameworkMethod method) { Description description = methodDescriptions.get(method);
if (description == null) {
description = Description.createTestDescription(getTestClass().getJavaClass(),
testName(method), method.getAnnotations());
methodDescriptions.putIfAbsent(method, description);
}
return description;
}
- MethodBlock //这里有关键的逻辑来组装 运行的statement
- 这里加入了 各种注解方法的 statement
- 这里会用createTest创建测试类
```java
protected Statement methodBlock(FrameworkMethod method) {
Object test;
try {
test = new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest();
}
}.run();
} catch (Throwable e) {
return new Fail(e);
}
Statement statement = methodInvoker(method, test);
statement = possiblyExpectingExceptions(method, test, statement);
statement = withPotentialTimeout(method, test, statement);
statement = withBefores(method, test, statement);
statement = withAfters(method, test, statement);
statement = withRules(method, test, statement);
return statement;
}
runLeaf实现
- 通知 和执行
- notifier
- statement
protected final void runLeaf(Statement statement, Description description, RunNotifier notifier) { EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description); eachNotifier.fireTestStarted(); try { statement.evaluate(); } catch (AssumptionViolatedException e) { //...//
InvokeMethod ,
- 这里的 传入runLeaf的是 InokeMethod,Statement的子类
- 它的 evaluate 是通过FrameworkMethod 的invokeExplosively 实现的
- 而invokeExplosively 是通过反射包 Method的invoke方法实现的
ZeroCodeUnitRunner
- 继承关系
- BlockJUnit4ClassRunner
- ParentRunner 实现 Filterable, Sortable 接口
- Runner 实现接口 Describable
- run 方法实现
- 主要还是调用父类的run
- 添加了一个 生成报告的listener
runChild
- 覆盖了父类的实现
- 识别 @JsonTestCase 与 @Scenario,有的调用 runLeafJsonTest
没有 走老路 runLeafJUnitTest ```java protected void runChild(FrameworkMethod method, RunNotifier notifier) {
final Description description = describeChild(method); JsonTestCase jsonTestCaseAnno = method.getMethod().getAnnotation(JsonTestCase.class); if(jsonTestCaseAnno == null){
jsonTestCaseAnno = evalScenarioToJsonTestCase(method.getMethod().getAnnotation(Scenario.class));}
if (isIgnored(method)) {
notifier.fireTestIgnored(description);} else if (jsonTestCaseAnno != null) {
runLeafJsonTest(notifier, description, jsonTestCaseAnno);} else {
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // It is an usual Junit test, not the JSON test case // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= runLeafJUnitTest(methodBlock(method), description, notifier);}
- runLeafJsonTest
- 配置文件转java实例,
- smartUtils.scenarioFileToJava(currentTestCase, ScenarioSpec.class);
- jackson databind , ObjectMapper readValue //TODO
```java
public <T> T scenarioFileToJava(String scenarioFile, Class<T> clazz) throws IOException {
if (scenarioFile.endsWith(".yml") || scenarioFile.endsWith(".yaml")) {
return yamlMapper.readValue(readYamlAsString(scenarioFile), clazz);
}
return mapper.readValue(readJsonAsString(scenarioFile), clazz);
}
- multiStepsRunner.runScenario
- ZeroCodeMultiStepsScenarioRunner 是个接口,行为由初始化塞入的实现类实现
�
notifier.fireTestStarted(description); notifier.fireTestFailure(new Failure(description, ioEx)); notifier.fireTestFinished(description);
private void runLeafJsonTest(RunNotifier notifier, Description description, JsonTestCase jsonTestCaseAnno) { if (jsonTestCaseAnno != null) { currentTestCase = jsonTestCaseAnno.value(); } notifier.fireTestStarted(description); LOGGER.debug("### Running currentTestCase : " + currentTestCase); ScenarioSpec child = null; try { child = smartUtils.scenarioFileToJava(currentTestCase, ScenarioSpec.class); LOGGER.debug("### Found currentTestCase : -" + child); passed = multiStepsRunner.runScenario(child, notifier, description); } catch (Exception ioEx) { ioEx.printStackTrace(); notifier.fireTestFailure(new Failure(description, ioEx)); } testRunCompleted = true; if (passed) { LOGGER.info(String.format("\n**FINISHED executing all Steps for [%s] **.\nSteps were:%s", child.getScenarioName(), child.getSteps().stream() .map(step -> step.getName() == null ? step.getId() : step.getName()) .collect(Collectors.toList()))); } notifier.fireTestFinished(description); }�
ZeroCodeMultiStepsScenarioRunnerImpl
runScenario
- ioWriterBuilder 写报告
- resultReportBuilder 报告
- parameterizedProcessor.resolveParameterized 解析 Scenario参数
executeSteps
- 遍历Scenario 每个step
对每个step执行 executeRetryWithSteps
解析出引用的step,重新组装step
//.... thisStep = extFileProcessor.resolveExtJsonFile(thisStep); List<Step> thisSteps = extFileProcessor.createFromStepFile(thisStep, thisStep.getId()); if(null == thisSteps || thisSteps.isEmpty()) thisSteps.add(thisStep); //....对每个step执行executeRetry
重头戏executeRetry
- StepExecutionState 存储step的运行状态
- stepExecutionState.addRequest(resolvedRequestJson); 把解析出的json存到请求
- 获取重试信息
- 执行 executionResult = executeApi(logPrefixRelationshipId, thisStep, resolvedRequestJson, scenarioExecutionState);
- logging repsonse
- handle assertion
- logging assertion
- 根据重试信息判断是否继续循环
- 根据是否忽略,处理失败与成功 assersion
Build step report for each step. 在finally 里
executeApi
根据 apiType 调用不同方法
- java
- restful
- kafka
-
ApiServiceExecutorImpl //apiExecutor的实现
使用 google guice 注入
- JavaMethodExecutor的实现类 JavaMethodExecutorImp
- HttpApiExecutor 的实现类 HttpApiExecutorImp
- BasicKafkaClient
�
�
�
�
�
�
