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

MandyW
  • 1,117
  • 3
  • 14
  • 23
  • 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 things – vikas Feb 02 '12 at 06:19
  • 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. – Heath Borders Mar 14 '12 at 16:17
  • 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 parse – Michael Mar 14 '12 at 19:18

4 Answers4

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);
    }
})();
ıɾuǝʞ
  • 2,829
  • 26
  • 38
  • Hi this looks very cool but I was trying to avoid using Jasmine and instead use the [tuneup.js](https://github.com/alexvollmer/tuneup_js) library to keep things simple. I have marked your answer as correct as it meets my requirement though, thank you. – MandyW Apr 18 '12 at 20:52
  • I just edited my answer to show you the solution I choosed to implement – ıɾuǝʞ May 01 '12 at 09:07
  • @kenji Can you explain a bit on how and where you use this function? – stackErr Oct 26 '12 at 22:03
  • I replace `logStart` with an anonymous function, and this function call the real `logStart` (after reporting test suite starting). – ıɾuǝʞ Oct 29 '12 at 14:06
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 <christian.hedin@jayway.com>
# 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
Martin Magakian
  • 3,746
  • 5
  • 37
  • 53
  • 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. – MandyW Apr 18 '12 at 20:48
  • @MandyW 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 out – Martin Magakian Apr 18 '12 at 23:47
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

houlianpi
  • 66
  • 3
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