跳转至

IDL中的单元测试

原文链接: https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/unit-testing-in-idl

12741 文章评分:

3.7

IDL中的单元测试

匿名 2015年5月7日 星期四

软件开发的关键环节之一就是测试。确保软件在多种输入下都能按预期运行,对于创建健壮且可维护的代码至关重要。在IDL中进行测试可能有些棘手,通常需要您自己实现一套维护测试的系统。不过,借助一个简单的框架,这项任务会变得轻松很多。

在深入框架之前,建议您先阅读关于测试驱动开发(Test Driven Development)以及编写优质测试的相关概念。网上有许多优秀的教程和概述,它们能很好地强调测试的重要性。现在,让我们开始看代码!

假设我们想编写一个函数 CONVERT_TO_STRING。由于我们是从零开始编写这个函数,让我们先定义函数的“契约”。它接收一个IDL变量作为输入,将其转换为具有自定义格式的字符串,然后返回。很好,让我们编写一些测试。

test_convert_to_string.pro:

pro test_convert_to_string_number
  compile_opt idl2
  on_error, 2

  input = 1
  expect = '1'
  result = convert_to_string(input)

  if result ne expect then begin
    message, 'Converting number failed.'
  endif
end

pro test_convert_to_string_null
  compile_opt idl2
  on_error, 2

  input = !NULL
  expect = '!NULL'
  result = convert_to_string(input)

  if result ne expect then begin
    message, 'Converting number failed.'
  endif
end

pro test_convert_to_string_object
  compile_opt idl2
  on_error, 2

  input = hash('a',1,'b',2,'c',3)
  expect = '{"c":3,"a":1,"b":2}'
  result = convert_to_string(input)

  if result ne expect then begin
    message, 'Converting number failed.'
  endif
end

pro test_convert_to_string
  compile_opt idl2

  print
  print, 'Testing suite for convert_to_string()'
end

在编写任何代码之前,我们已经有测试用例了。我们之所以可以这样做,是因为我们定义了函数的“契约”。我们清楚地知道函数应该接收什么输入,以及输出应该是什么。现在,运行这些代码可能有点繁琐,所以让我们设置一些框架。

unit_test_runner.pro:

; Path – 测试目录的路径
pro unit_test_runner, path
  compile_opt idl2

  if ~file_test(path, /directory) then begin
    message, 'Input must be a path.'
  endif

  test_files = file_search(path, 'test*.pro')
  resolve_routine, file_basename(test_files,'.pro'), /compile_full_file
  tests = routine_info()

  print
  print,'--------------------------------------------------------------------------------'

  error_count = 0
  for i=0, tests.*length*-1 do begin
    catch, errorStatus
    if (errorStatus ne 0) then begin
      catch, /cancel
      print, 'ERROR: ', !ERROR_STATE.*msg*
      i++
      error_count++
      continue
    endif

    if (tests[i]).startswith('TEST_') then begin
      call_procedure, tests[i]
    endif
  endfor

  print
  print,'--------------------------------------------------------------------------------'
  print

  if error_count gt 0 then begin
    print, 'Unit test failures on: ' + path
  endif else begin
    print, 'Unit tests pass.'
  endelse

end

现在我们要做的就是给 UNIT_TEST_RUNNER 一个指向我们测试文件的路径,它就会运行它们!让我们开始专心编码。

convert_to_string.pro:

; Input - IDL 变量
; Output - 变量的自定义字符串表示
function convert_to_string, var
  compile_opt idl2

  switch size(var,/TYPE) of
    0: begin
      return, '!NULL '
      break
    end
    11: begin
      if isa(var,'HASH') or isa(var,'DICTIONARY') or isa(var,'ORDEREDHASH') then begin
        return, json_serialize(var)
      endif
      break
    end
    else: begin
      return, strtrim(var,2)
    end
  endswitch
end

现在运行我们的测试套件:

--------------------------------------------------------------------------------

Testing suite for convert_to_string()
% Compiled module: CONVERT_TO_STRING.
ERROR: TEST_CONVERT_TO_STRING_NULL: Converting !NULL failed.

--------------------------------------------------------------------------------

Unit test failures on: C:\convert_to_string

哎呀!我们的 !NULL 情况返回的值不符合预期(好在这是个容易修复的问题)。

通过采用测试先行的开发方式,您被迫去思考每个函数的“契约”(输入/输出)。通过针对此契约实现单元测试,我们就可以有信心地使用新函数。如果所有代码都有单元测试,那么代码中出现的任何问题都能轻松地被识别和修复。

注意:请确保每个代码段都保存到指定的文件中(文件名已在代码前给出),并且所有文件都在您的 IDL 路径(IDL PATH)中。

Landsat Program Well Worth It's Price Tag Use ENVI and DigitalGlobe Data to Help Recovery Efforts in Nepal