Есть ли какой-то смысл модульного тестирования сериализации? - PullRequest
14 голосов
/ 09 июля 2009

У меня есть класс, который сериализует набор объектов (с использованием XML-сериализации), которые я хочу провести модульное тестирование.

Моя проблема в том, что я чувствую, что буду тестировать реализацию XML-сериализации .NET, а не что-нибудь полезное. У меня также есть небольшой сценарий с курицей и яйцом, когда для тестирования Reader мне понадобится файл, созданный Writer.

Я думаю, что вопросы (есть 3, но они все связаны), я в конечном счете ищу обратную связь:

  1. Можно ли протестировать Writer без использования Reader?
  2. Какова лучшая стратегия для тестирования ридера (XML-файл? Насмешка с записью / воспроизведением)? Действительно ли все, что вы действительно будете делать, это тестировать значения свойств объектов, которые были десериализованы?
  3. Какая лучшая стратегия для тестирования писателя!

Справочная информация о сериализации Xml

Я не использую схему, поэтому все элементы и атрибуты XML соответствуют свойствам объектов. Поскольку схемы нет, теги / атрибуты, которые не соответствуют атрибутам, найденным в свойствах каждого объекта, просто игнорируются XmlSerializer (поэтому значение свойства равно нулю или по умолчанию). Вот пример

<MyObject Height="300">
    <Name>Bob</Name>
    <Age>20</Age>
<MyObject>

будет отображаться в

public class MyObject
{
  public string Name { get;set; }
  public int Age { get;set; }

  [XmlAttribute]
  public int Height { get;set; }
}

и наоборот. Если объект изменится на приведенный ниже, XML все равно будет успешно десериализован, но FirstName будет пустым.

public class MyObject
{
  public string FirstName { get;set; }
  public int Age { get;set; }

  [XmlAttribute]
  public int Height { get;set; }
}

Недопустимый XML-файл будет десериализован правильно, поэтому модульный тест пройдет, если вы не запустили утверждения для значений MyObject.

Ответы [ 13 ]

26 голосов
/ 09 июля 2009

Нужно ли иметь возможность обратной совместимости? Если это так, возможно, стоит создать модульные тесты для файлов, созданных старыми версиями, которые все еще могут быть десериализованы новыми версиями.

Кроме этого, если вы когда-нибудь представите что-нибудь «интересное», то может стоить модульного теста, чтобы просто проверить, что вы можете сериализовать и десериализовать, просто чтобы убедиться, что вы не делаете что-то напуганное с readonly собственность и т. д.

19 голосов
/ 14 июля 2009

Я бы сказал, что для сериализации модульных тестов важно, если жизненно важно, чтобы вы могли читать данные между версиями. И вы должны провести тестирование с "известными хорошими" данными (т. Е. Недостаточно просто записать данные в текущей версии, а затем прочитать их снова).

Вы упоминаете, что у вас нет схемы ... почему бы не сгенерировать ее? Либо вручную (это не очень сложно), либо с помощью xsd.exe. Тогда у вас есть что-то, что вы можете использовать в качестве шаблона, и вы можете проверить это, используя XmlReader. Сейчас я выполняю много работ с сериализацией xml, и гораздо проще обновить схему, чем беспокоиться о том, правильно ли я получаю данные.

Даже XmlSerializer может стать сложным; особенно если вы используете подклассы ([XmlInclude]), пользовательскую сериализацию (IXmlSerializable) или конструкцию не по умолчанию XmlSerializer (передачу дополнительных метаданных во время выполнения в ctor) Другая возможность - творческое использование [XmlIngore], [XmlAnyAttribute] или [XmlAnyElement]; Например, вы можете поддерживать непредвиденные данные для двусторонней (только) версии X, но хранить их в известном свойстве в версии Y.


С сериализацией вообще:

Причина проста: вы можете взломать данные! Насколько плохо вы это делаете, зависит от сериализатора; например, с BinaryFormatter (и я знаю, что вопрос XmlSerializer), просто меняется с:

public string Name {get;set;}

до

private string name;
public string Name {
    get {return name;}
    set {name = value; OnPropertyChanged("Name"); }
}

может быть достаточно , чтобы прервать сериализацию , так как имя поля изменилось (и BinaryFormatter любит поля).

В других случаях вы можете случайно переименовать данные (даже в контрактных сериализаторах, таких как XmlSerializer / DataContractSerializer). В таких случаях вы обычно можете переопределить идентификаторы проводов (например, [XmlAttribute("name")] и т. Д.), Но важно проверить это!

В конечном счете, все сводится к следующему: важно ли читать старые данные? Обычно это так; так что не отправляйте его ... докажите , что можете.

7 голосов
/ 09 июля 2009

Для меня это абсолютно в категории «Не беспокоить». Я не тестирую свои инструменты. Однако, если вы написали свой собственный класс сериализации, то непременно протестируйте его модулем.

5 голосов
/ 09 июля 2009

Если вы хотите убедиться, что сериализация ваших объектов не нарушается, то обязательно проведите модульное тестирование. Если вы читаете документы MSDN для класса XMLSerializer :

XmlSerializer не может сериализовать или десериализовать следующее:

Массивы ArrayList
Массивы списка

Существует также особая проблема с перечислениями, объявленными как unsigned long. Кроме того, любые объекты, помеченные как [Obsolete], не сериализуются начиная с .Net 3.5 и выше.

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

По сути, вы не юнит тестируете сериализацию XML, вы тестируете возможность сериализации ваших объектов. То же самое относится и к десериализации.

3 голосов
/ 18 июля 2009

Да, если то, что должно быть проверено, проверено надлежащим образом, с небольшим вмешательством.

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

