2011-12-20 4 views
3

В настоящее время я занимаюсь реализацией интересных структур данных в Ruby и столкнулся с проблемой тестирования функций, которые не имеют прогнозируемого результата. В настоящее время я работаю над Bloom Filter, что я включил реализацию ниже для полноты:Тестирование непредсказуемых функций

require "zlib" 

class BloomFilter 
    def initialize(size=100, hash_count=3) 
    raise(ArgumentError, "negative or zero buffer size") if size <= 0 
    raise(ArgumentError, "negative or zero hash count") if hash_count <= 0 

    @size = size 
    @hash_count = hash_count 
    @buffer = Array.new(size, false) 
    end 

    def insert(element) 
    hash(element).each { |i| @buffer[i] = true} 
    end 

    def maybe_include?(element) 
    hash(element).map { |i| @buffer[i] }.inject(:&) 
    end 

    private :hash 
    def hash(element) 
    hashes = [] 

    1.upto(@hash_count) do |i| 
     hashes << Zlib.crc32(element, i) 
    end 

    hashes.map { |h| h % @size } 
    end 
end 

Одна из проблем, с фильтром Блум является то, что он имеет возможность возвращения ложных срабатываний, ложно возвращает истину для включение элементов, которые никогда не были вставлены в фильтр.

Иногда фильтр ведет себя таким образом, что это легко проверяемым:

b = BloomFilter.new(50, 5) 

b.insert("hello") 
puts b.maybe_include?("hello") # => true 
puts b.maybe_include?("goodbye") # => false 

Однако иногда баксы тенденцию и ведет себя непредсказуемым образом. (Я уменьшил размер буфера здесь, чтобы найти конфликт быстро.)

b = BloomFilter.new(5, 4) 

b.insert("testing") 
puts b.maybe_include?("testing") # => true 
puts b.maybe_include?("not present") # => false 
puts b.maybe_include?("false positive") # => true (oops) 

Так вдруг у нас есть строка «ложноположительный» обеспечивая ... ложноположительный. Мой вопрос: как мы можем это проверить?

  • Если мы выбираем значения, которые просто случаются работать с нашими испытаниями, то я чувствую, как тесты становятся слишком хрупкими. Например, если мы изменим функцию хеширования, тогда у нас может быть совершенно правильный фильтр Bloom , который начинает сбой некоторых тестов из-за значений, которые мы выбрали , для проверки исходной реализации.

  • Моя вторая мысль была, чтобы проверить, что фильтр ведет себя в ожидаемой образом, только убедившись, что мы получаем примерно в expected number of false positives от него, изменяя количество хеш-функций и размер внутреннего буфера . Хотя этот подход может протестировать общую грубую правильность фильтра, я беспокоюсь, что он не сможет поймать ошибки , которые заставляют сообщать о неправильных значениях для отдельных случаев (например, false минус).

Am Я слишком пессимистично по поводу эффективности двух методов тестирования выше, или я пропускаю способ проверить такие классы, как фильтр Блума, который на выходе непредсказуема?

ответ

2

Вы правы, что выбираете значения, которые просто происходят работать - плохая идея. Однако ваша вторая идея не так уж плоха.

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

-1

Фильтр Bloom - это пространственно-эффективная вероятностная структура данных, которая используется для проверки того, является ли элемент элементом набора. Возможны ложные срабатывания, но ложных негативов нет.

Просто из описания того, что делает фильтр Bloom, должно быть ясно, что нет смысла тестировать ложные срабатывания. По сути, не определено, каков результат положительного теста, поэтому вы не можете делать тесты для него, ожидающие определенного результата. Единственное, что вы можете гарантировать, и, следовательно, испытывают на следующие:

  • функция возвращает логическое значение
  • функция не бросает ошибки
  • нет ложноотрицательных
0

Тестирование это подтверждение ваших ожиданий. Если вы не можете объяснить себе, как будет возвращаться фильтр Bloom (учитывая хрупкость, как вы упомянули), вы не можете рассчитывать на это ожидание. (Клянусь, я не пытался сделать каламбур: P)

Мое первое чувство кишки должно было бы подтвердить ложноположительный процент на N сгенерированных входах на всех интересных алгоритмах хеширования. Это автоматизирует вас столько же безопасности, сколько вы бы делали эти тесты вручную.

Для достижения этой цели, я рекомендовал бы тестовый код достаточно для факторизуется вам выразить просто:

< предупреждения> непроверенных код </предупреждение>

class BloomFilterTestCase << TestCase 
    def bloom_incidence(alg, pop, false_positives) 
    define_method("test_bloom_incidence_${alg}_${pop}_${false_positives}") do 
     # code code code 
    end 
    end 

    bloom_incidence :naive, 50, 0.05 
end