Тествайте уеб приложения с HttpUnit

В типично корпоративно приложение много области изискват тестване. Като се започне от най-простите компоненти, класове, разработчиците или разработчиците на специализирани тестове трябва да програмират модулни тестове, за да гарантират, че най-малките единици на приложението се държат правилно. Всеки компонент може потенциално да премине единичните тестове сам; разработчиците обаче трябва да гарантират, че работят съвместно, както се очаква - като част от подсистема и като част от цялото приложение - следователно трябва да се извършват тестове за интеграция . В някои проекти трябва да бъдат изпълнени изискванията за производителност, така че инженерите по осигуряване на качеството извършват тестове за натоварване, за да проверят и документират как приложението работи при различни условия. По време на разработването на приложения инженерите за осигуряване на качеството извършват автоматизирани и ръчни функционални тестовеза да тествате поведението на приложението от гледна точка на потребителя. Когато даден проект за разработка почти завърши специфичен етап, могат да се извършат тестове за приемане, за да се провери дали приложението отговаря на изискванията.

HttpUnit е рамка, базирана на JUnit, която позволява внедряването на автоматизирани тестови скриптове за уеб приложения. Той е най-подходящ за изпълнение на автоматизирани функционални тестове или тестове за приемане. Както подсказва името, той може да се използва за модулно тестване; обаче, типичните компоненти на уеб слоя като JSP (JavaServer Pages) страници, сървлети и други компоненти на шаблона не се поддават на модулно тестване. Що се отнася до различни базирани на MVC (Model-View Controller) компоненти, базирани на рамка, те са по-подходящи за тестване с други рамки за тестване. Действията на Struts могат да бъдат единично тествани с StrutsUnit, а действията на WebWork 2 могат да бъдат тествани, например, без уеб контейнер.

Тестови цели

Преди да преминем към подробностите за архитектурата и внедряването, важно е да изясним точно какво ще трябва да се докаже на скриптовете за тестване за уеб приложението. Възможно е просто да симулирате поведението на случаен посетител на уебсайта, като просто щракнете върху интересни връзки и прочетете страници в произволен ред, но резултатът от тези произволни скриптове няма да опише пълнотата и качеството на приложението.

Типично корпоративно уеб приложение (или сложен уебсайт) има няколко документа, описващи изискванията на различните потребители или поддържащи приложения. Те могат да включват спецификации за случаи на употреба, спецификации за нефункционални изисквания, спецификации за тестови случаи, получени от другите артефакти, документи за дизайн на потребителския интерфейс, макети, профили на актьори и различни допълнителни артефакти. За просто приложение, цялата спецификация може да се състои от обикновен текстов файл със списък с изисквания.

От тези документи трябва да създадем организиран списък с тестови случаи. Всеки тестов случай описва сценарий, който може да бъде осъществен от уеб посетител чрез уеб браузър. Добра практика е да се стремите към сценарии с подобен размер - по-големите сценарии могат да бъдат разделени на по-малки парчета. Много отлични книги и статии обсъждат създаването на спецификации за тестови случаи. За тази статия, нека приемем, че имате набор от елементи, които искате да тествате за вашето уеб приложение, организирани в групи от сценарии за тестови случаи.

Време е да изтеглите неща!

Добре, сега знаем скучните неща, нека изтеглим няколко готини играчки! На първо място, ние се нуждаем от инсталиран Java 2 SDK, за да компилираме и изпълним нашите тестове. След това трябва да изтеглим HttpUnit framework - понастоящем на версия 1.5.5. Двоичният пакет съдържа всички необходими библиотеки на трети страни. Ще ни е необходим и инструментът за изграждане на Ant, за да стартираме тестовете и да генерираме автоматично отчети. Всяка доста скорошна версия на тези инструменти вероятно би работила; Просто предпочитам да използвам най-новата и най-добрата версия на всичко.

За да пишете и изпълнявате тестове, препоръчвам да използвате IDE, който има вграден JUnit тест бегач. Използвам Eclipse 3.0M7, за да разработя тестовите си скриптове, но IntelliJ също има поддръжка на JUnit, както и най-наскоро пуснатите IDE.

HttpUnit: HTTP симулатор на клиент

Тъй като искаме да тестваме уеб приложения, в идеалния случай инструментът за тестване трябва да се държи точно както уеб браузърите на потребителите. Нашето приложение (целта на теста) не трябва да знае каквато и да е разлика при обслужването на страници в уеб браузър или тестовия инструмент. Точно това осигурява HttpUnit: той симулира заявките за нормален браузър GET и POST и осигурява хубав обектен модел, с който да кодираме нашите тестове.

Вижте подробното ръководство за API за останалите класове и методи; Фигура 1 просто дава кратък преглед на класовете, които използвам най-често. Потребителска сесия (последователност от взаимодействия с уеб приложението) се капсулира с WebConversation. Изграждаме WebRequests, обикновено конфигурирайки URL и параметрите и след това го изпращаме надолу през WebConversation. След това рамката връща a WebResponse, съдържаща върнатата страница и атрибути от сървъра.

Ето примерен тестов пример за HttpUnit от документите HttpUnit:

/ ** * Проверява, че изпращането на формуляра за вход с името "master" води до резултатите * на страница, съдържаща текста "Строго секретно" ** / public void testGoodLogin (), хвърля изключение {WebConversation razgovor = new WebConversation (); WebRequest заявка = нов GetMethodWebRequest ("//www.meterware.com/servlet/TopSecret"); WebResponse отговор = konverзация.getResponse (заявка); WebForm loginForm = response.getForms () [0]; request = loginForm.getRequest (); request.setParameter ("name", "master"); отговор = разговор.getResponse (заявка); assertTrue („Входът не е приет“, response.getText (). indexOf („Успяхте!“)! = -1); assertEquals ("Заглавие на страницата", "Строго секретно", response.getTitle ()); }

