How can I turn the output of my iOS UIAutomation tests into JUnit style output for Jenkins?
Asked Answered
G

4

9

I am using UIAutomation scripts to test my iOS application. I've managed to get the scripts running from the command line but now I need to convert the output (pass/fails) in a format that Jenkins can understand, ideally JUnit style.

Has anyone written any scripts to do this before I try & write one?

Many thanks

Gwenngwenneth answered 6/1, 2012 at 21:46 Comment(3)
even i am trying the same thing. did you integrate all steps together through jenkins..code checkin --> build --> running your atomation scripts --> fetching test result out of it please let me know if you tried above thingsBolster
I searched extensively, and haven't found anything. We just have something hacked together for our own purposes. Please make something reuseable. I'll use it.Issi
What do you mean by "convert it to a format jenkins understands" ? if you want jenkins just to tell if the build passed or failed according to the tests it can be done by parsing it in a shell script and run it as a build step. otherwise, if you want to use jenkins report publishing abilities and get a nice report at the end you have to convert it to Junit xml style, i think its the only thing jenkins knows how to parseHaehaecceity
T
3

Maybe you can have a look at : https://github.com/shaune/jasmine-ios-acceptance-tests

Edit : I've also avoided to use jasmine. To 'listen' start, pass and fail test, I've simply replaced UIALogger.logStart, UIALogger.logFail and UIALogger.logPass :

(function () {
    // An anonymous function wrapper helps you keep oldSomeFunction private     
    var oldSomeFunction = UIALogger.logStart;
    UIALogger.logStart = function () {
    //UIALogger.logDebug("intercepted a logStart : " + arguments);
    OKJunitLogger.reportTestSuiteStarting(arguments[0]);
    oldSomeFunction.apply(this, arguments);
    }
})();
Tetrabrach answered 12/4, 2012 at 20:49 Comment(4)
Hi this looks very cool but I was trying to avoid using Jasmine and instead use the tuneup.js library to keep things simple. I have marked your answer as correct as it meets my requirement though, thank you.Gwenngwenneth
I just edited my answer to show you the solution I choosed to implementPreconize
@kenji Can you explain a bit on how and where you use this function?Tortoni
I replace logStart with an anonymous function, and this function call the real logStart (after reporting test suite starting).Preconize
C
1

I bealive you will find what you need here: https://github.com/shinetech/jenkins-ios-example

What you're interest in is the script call "ocunit2junit.rb"

I used it for a project, it work very well.

#!/usr/bin/ruby
#
# ocunit2junit.rb was written by Christian Hedin <[email protected]>
# Version: 0.1 - 30/01 2010
# Usage: 
# xcodebuild -yoursettings | ocunit2junit.rb
# All output is just passed through to stdout so you don't miss a thing!
# JUnit style XML-report are put in the folder specified below.
#
# Known problems:
# * "Errors" are not cought, only "warnings".
# * It's not possible to click links to failed test in Hudson
# * It's not possible to browse the source code in Hudson
#
# Acknowledgement:
# Big thanks to Steen Lehmann for prettifying this script.
################################################################
# Edit these variables to match your system
#
#
# Where to put the XML-files from your unit tests
TEST_REPORTS_FOLDER = "test-reports"
#
#
# Don't edit below this line
################################################################

require 'time'
require 'FileUtils'
require 'socket'

