Skip to content

Commit 9261431

Browse files
committed
Updated Junit reporter to conform with Junit XSD
1 parent 57cba82 commit 9261431

16 files changed

Lines changed: 142 additions & 71 deletions

docs/images/junit_errors.png

17.8 KB
Loading

docs/images/junit_summary.png

20 KB
Loading
-41.3 KB
Binary file not shown.
-51.2 KB
Binary file not shown.

docs/userguide/reporters.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,24 +39,26 @@ Example outputs from documentation reporter.
3939
![doc_reporter_outputs](../images/documentation_reporter_color.png)
4040

4141

42-
# XUnit reporter
42+
# JUnit reporter
4343

44-
Most of continuous integration servers (like Jenkins) are capable of consuming unit test execution results in [XUnit/JUnit](https://en.wikipedia.org/wiki/XUnit) format.
45-
The `ut_xunit_reporter` is producing outcomes as XUnit-compatible XML unit test report, that can be used by CI servers to display their custom reports and provide metrics (like tests execution trends).
44+
Most of continuous integration servers (like Jenkins) are capable of consuming unit test execution results in [JUnit](https://en.wikipedia.org/wiki/JUnit) format.
45+
The `ut_junit_reporter` in earlier version referred as ut_xunit_reporter is producing outcomes as JUnit-compatible XML unit test report, that can be used by CI servers to display their custom reports and provide metrics (like tests execution trends).
46+
Please note that in previous versions it was called ut_xunit_reporter and for backward compatibility that name still exists.
4647

47-
Invocation of tests with XUnit reporter.
48+
Invocation of tests with JUnit reporter.
4849

49-
`exec ut.run(ut_xunit_reporter());`
50+
`exec ut.run(ut_junit_reporter());`
5051

51-
The `ut_xunit_reporter` doesn't accept any arguments.
52+
The `ut_junit_reporter` doesn't accept any arguments.
5253

53-
Example of xunit report integrated with [Jenkins CI](https://jenkins.io/)
54+
Example of junit report integrated with [Jenkins CI](https://jenkins.io/)
5455

55-
![xunit_reporter_outputs](../images/xunit_reporter_jenkins.png)
56+
![junit_reporter_outputs](../images/junit_summary.png)
5657

5758
Example of failure report details
5859

59-
![xunit_reporter_outputs](../images/xunit_reporter_jenkins_errors.png)
60+
![junit_reporter_outputs](../images/junit_errors.png)
61+
6062

6163

6264
# Teamcity reporter

source/api/ut_xunit_reporter.syn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
create synonym ut_xunit_reporter for ut_junit_reporter;

source/create_synonyms_and_grants_for_public.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ grant execute on &&ut3_owner..ut_match to public;
4848
grant execute on &&ut3_owner..ut to public;
4949
grant execute on &&ut3_owner..ut_runner to public;
5050
grant execute on &&ut3_owner..ut_teamcity_reporter to public;
51-
grant execute on &&ut3_owner..ut_xunit_reporter to public;
51+
grant execute on &&ut3_owner..ut_junit_reporter to public;
5252
grant execute on &&ut3_owner..ut_tfs_junit_reporter to public;
5353
grant execute on &&ut3_owner..ut_documentation_reporter to public;
5454
grant execute on &&ut3_owner..ut_coverage_html_reporter to public;
@@ -109,6 +109,7 @@ create public synonym ut for &&ut3_owner..ut;
109109
create public synonym ut_runner for &&ut3_owner..ut_runner;
110110
create public synonym ut_teamcity_reporter for &&ut3_owner..ut_teamcity_reporter;
111111
create public synonym ut_xunit_reporter for &&ut3_owner..ut_xunit_reporter;
112+
create public synonym ut_junit_reporter for &&ut3_owner..ut_junit_reporter;
112113
create public synonym ut_tfs_junit_reporter for &&ut3_owner..ut_tfs_junit_reporter;
113114
create public synonym ut_documentation_reporter for &&ut3_owner..ut_documentation_reporter;
114115
create public synonym ut_coverage_html_reporter for &&ut3_owner..ut_coverage_html_reporter;

source/create_synonyms_and_grants_for_user.sql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ grant execute on &&ut3_owner..ut_match to &ut3_user;
6868
grant execute on &&ut3_owner..ut to &ut3_user;
6969
grant execute on &&ut3_owner..ut_runner to &ut3_user;
7070
grant execute on &&ut3_owner..ut_teamcity_reporter to &ut3_user;
71-
grant execute on &&ut3_owner..ut_xunit_reporter to &ut3_user;
71+
grant execute on &&ut3_owner..ut_junit_reporter to &ut3_user;
7272
grant execute on &&ut3_owner..ut_tfs_junit_reporter to &ut3_user;
7373
grant execute on &&ut3_owner..ut_documentation_reporter to &ut3_user;
7474
grant execute on &&ut3_owner..ut_coverage_html_reporter to &ut3_user;
@@ -127,7 +127,8 @@ create or replace synonym &ut3_user..match for &&ut3_owner..match;
127127
create or replace synonym &ut3_user..ut for &&ut3_owner..ut;
128128
create or replace synonym &ut3_user..ut_runner for &&ut3_owner..ut_runner;
129129
create or replace synonym &ut3_user..ut_teamcity_reporter for &&ut3_owner..ut_teamcity_reporter;
130-
create or replace synonym &ut3_user..ut_xunit_reporter for &&ut3_owner..ut_xunit_reporter;
130+
create or replace synonym &ut3_user..ut_xunit_reporter for &&ut3_owner..ut_junit_reporter;
131+
create or replace synonym &ut3_user..ut_junit_reporter for &&ut3_owner..ut_junit_reporter;
131132
create or replace synonym &ut3_user..ut_tfs_junit_reporter for &&ut3_owner..ut_tfs_junit_reporter;
132133
create or replace synonym &ut3_user..ut_documentation_reporter for &&ut3_owner..ut_documentation_reporter;
133134
create or replace synonym &ut3_user..ut_coverage_html_reporter for &&ut3_owner..ut_coverage_html_reporter;

source/install.sql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,8 @@ prompt Installing PLSQL profiler objects into &&ut3_owner schema
237237
@@install_component.sql 'reporters/ut_teamcity_reporter_helper.pks'
238238
@@install_component.sql 'reporters/ut_teamcity_reporter_helper.pkb'
239239
@@install_component.sql 'reporters/ut_teamcity_reporter.tpb'
240-
@@install_component.sql 'reporters/ut_xunit_reporter.tps'
241-
@@install_component.sql 'reporters/ut_xunit_reporter.tpb'
240+
@@install_component.sql 'reporters/ut_junit_reporter.tps'
241+
@@install_component.sql 'reporters/ut_junit_reporter.tpb'
242242
@@install_component.sql 'reporters/ut_tfs_junit_reporter.tps'
243243
@@install_component.sql 'reporters/ut_tfs_junit_reporter.tpb'
244244
@@install_component.sql 'reporters/ut_sonar_test_reporter.tps'
@@ -269,6 +269,7 @@ prompt Installing PLSQL profiler objects into &&ut3_owner schema
269269
@@install_component.sql 'api/equal.syn'
270270
@@install_component.sql 'api/have_count.syn'
271271
@@install_component.sql 'api/match.syn'
272+
@@install_component.sql 'api/ut_xunit_reporter.syn'
272273

273274
set linesize 200
274275
set define on
Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
create or replace type body ut_xunit_reporter is
1+
create or replace type body ut_junit_reporter is
22
/*
33
utPLSQL - Version 3
44
Copyright 2016 - 2017 utPLSQL Project
@@ -15,14 +15,13 @@ create or replace type body ut_xunit_reporter is
1515
See the License for the specific language governing permissions and
1616
limitations under the License.
1717
*/
18-
19-
constructor function ut_xunit_reporter(self in out nocopy ut_xunit_reporter) return self as result is
18+
constructor function ut_junit_reporter(self in out nocopy ut_junit_reporter) return self as result is
2019
begin
2120
self.init($$plsql_unit);
2221
return;
2322
end;
2423

25-
overriding member procedure after_calling_run(self in out nocopy ut_xunit_reporter, a_run in ut_run) is
24+
overriding member procedure after_calling_run(self in out nocopy ut_junit_reporter, a_run in ut_run) is
2625
l_suite_id integer := 0;
2726
l_tests_count integer := a_run.results_count.disabled_count + a_run.results_count.success_count +
2827
a_run.results_count.failure_count + a_run.results_count.errored_count;
@@ -35,9 +34,11 @@ create or replace type body ut_xunit_reporter is
3534
procedure print_test_elements(a_test ut_test) is
3635
l_lines ut_varchar2_list;
3736
l_output clob;
37+
l_cdata_tag_start varchar2(10) := '<![CDATA[';
38+
l_cddata_tag_end varchar2(10) := ']]>';
3839
begin
39-
self.print_text('<testcase classname="' || dbms_xmlgen.convert(get_path(a_test.path, a_test.name)) || '" ' || ' assertions="' ||
40-
nvl(a_test.all_expectations.count,0) || '"' || self.get_common_item_attributes(a_test) || case when
40+
self.print_text('<testcase classname="' || dbms_xmlgen.convert(get_path(a_test.path, a_test.name)) || '"' || ' assertions="' ||
41+
nvl(a_test.all_expectations.count,0) || '"' || self.get_common_test_attributes(a_test) || case when
4142
a_test.result != ut_utils.gc_success then
4243
' status="' || ut_utils.test_result_to_char(a_test.result) || '"' end || '>');
4344
if a_test.result = ut_utils.gc_disabled then
@@ -46,16 +47,22 @@ create or replace type body ut_xunit_reporter is
4647
if a_test.result = ut_utils.gc_error then
4748
self.print_text('<error>');
4849
self.print_text('<![CDATA[');
49-
self.print_clob(ut_utils.table_to_clob(a_test.get_error_stack_traces()));
50+
self.print_clob(
51+
replace(replace(ut_utils.table_to_clob(a_test.get_error_stack_traces()),l_cdata_tag_start,''),l_cddata_tag_end,'')
52+
);
5053
self.print_text(']]>');
5154
self.print_text('</error>');
5255
elsif a_test.result > ut_utils.gc_success then
5356
self.print_text('<failure>');
5457
self.print_text('<![CDATA[');
5558
for i in 1 .. a_test.failed_expectations.count loop
59+
5660
l_lines := a_test.failed_expectations(i).get_result_lines();
61+
5762
for j in 1 .. l_lines.count loop
58-
self.print_text(l_lines(j));
63+
self.print_text(
64+
replace(replace(l_lines(j),l_cdata_tag_start,''),l_cddata_tag_end,'')
65+
);
5966
end loop;
6067
self.print_text(a_test.failed_expectations(i).caller_info);
6168
end loop;
@@ -70,6 +77,17 @@ create or replace type body ut_xunit_reporter is
7077
self.print_clob(l_output);
7178
self.print_text(']]>');
7279
self.print_text('</system-out>');
80+
else
81+
self.print_text('<system-out/>');
82+
end if;
83+
if a_test.before_test.get_error_stack_trace() is not null or a_test.after_test.get_error_stack_trace() is not null then
84+
self.print_text('<system-err>');
85+
self.print_text('<![CDATA[');
86+
self.print_text(trim(a_test.before_test.get_error_stack_trace()) || trim(chr(10) || chr(10) || a_test.after_test.get_error_stack_trace()));
87+
self.print_text(']]>');
88+
self.print_text('</system-err>');
89+
else
90+
self.print_text('<system-err/>');
7391
end if;
7492
self.print_text('</testcase>');
7593
end;
@@ -78,10 +96,35 @@ create or replace type body ut_xunit_reporter is
7896
l_tests_count integer := a_suite.results_count.disabled_count + a_suite.results_count.success_count +
7997
a_suite.results_count.failure_count + a_suite.results_count.errored_count;
8098
l_suite ut_suite;
99+
l_tests ut_suite_items := ut_suite_items();
81100
begin
82101
a_suite_id := a_suite_id + 1;
83102
self.print_text('<testsuite tests="' || l_tests_count || '"' || ' id="' || a_suite_id || '"' || ' package="' ||
84-
dbms_xmlgen.convert(a_suite.path) || '" ' || self.get_common_item_attributes(a_suite) || '>');
103+
dbms_xmlgen.convert(a_suite.path) || '" ' || self.get_common_suite_attributes(a_suite) || '>');
104+
105+
-- Becasue testsuites have to appear before test we capture test and leave it for later.
106+
for i in 1 .. a_suite.items.count loop
107+
if a_suite.items(i) is of(ut_test) then
108+
l_tests.extend;
109+
l_tests(l_tests.last) := treat(a_suite.items(i) as ut_test);
110+
elsif a_suite.items(i) is of(ut_logical_suite) then
111+
print_suite_elements(treat(a_suite.items(i) as ut_logical_suite), a_suite_id);
112+
end if;
113+
end loop;
114+
115+
-- Now when all testsuite are printed do the testcases.
116+
for i in 1 .. l_tests.count loop
117+
print_test_elements(treat(l_tests(i) as ut_test));
118+
end loop;
119+
120+
/*for i in 1 .. a_suite.items.count loop
121+
if a_suite.items(i) is of(ut_test) then
122+
print_test_elements(treat(a_suite.items(i) as ut_test));
123+
elsif a_suite.items(i) is of(ut_logical_suite) then
124+
print_suite_elements(treat(a_suite.items(i) as ut_logical_suite), a_suite_id);
125+
end if;
126+
end loop;*/
127+
85128
if a_suite is of(ut_suite) then
86129
l_suite := treat(a_suite as ut_suite);
87130

@@ -91,6 +134,8 @@ create or replace type body ut_xunit_reporter is
91134
self.print_clob(l_suite.get_serveroutputs());
92135
self.print_text(']]>');
93136
self.print_text('</system-out>');
137+
else
138+
self.print_text('<system-out/>');
94139
end if;
95140

96141
if l_suite.before_all.error_stack is not null or l_suite.after_all.error_stack is not null then
@@ -99,36 +144,36 @@ create or replace type body ut_xunit_reporter is
99144
self.print_text(trim(l_suite.before_all.error_stack) || trim(chr(10) || chr(10) || l_suite.after_all.error_stack));
100145
self.print_text(']]>');
101146
self.print_text('</system-err>');
147+
else
148+
self.print_text('<system-err/>');
102149
end if;
103150
end if;
104-
105-
for i in 1 .. a_suite.items.count loop
106-
if a_suite.items(i) is of(ut_test) then
107-
print_test_elements(treat(a_suite.items(i) as ut_test));
108-
elsif a_suite.items(i) is of(ut_logical_suite) then
109-
print_suite_elements(treat(a_suite.items(i) as ut_logical_suite), a_suite_id);
110-
end if;
111-
end loop;
112151
self.print_text('</testsuite>');
113152
end;
114153
begin
115154
l_suite_id := 0;
116-
self.print_text('<testsuites tests="' || l_tests_count || '"' || self.get_common_item_attributes(a_run) || '>');
155+
self.print_text('<testsuites tests="' || l_tests_count || '"' || self.get_common_suite_attributes(a_run) || '>');
117156
for i in 1 .. a_run.items.count loop
118157
print_suite_elements(treat(a_run.items(i) as ut_logical_suite), l_suite_id);
119158
end loop;
120159
self.print_text('</testsuites>');
121160
end;
122161

123-
member function get_common_item_attributes(a_item ut_suite_item) return varchar2 is
162+
member function get_common_suite_attributes(a_item ut_suite_item) return varchar2 is
124163
begin
125-
return ' skipped="' || a_item.results_count.disabled_count
126-
|| '" error="' || a_item.results_count.errored_count
127-
|| '" failure="' || a_item.results_count.failure_count
164+
return ' disabled="' || a_item.results_count.disabled_count
165+
|| '" errors="' || a_item.results_count.errored_count
166+
|| '" failures="' || a_item.results_count.failure_count
128167
|| '" name="' || dbms_xmlgen.convert(nvl(a_item.description, a_item.name))
129168
|| '" time="' || ut_utils.to_xml_number_format(a_item.execution_time()) || '" ';
130169
end;
131170

171+
member function get_common_test_attributes(a_item ut_suite_item) return varchar2 is
172+
begin
173+
return ' name="' || dbms_xmlgen.convert(nvl(a_item.description, a_item.name))
174+
|| '" time="' || ut_utils.to_xml_number_format(a_item.execution_time()) || '" ';
175+
end;
176+
132177
overriding member function get_description return varchar2 as
133178
begin
134179
return 'Provides outcomes in a format conforming with JUnit 4 and above as defined in: https://gist.github.com/kuzuha/232902acab1344d6b578';

0 commit comments

Comments
 (0)