понедельник, 20 февраля 2012 г.

Подготовка автотеста для проверки Системы Безопасности

Как протестировать систему безопасности?
1.    Выдать пользователю роль;
2.    Зайти под ним в систему;
3.    Зайти в проверяемый модуль;
4.    Профит! В смысле, сам тест
Первые три пункта весьма удручают при ручном тестировании. А еще сильнее – при автоматизированном. Ведь мы делаем автотесты, чтобы облегчить свою жизнь, создать возможность прогнать большее число тестов за меньшее время…
Поэтому первый пункт желательно выкинуть из GUI, а настраивать прямо через базу. Ведь он может много времени занять: зайти под мега-супер-админом, найти в системе нужного пользователя, удалить все его старые роли, выдать только нужную… А все вокруг лагает, лагает…
Проще ведь через базу, да? Найти нужную табличку «пользователь - его роли», удалить оттуда все строки, относящие к данному пользователю (вы можете создавать его в том же автотесте, можете использовать уже существующего, если с созданием новой учетки есть некоторые проблемы/затраты во времени (подтверждение админом), тут уже вам решать, откуда id пользователя тащить). И добавить одну или несколько своих.
Попробуем сделать это на примере нашего приложения, достающего из базы всех авторов, а также их книги.
В студии у нас уже есть в этом solution-е 2 проекта: «Books.Model» и «Books.Web». Добавляем новый проект класса «Library» - «Books.Tests». В реальной жизни нам понадобится только наша модель и сами тесты, «веб»-часть мы просто выкидываем.
Но раз уж она есть, будем контролировать корректность выполнения теста. Ведь по сути у нас задача написать не сам тест, а «то, что будет сделано ДО выполнения тестов». Значит, никакие проверки внутри не нужны. Проверим руками, запустив приложение. Если нет веб-части, можно посмотреть и в SQL Server Managеment Studio.
Итак, создали тесты. Добавляем сразу конфигурационный файл в проект:

В нем указываем строку соединения.
  <connectionStrings>
<add name="BooksDb" connectionString="Data Source=.\SQLEXPRESS;Initial     Catalog=Books;User ID=ap;Password="/>
  </connectionStrings>

Теперь запишем сам тест:
using Books.Model;
using NUnit.Framework;

namespace Books.Tests
{
    [TestFixture]
    public class Test
    {
        public int idAuthor;
        public int idBook;

        [Test]
        public void ЗадатьАвтораУжастика()
        {
            idAuthor = 2;
            idBook = 8;

using (var db = new Books.Model.BooksModelDataContext(ConfigurationManager.ConnectionStrings["BooksDb"].ConnectionString))
            {
                using (var tran = new TransactionScope())
                {
                    var ordDetailQuery =
                        from a in db.BookAuthorships
                        where a.authorId == idAuthor
                         select a;

                    db.BookAuthorships.DeleteAllOnSubmit(ordDetailQuery);


                    var ba = new BookAuthorship();
                    ba.authorId = idAuthor;
                    ba.bookId = idBook;

                    db.BookAuthorships.InsertOnSubmit(ba);

                    db.SubmitChanges();
                    tran.Complete();
                }
            }
        }
    }
}

using Books.Model; - мы будем работать с моделью, подключаем ее в references на закладке проектов, подключаем ее namespace в наш тест.

using NUnit.Framework; - с его помощью мы определяем наш набор тестов.

[TestFixture] – атрибут NUnit.Framework, указывает на то, что ниже идет набор тестов, помеченных атбитом [Test]. В реальности этот наш «тест» будет не самим тестом, а заготовкой, то есть он будет лежать в базовом классе, помеченный атрибутом [TestFixtureSetUp].

idAuthor = 2;
idBook = 8;

В нашем тесте мы хотим конкретному автору («Ольга К») сопоставить конкретную книгу. Лезем в базу, узнаем их id. Задаем явно. Можно тащить из какого-нибудь конфига. Если потребуется.
Далее стандартно – чтобы обратиться к базе, нужно выполнить операцию. Операция должна быть обернута в транзакцию. Для открытия транзакции необходимо открыть соединение.
Делаем выборку из базы всех строк с автором «Ольга К»
var ordDetailQuery =
  from a in db.BookAuthorships
  where a.authorId == idAuthor
  select a;

Удаляем их:
db.BookAuthorships.DeleteAllOnSubmit(ordDetailQuery);

Создаем entity с параметрами, которые мы хотим видеть в базе в итоге:
var ba = new BookAuthorship();
ba.authorId = idAuthor;
ba.bookId = idBook;

Помещаем его в базу:
db.BookAuthorships.InsertOnSubmit(ba);

Сохраняем изменения в базе, закрываем транзакцию.
F5 – стартуем приложение. У нашей «подопытной» три книги:


Прогоняем тест.

Снова стартуем приложение:


Вуаля J

2 комментария:

  1. >Проще ведь через базу, да?
    Наверное, проще, но ведь сами же лезете грязными руками в БД. Как вы потом будете доказывать, что проблема в тестируемом приложении, а не в том, что ваш автотест криво проадейтил данные в БД? Будете руками воспроизводить, на этот раз по-честному?

    И - как убедитесь, что при работе через UI все настройки выставляются правильно? Отдельно это тестировать?

    Да и при изменении БД придется автотествы переписывать.

    Лучше бы как средство ускорения в таком случае использовать API. Если его нет - то это уже другой вопрос и начало другой темы )

    ОтветитьУдалить
  2. Да, я проверю ручками, если тест упадет. И сделаю это в любом случае. Если же протестировать апдейт, то с ним потом проблем не будет.
    И да, назначение роли можно проверить отдельно.
    Можно сгруппировать тесты по-разному.

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

    Давайте остановимся в этой теме на конкретном вопросе :)

    ОтветитьУдалить