class ReportParser

  attr_reader :exit_code

  def initialize(piped_input)
    @piped_input = piped_input
    @exit_code = 0

    FileUtils.rm_rf(TEST_REPORTS_FOLDER)
    FileUtils.mkdir(TEST_REPORTS_FOLDER)
    parse_input
  end

  private

  def parse_input
    @piped_input.each do |piped_row|
      puts piped_row
      case piped_row

        when /Test Suite '(\S+)'.*started at\s+(.*)/
          t = Time.parse($2.to_s)
          handle_start_test_suite(t)

        when /Test Suite '(\S+)'.*finished at\s+(.*)./
          t = Time.parse($2.to_s)
          handle_end_test_suite($1,t)     

        when /Test Case '-\[\S+\s+(\S+)\]' started./
          test_case = $1

        when /Test Case '-\[\S+\s+(\S+)\]' passed \((.*) seconds\)/
          test_case = $1
          test_case_duration = $2.to_f
          handle_test_passed(test_case,test_case_duration)

        when /(.*): error: -\[(\S+) (\S+)\] : (.*)/
          error_location = $1
          test_suite = $2
          test_case = $3
          error_message = $4
          handle_test_error(test_suite,test_case,error_message,error_location)

        when /Test Case '-\[\S+ (\S+)\]' failed \((\S+) seconds\)/
          test_case = $1
          test_case_duration = $2.to_f
          handle_test_failed(test_case,test_case_duration)

        when /failed with exit code (\d+)/
          @exit_code = $1.to_i

        when
          /BUILD FAILED/
          @exit_code = -1;
      end
    end
  end

  def handle_start_test_suite(start_time)
    @total_failed_test_cases = 0
    @total_passed_test_cases = 0
    @tests_results = Hash.new # test_case -> duration
    @errors = Hash.new  # test_case -> error_msg
    @ended_current_test_suite = false
    @cur_start_time = start_time
  end

  def handle_end_test_suite(test_name,end_time)
    unless @ended_current_test_suite
      current_file = File.open("#{TEST_REPORTS_FOLDER}/TEST-#{test_name}.xml", 'w')
      host_name = string_to_xml Socket.gethostname
      test_name = string_to_xml test_name
      test_duration = (end_time - @cur_start_time).to_s
      total_tests = @total_failed_test_cases + @total_passed_test_cases
      suite_info = '<testsuite errors="0" failures="'+@total_failed_test_cases.to_s+'" hostname="'+host_name+'" name="'+test_name+'" tests="'+total_tests.to_s+'" time="'+test_duration.to_s+'" timestamp="'+end_time.to_s+'">'
      current_file << "<?xml version='1.0' encoding='UTF-8' ?>\n"
      current_file << suite_info
      @tests_results.each do |t|
        test_case = string_to_xml t[0]
        duration = @tests_results[test_case]
        current_file << "<testcase classname='#{test_name}' name='#{test_case}' time='#{duration.to_s}'"
        unless @errors[test_case].nil?
          # uh oh we got a failure
          puts "tests_errors[0]"
          puts @errors[test_case][0]
          puts "tests_errors[1]"
          puts @errors[test_case][1]

          message = string_to_xml @errors[test_case][0].to_s
          location = string_to_xml @errors[test_case][1].to_s
          current_file << ">\n"
          current_file << "<failure message='#{message}' type='Failure'>#{location}</failure>\n"
          current_file << "</testcase>\n"
        else
          current_file << " />\n"
        end
      end
      current_file << "</testsuite>\n"
      current_file.close
      @ended_current_test_suite = true
    end
  end

  def string_to_xml(s)
    s.gsub(/&/, '&amp;').gsub(/'/, '&quot;').gsub(/</, '&lt;')
  end

  def handle_test_passed(test_case,test_case_duration)
    @total_passed_test_cases += 1
    @tests_results[test_case] = test_case_duration
  end

  def handle_test_error(test_suite,test_case,error_message,error_location)
#    error_message.tr!('<','').tr!('>','')
    @errors[test_case] = [ error_message, error_location ]
  end

  def handle_test_failed(test_case,test_case_duration) 
    @total_failed_test_cases +=1
    @tests_results[test_case] = test_case_duration
  end

end

#Main
#piped_input = File.open("tests_fail.txt") # for debugging this script
piped_input = ARGF.read

report = ReportParser.new(piped_input)

exit report.exit_code
Coreen answered 17/4, 2012 at 4:34 Comment(2)
HI Martin, I am using this for my OCUnit tests but my question relates to UI tests for which I am using Apple's UI Automation Instrument. These tests are written in javascript and generate XML output.Gwenngwenneth
@Gwenngwenneth hooo, I'm sorry I didn't get your question properly. The code I provided is for change the OCUnit tests into Jenkins/Hudson not for changing UIAutomation script output into Jenkins/Hudson. Anyway I hope you find outCoreen
E
1

Maybe you can use this.

And in Jenkins execute shell:

sh setup.sh sh runTests.sh ./sample/alltests.js "/Users/komejun/Library/Application Support/iPhone Simulator/5.0/Applications/1622F505-8C07-47E0-B0F0-3A125A88B329/Recipes.app/"

And the report will be auto-created in ./ynmsk-report/test.xml

Entomology answered 7/5, 2012 at 3:0 Comment(0)
S
0

You can use the tuneupjs library. The library provides test runner that generates a xml report (jUnit style). It works with Xcode 6. For the xml report just provide -x param. Check it here: http://www.tuneupjs.org/running.html

Schreibman answered 10/8, 2015 at 13:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.