Дело не только в том, закончите ли вы тестирование сериализации / десериализации .NET; Вы должны протестировать свой интерфейс с внешним миром - чтобы вы могли выводить XML в ожидаемом формате и чтобы вы могли правильно использовать XML в ожидаемом формате.

У вас должны быть статические данные XML, которые можно использовать для сравнения с выходными данными сериализации и использования в качестве входных данных для десериализации.

Предположим, вы отдаете задание записывать и читать заметки тому же парню:

You - Bob, I want you to jot down the following: "small yellow duck."
Bob - OK, got it.
You - Now, read it back to me.
Bob - "small yellow duck"

Теперь, что мы здесь проверили? Боб действительно может написать? Боб вообще что-нибудь написал или он запомнил слова? Боб действительно может читать? - его собственный почерк? Как насчет почерка другого человека? У нас нет ответов ни на один из этих вопросов.

Теперь давайте представим Алису на картинке:

You - Bob, I want you to jot down the following: "small yellow duck."
Bob - OK, got it.
You - Alice, can you please check what Bob wrote?
Alice - OK, he's got it.
You - Alice, can you please jot down a few words?
Alice - Done.
You - Bob, can you please read them?
Bob - "red fox"
Alice - Yup, that sounds right.

Теперь мы знаем с уверенностью, что Боб может писать и читать правильно - до тех пор, пока мы можем полностью доверять Алисе. Статические данные XML (в идеале проверяемые по схеме) должны быть достаточно надежными.

2 голосов
/ 17 июля 2009

Я согласен с вами, что вы будете тестировать реализацию .NET больше, чем собственный код. Но если это то, что вы хотите сделать (возможно, вы не доверяете реализации .NET :)), я мог бы подойти к вашим трем вопросам следующим образом.

  1. Да, конечно, можно протестировать писателя без читателя. Используйте программу записи для сериализации примера (20-летний Боб), который вы предоставили MemoryStream. Откройте MemoryStream с помощью XmlDocument. Утвердите, что корневой узел называется «MyObject». Утвердите, что он имеет один атрибут с именем «Высота» со значением «300». Утверждается, что есть элемент «Имя», содержащий текстовый узел со значением «Боб». Утверждается, что существует элемент «Возраст», содержащий текстовый узел со значением «20».

  2. Просто сделайте обратный процесс # 1. Создайте XmlDocument из строки XML 20-летнего Боба. Десериализовать поток с читателем. Свойство Assert для имени равно «Bob». Утвердите, что свойство Age равно 20. Вы можете сделать такие вещи, как добавление контрольного примера с незначительным пробелом или одинарные кавычки вместо двойных кавычек, чтобы быть более тщательными.

  3. См. № 1. Вы можете расширить его, добавив то, что вы считаете хитрыми «крайними» случаями, которые, по вашему мнению, могут его сломать. Имена с различными символами Юникода. Удлиненные имена. Пустые имена. Отрицательные возрасты. Etc.

2 голосов
/ 15 июля 2009

Существует множество типов, с которыми сериализация не может справиться и т. Д. Также, если у вас неверные атрибуты, часто возникает исключение при попытке прочитать XML-файл обратно.

Я стремлюсь создать дерево примеров объектов, которые можно сериализовать хотя бы с одним примером каждого класса (и подкласса). Затем, как минимум, сериализовать дерево объектов в поток строк и затем прочитать его обратно из потока строк.

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

Как уже говорили другие люди, если вам нужно иметь возможность считывать данные, сохраненные старыми версиями вашего программного обеспечения, вам лучше сохранить набор файлов с примерами данных для каждой поставляемой версии и иметь тесты, чтобы подтвердить, что вы все еще можете прочитать их. Это сложнее, чем кажется на первый взгляд, так как значение полей в объекте может изменяться в зависимости от версии, поэтому просто создать текущий объект из старого сериализованного файла недостаточно, вы должны убедиться, что значение одинаково так как это была версия программного обеспечения, которая сохранила файл. (Поместите атрибут версии в ваш корневой объект сейчас!)

2 голосов
/ 14 июля 2009

По моему опыту, это определенно стоит сделать, особенно если потребитель собирается использовать XML в качестве XML-документа. Например, потребителю может потребоваться, чтобы каждый элемент присутствовал в документе, чтобы избежать проверки на ноль узлов при обходе или для проверки схемы.

По умолчанию XML-сериализатор пропустит свойства с нулевым значением, если вы не добавите атрибут [XmlElement (IsNullable = true)]. Точно так же вам, возможно, придется перенаправить общие свойства списка в стандартные массивы с атрибутом XMLArray.

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

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

1 голос
/ 17 июля 2009

Мы принимаем тестирование нашей сериализации, а не юнит-тестирование.

Это означает, что наши приемочные тестеры берут схему XML или, как в вашем случае, некоторый образец XML, и воссоздают свой собственный сериализуемый класс передачи данных.

Затем мы используем NUnit для тестирования нашей службы WCF с этим XML-кодом чистой комнаты.

С помощью этой техники мы выявили много-много ошибок. Например, когда мы изменили имя члена .NET и забыли добавить тег [XmlElement] со свойством Name =.

1 голос
/ 09 июля 2009

В некоторых случаях я делал это ... не тестируя сериализацию как таковую, а используя некоторые «заведомо хорошие» сериализации XML, а затем загружая их в мои классы, и проверяя, что все свойства (если применимо) имеют ожидаемый значения.

Это ничего не будет проверять для первой версии ... но если классы когда-либо будут развиваться, я знаю, что поймаю любые критические изменения в формате.

...