LINUX.ORG.RU

Богам юнит теста!

 , ,


1

3

Доброго времени суток, ЛОР!

Помогите нубу разобраться с модульным тестирование вообще и для Lua в частности. У меня нет понимания - на что в принципе можно протестировать некоторые вещи? Например, на что можно протестировать следующию функцию:

function slice(tbl, a, b, step)
    local ret, cnt = {}, 1
    if not b then
        a, b = 1, a
    end
    step = step or 1
	for i=a, b, step do
	    ret[cnt] = tbl[i]
	    cnt = cnt + 1
	end
    return ret
end

У меня фантазии хватило только на это:

function testSlice()
    local t = { 1, 2, 3 }
    assertNotNil( slice(t, 1, 2, 1) )
    assert( is_table( slice(t, 1, 2, 1) ) )
    assertNotNil( slice(t, 1, nil, 1) )
    assert( is_table( slice(t, 1, nil, 1) ) )
end

Тест покрывает эту функцию на 100%, но у меня остается сильное ощущение, что я не все учитываю. Поэтому прошу многоуважаемое сообщество подсказать мне, неопытному падавану, на что еще можно протестировать вышепиведенную функцию.

Заранее спасибо.


юнит тесты разные бывают и пишутся в зависимости от своего предназначения в разное время и для разных задач.

к примеру, регрессионные пишутся чтобы баги не повторялись.

ты сначала подумай чего ты хочешь добиться, а то получается «мне надо скрепить 2 вот эти детали, но я не знаю как, какой стороной и зачем вообще».

BaBL ★★★★★ ()
Ответ на: комментарий от BaBL

Ну вот данную функцию на что _вообще_ можно протестировать?

Sektor ()

Тестировать нужно на то, что в будущем очень вероятно сломать. Что конкретно ты собираешься менять в этой функции, что может изменить ее функциональность?

vurdalak ★★★★★ ()

Так как боги не торопятся появляться, выскажу свое скромное мнение. Каким образом можно разбить твою функцию? Некорректными параметрами. Вызови ее для списка нулевой длины.

PatrickKilpatrick ()
Ответ на: комментарий от PatrickKilpatrick

Каким образом можно разбить твою функцию? Некорректными параметрами. Вызови ее для списка нулевой длины.

А еще ее можно разбить при «a» и «step» = nil. Но это уже должны быть проверким внутри самой функции. А мне бы хотелось знать, что «внешне» у этой функции можно протестировать. Неужели только тип и «не nil» возвращаеиого значения?

Sektor ()
Ответ на: комментарий от Sektor

Но это уже должны быть проверким внутри самой функции. А мне бы хотелось знать, что «внешне» у этой функции можно протестировать.

Нет. Насколько я понимаю это должны гарантировать внешние тесты. Грубо говоря внешние тесты должны гарантировать наличие внутренних.

PatrickKilpatrick ()

Простые функции на последовательнастях стоит тестить подобно методу мат индукции. Проверь, что функция работает на пустой таблице, на не пустой. Это так называемый happy-path. Потом проверь краевые случаи для входных параметров: для

b - a < 0,
b - a == 0,
b - a == len(tbl),
b - a > len(tbl),
 
step < 0,
step = 0,
step == len(tbl),
step > len(tbl),
step < b - a,
step == b - a,
step > b - a
dizza ★★★★★ ()
Ответ на: комментарий от PatrickKilpatrick

Грубо говоря внешние тесты должны гарантировать наличие внутренних.

Согласен. Ну а пока у _конкретной_ функции какие проверки могут быть?

Sektor ()

И да, толку от твоей проверки - assertNotNil, is_table. Ты с expected reult-ом сравнивай.

dizza ★★★★★ ()

Вот хороший пример из хорошей книги.

Если TDD, я обычно тестирую:

  • Крайние случаи, например: нулевые данные, некорректные данные, граничные значения (если есть).
  • Поведение в неочевидных случаях, в вашем примере поведение, когда аргументы a, b, step, не заданы.
  • Типичные сценарии, от простых к сложным.
  • Смотрю, что может поменяться в коде например при отпимизации или рефакторинге, что можно при этом поломать, и если соотв. теста еще нет, добавляю его заранее.
  • Если есть смысл, стресс-тесты, проверка выхода за границы буфера, etc.

Богам юнит теста!

Зря такой заголовок :)

gv ()

Старайся больше внимания уделять крайним случаям и ошибкам. Вот пример на Ruby:

slice.rb:

def slice(arr, from, to = nil, step = nil)
  from, to = 0, from unless to
  step ||= 1

  raise TypeError unless arr.is_a? Array
  raise TypeError, 'Step and bounds must be integral' unless
    (step.is_a? Integer or step.is_a? Bignum) and
    (from.is_a? Integer or from.is_a? Bignum) and
    (to.is_a? Integer or to.is_a? Bignum)

  raise ArgumentError, 'Invalid slice bounds' if from > to
  raise ArgumentError, 'Array must be non-emtpy' if arr.empty?

  (from..to).step(step).each_with_object([]) do |idx, ret|
    ret << arr.fetch(idx)
  end
end

slice_spec.rb:

require_relative './slice.rb'

describe '#slice' do
  it 'should raise ArgumentError for empty array' do
    expect { slice [], 42 }.to raise_error(ArgumentError)
  end

  context 'with non-empty array' do
    subject(:arr) { [42, 15, 100500, 19, 24, 241, 412] }

    it 'should raise TypeError if argument types are invalid' do
      expect { slice arr, 0, 6, 2.5 }.to raise_error(TypeError)
      expect { slice arr, 0.5, 3 }.to raise_error(TypeError)
      expect { slice arr, 'to' }.to raise_error(TypeError)
      expect { slice true, 2 }.to raise_error(TypeError)
    end

    it 'should raise errors if slice bounds are invalid' do
      expect { slice arr, 5, 1 }.to raise_error(ArgumentError)
      expect { slice arr, 99 }.to raise_error(IndexError)
    end

    it 'should raise ArgumentError if step is negative' do
      expect { slice arr, 0, 1, -1 }.to raise_error(ArgumentError)
    end

    it 'should slice the whole array' do
      expect(slice(arr, arr.length - 1).length).to eq(arr.length)
      expect(slice arr, arr.length - 1).to eq(arr)
    end

    it 'should slice with given range' do
      expect(slice arr, 1, 5).to eq([15, 100500, 19, 24, 241])
    end

    it 'should slice with given range and step' do
      expect(slice arr, 1, 5, 2).to eq([15, 19, 241])
    end
  end

  # and so on...
end

Естественно, какую ситуацию считать ошибочной, — решать тебе.

theNamelessOne ★★★★★ ()
Ответ на: комментарий от theNamelessOne

slice_spec.rb

Классная штука этот rspec, спасибо за пример.

gv ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.