Архитектурни съображения

Забележете как примерният пример на Java съдържа името на домейн на сървъра, изпълняващ приложението. По време на разработването на нова система приложението живее на множество сървъри и сървърите могат да изпълняват множество версии. Очевидно е лоша идея да запазите името на сървъра в изпълнението на Java - за всеки нов сървър трябва да прекомпилираме нашите източници. Други елементи не трябва да живеят в изходните файлове, като потребителски имена и пароли, които трябва да се конфигурират за конкретното внедряване. От друга страна, не бива да преувеличаваме простото изпълнение на тестови случаи. Обикновено спецификацията на тестовия случай вече съдържа по-голямата част от състоянието на системата и конкретни описания на параметри за нашия сценарий, така че няма смисъл всичко да се параметризира в изпълнението.

During coding, you'll realize that many code sections appear in more than one test-case implementation (potentially in all of the test cases). If you're an experienced object-oriented developer, you'll be tempted to create class hierarchies and common classes. In some cases, that makes a lot of sense—for example, the login procedure should be a common method available for all test cases. However, you need to step back a bit and realize that you're not building a new production system on top of the target-of-test application—these Java classes are no more than test scripts to validate the Website output. Exercise common sense and aim for simple, sequential, and self-contained test scripts.

The test cases are typically fragile. If a developer changes a URL, reorganizes the layout's

will simple, sequential script

Traceability is crucial for our test cases. If something goes KA-BOOM, or, for example, a calculation result is wrong, it's important to point the developer to the corresponding test-case specification and the use-case specification for a quick bug resolution. Therefore, annotate your implementation with references to the original specification documents. Including the version number of those documents is also useful. That could be just a simple code comment or a complex mechanism where the test reports themselves link to the documents; the important thing is to have the reference in the code and to keep the traceability.

When do I get to write code?

Now that you're aware of the requirements (use-case docs and corresponding test-case specifications), understand the framework's basics, and have a set of architectural guidelines, let's get to work.

For the development of the test-case implementations, I prefer to work in Eclipse. First of all, it has a nice JUnit test runner. You can select a Java class, and from the Run menu, you can run it as a JUnit unit test. The runner displays the list of recognized test methods and the execution result. When everything goes okay during the test run, it gives a nice green line. If an exception or assertion failure occurred, it displays a distressing red line. I think the visual feedback is really important—it offers a sense of accomplishment, especially when writing unit tests for your own code. I also like to use Eclipse for its refactoring capabilities. If I realize that within a test-case class I need to copy and paste code sections, I can just use the Refactoring menu to create a method from the code section instead. If I realize that numerous test cases will use the same method, I can use the menu to pull up my method into my base class.

Based on the architectural requirements above, for each project, I typically create a base test-case class, which extends the JUnit TestCase class. I call it ConfigurableTestCase. Each test-case implementation extends this class, see Figure 2.

ConfigurableTestCase typically contains the common methods and initialization code for the test case. I use a property file to store the server name, the application context, various login names for each role, and some additional settings.

The specific test-case implementations contain one test method per test-case scenario (from the test-case specification document). Each method typically logs in with a specific role and then executes the interaction with the Web application. Most test cases do not need a specific user to accomplish the activities; they typically require a user in a specific role, like Administrator, or Visitor, or Registered User. I always create a LoginMode enum, which contains the available roles. I use the Jakarta Commons ValuedEnum package to create enums for the roles. When a specific test method in a test-case implementation logs in, it must specify which login role is required for that particular test scenario. Of course, the ability to log in with a specific user should also be possible, for example, to verify the Registered User use case.

After each request and response cycle, we typically need to verify if the returned page contains an error, and we need to verify our assertions about what content the response should contain. We must be careful here as well; we should only verify items that are not variable and not too fragile in the application. For example, if we assert specific page titles, our tests will probably not run if the language is selectable in the application and we want to verify a different language deployment. Similarly, there's little point in checking an item on the page based on its position within a table layout; table-based designs change frequently, so we should strive to identify elements based on their IDs. In case some important elements on the page don't have IDs or names, we should just ask the developers to add them, rather than trying to work around them.

JUnit assertions offer a poor approach for checking if the look and feel, layout, and page design comply with the requirements. It is possible, given an infinite amount of time for the test development, but a good human tester can assess these things more efficiently. So concentrate on verifying the Web application's functionality, rather than checking everything possible on the page.

Here's an updated test scenario based on our test-case architecture. The class extends ConfigurableTestCase, and the login details are handled in the base class:

 /** * Verifies that submitting the login form with the name "master" results * in a page containing the text "Top Secret" **/ public void testGoodLogin() throws Exception { WebConversation conversation = new WebConversation(); WebResponse response = login(conversation, LoginMode.ADMIN_MODE); assertTrue( "Login not accepted", response.getText().indexOf( "You made it!" ) != -1 ); assertEquals( "Page title", "Top Secret", response.getTitle() ); } 

Tips and tricks

Most scenarios can be handled quite easily by setting WebForm parameters and then looking for specific elements with results in the WebResponse pages, but there are always some challenging test cases.





структура или променя идентификатора на елемент на формуляр, посетителят вероятно няма да види никаква разлика, но вашите тестови скриптове ще бъдат издухани. Очаквайте много преработка и промяна за всяко изпълнение на тестовия случай. Обектно-ориентираният дизайн може да намали усилията за преработване на общи части в тестовите случаи, но от гледна точка на инженер или тестер за осигуряване на качеството съм сигурен, че един, който взаимодейства с уебсайт, е по-лесен за поддръжка и поправяне. #####