index.rst 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. .. _testing:
  2. Testing
  3. =======
  4. This section provides an overview of how testing in GeoServer works and a brief guide for
  5. developers to help with the process of writing tests.
  6. Libraries
  7. ---------
  8. GeoServer utilizes a number of commonly used testing libraries.
  9. JUnit
  10. ^^^^^
  11. The well known `JUnit <http://junit.org>`_ framework is the primary test library used in
  12. GeoServer. The current version used is Junit 4.x. While it is possible to continue to write
  13. JUnit 3.x style tests with JUnit 4, new tests should be written in the JUnit4 style
  14. with annotations.
  15. Current version: 4.11
  16. XMLUnit
  17. ^^^^^^^
  18. The `XMLUnit <http://xmlunit.sourceforge.net>`_ library provides a convenient way to make
  19. test assertions about the structure of XML documents. Since many components and services in
  20. GeoServer output XML, XMLUnit is a very useful library.
  21. Current version: 1.3
  22. MockRunner
  23. ^^^^^^^^^^
  24. The `MockRunner <http://mockrunner.sourceforge.net>`_ framework provides a set of classes that
  25. implement the various interfaces of the J2EE and Java Servlet apis. It is typically used to
  26. create ``HttpServletRequest`` , ``HttpServletResponse``, etc... objects for testing servlet
  27. based components.
  28. Current version: 0.3.6
  29. EasyMock
  30. ^^^^^^^^
  31. The `EasyMock <http://www.easymock.org>`_ library is a
  32. `mocking framework <http://en.wikipedia.org/wiki/Mock_object>`_ that is used to simulate
  33. various objects without actually creating a real version of them. This is an extremely useful
  34. tool when developing unit tests for a component A, that requires component B when component
  35. B may not be so easy to create from scratch.
  36. Current version: 2.5.2
  37. Testing Categories and Terminology
  38. -----------------------------------
  39. Software testing falls into many different categories and the GeoServer code base is no
  40. exception. In the GeoServer code base one may find different types of tests.
  41. Unit
  42. ^^^^
  43. Tests that exercise a particular method/class/component in complete isolation. In GeoServer
  44. these are tests that typically don't extend from any base class and look like what one would
  45. typically expect a unit test to look like.
  46. Integration/Mock
  47. ^^^^^^^^^^^^^^^^
  48. Tests that exercise a component by that must integrate with another component to operate.
  49. In GeoServer these are tests that somehow mock up the dependencies for the component under
  50. test either by creating it directly or via a mocking library.
  51. System
  52. ^^^^^^
  53. Tests that exercise a component or set of components by testing it in a fully running system.
  54. In GeoServer these are tests that create a fully functional GeoServer system, including
  55. a data directory/configuration and a spring context.
  56. Helper classes are provided to help inject your classes into the system configuration including ``GeoServerExtensionsHelper``.
  57. Writing System Tests
  58. --------------------
  59. System tests are the most common type of test case in GeoServer, primarily because they are
  60. the easiest tests to write. However they come with a cost of performance. The GeoServer system
  61. test framework provides a fully functional GeoServer system. Creating this system is an
  62. expensive operation so a full system test should be used only as a last resort.
  63. Developers are encouraged to consider a straight unit or mock tests before resorting to a
  64. full system test.
  65. In GeoServer system tests extend from the ``org.geoserver.test.GeoServerSystemTestSupport`` class.
  66. The general lifecycle of a system test goes through the following states:
  67. #. System initialization
  68. #. System creation
  69. #. Test execution
  70. #. System destruction
  71. Phases 1 and 2 are referred to as the setup phase. It is during this phase that two main
  72. operations are performed. The first is the creation of the GeoServer data directory on
  73. disk. The second is the creation of the spring application context.
  74. Single vs Repeated Setup
  75. ^^^^^^^^^^^^^^^^^^^^^^^^
  76. By default, for performance reasons, the setup phase is executed only once for a system
  77. test. This can however be configured by annotating the test class with a special annotation
  78. named ``TestSetup``. For example, to specify that the setup should be executed many times,
  79. for each test method of the class:
  80. .. code-block:: java
  81. @TestSetup(run=TestSetupFrequency.REPEAT)
  82. public class MyTestCase extends GeoServerSystemTestSupport {
  83. ...
  84. }
  85. This however should be used only as a last resort since as mentioned before a repeated
  86. setup makes the test execute very slowly. An alternative to a repeated setup is to have the
  87. test case revert any changes that it makes during its execution, so that every test method
  88. can execute in a consistent state. The ``GeoServerSystemTestSupport`` contains a number of
  89. convenience methods for doing this. Consider the following test:
  90. .. code-block:: java
  91. public class MyTestCase extends GeoServerSystemTestSupport {
  92. @Before
  93. public void revertChanges() {
  94. //roll back any changes made
  95. revertLayer("foo");
  96. }
  97. @Test
  98. public void testThatChangesLayerFoo() {
  99. //change layer foo in some way
  100. }
  101. }
  102. The test makes some changes to a particular layer but uses a before hook to revert any
  103. such changes. In general this is the recommended pattern for system tests that must are not
  104. read-only and must modify configuration or data to execute.
  105. Method Level SetUp
  106. ^^^^^^^^^^^^^^^^^^
  107. A third method of controlling test setup frequency is available at the test case level.
  108. Annotating a test method with the ``RunTestSetup`` annotation will cause the test setup to be
  109. run before the test method is executed. For example:
  110. .. code-block:: java
  111. public class MyTestCase extends GeoServerSystemTestSupport {
  112. @Before
  113. public void revertChanges() {
  114. //roll back any changes made
  115. revertLayer("foo");
  116. }
  117. @Test
  118. public void test1() {
  119. }
  120. @Test
  121. public void test2() {
  122. }
  123. @Test
  124. @RunTestSetup
  125. public void test3() {
  126. }
  127. @Test
  128. public void test4() {
  129. }
  130. }
  131. In the above method the test setup will be run twice. Once before the entire test class is
  132. run, and again before the test3 method is executed.
  133. Setup/Teardown Hooks
  134. ^^^^^^^^^^^^^^^^^^^^
  135. There are a number of ways to hook into test lifecycle to provide setup and tear down
  136. functionality.
  137. JUnit @Before, @After, @BeforeClass, @AfterClass
  138. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  139. As with any JUnit test various annotations are available to perform tasks at various points
  140. of the test life cycle. However with a GeoServer system test one must be wary of the task having
  141. a dependency on the system state. For this reason the ``GeoServerSystemTestSupport`` class
  142. provides its own callbacks.
  143. setUpTestData
  144. ~~~~~~~~~~~~~
  145. This callback method is invoked before the system has been created. It is meant to provide the
  146. test with a way to configure what configuration gets created in the GeoServer data directory
  147. for the test. By default the test setup will create a standard set of vector layers. This
  148. method is where that should be changed, for instance to indicate that the test requires that
  149. raster layers be created as well. For example:
  150. .. code-block:: java
  151. public class MySystemTest extends GeoServerSystemTestBase {
  152. protected void setUpTestData(SystemTestData testData) {
  153. // do the default by calling super
  154. super.setUpTestData(testData);
  155. // add raster layers
  156. testData.setUpDefaultRasterLayers();
  157. }
  158. }
  159. Depending on whether the test uses a single or repeated setup this method will be called once
  160. or many times.
  161. onSetUp
  162. ~~~~~~~
  163. This callback method is invoked after the system has been created. It is meant for standard
  164. post system initialization tasks. Like for instance changing some service configuration,
  165. adding new layers, etc...
  166. Depending on whether the test uses a single or repeated setup this method will be called once
  167. or many times. For this reason this method can not be used to simply initialize fields of the
  168. test class. For instance, consider the following:
  169. .. code-block:: java
  170. public class MySystemTest extends GeoServerSystemTestBase {
  171. Catalog catalog;
  172. @Override
  173. protected void onTestSetup(SystemTestData testData) throws Exception {
  174. // add a layer named foo to the catalog
  175. Catalog catalog = getCatalog();
  176. catalog.addLayer(new Layer("foo"));
  177. // initialize the catalog field
  178. this.catalog = catalog;
  179. }
  180. @Test
  181. public void test1() {
  182. catalog.getLayerByName("foo");
  183. }
  184. @Test
  185. public void test2() {
  186. catalog.getLayerByName("foo");
  187. }
  188. }
  189. Since this is a one time setup, the onSetUp method is only executed once, before the test1
  190. method. When the test2 method is executed it is actually a new instance of the test class,
  191. but the onTestSetup is not re-executed. The proper way to this initialization would be:
  192. .. code-block:: java
  193. public class MySystemTest extends GeoServerSystemTestBase {
  194. Catalog catalog;
  195. @Override
  196. protected void onTestSetup(SystemTestData testData) throws Exception {
  197. // add a layer named foo to the catalog
  198. Catalog catalog = getCatalog();
  199. catalog.addLayer(new Layer("foo"));
  200. // initialize the catalog field
  201. this.catalog = catalog;
  202. }
  203. @Before
  204. public void initCatalog() {
  205. this.catalog = getCatalog();
  206. }
  207. }
  208. System Test Data
  209. ^^^^^^^^^^^^^^^^
  210. The GeoServer system test will create a data directory with a standard set of
  211. vector layers. The contents of this data directory are as follows:
  212. Workspaces
  213. ~~~~~~~~~~
  214. .. list-table::
  215. :widths: 1 3 1 1
  216. :header-rows: 1
  217. * - Workspace
  218. - URI
  219. - Layer Count
  220. - Default?
  221. * - cdf
  222. - http://www.opengis.net/cite/data
  223. - 8
  224. -
  225. * - cgf
  226. - http://www.opengis.net/cite/geometry
  227. - 6
  228. -
  229. * - cite
  230. - http://www.opengis.net/cite
  231. - 12
  232. -
  233. * - gs
  234. - http://geoserver.org
  235. - 0
  236. - Yes
  237. * - sf
  238. - http://cite.opengeospatial.org/gmlsf
  239. - 3
  240. -
  241. Stores and Layers
  242. ~~~~~~~~~~~~~~~~~
  243. .. list-table::
  244. :widths: 2 2 3 3
  245. :header-rows: 1
  246. * - Workspace
  247. - Store
  248. - Layer Name
  249. - Default Style
  250. * - cdf
  251. - cdf
  252. - Deletes
  253. - Default
  254. * - cdf
  255. - cdf
  256. - Fifteen
  257. - Default
  258. * - cdf
  259. - cdf
  260. - Inserts
  261. - Default
  262. * - cdf
  263. - cdf
  264. - Locks
  265. - Default
  266. * - cdf
  267. - cdf
  268. - Nulls
  269. - Default
  270. * - cdf
  271. - cdf
  272. - Other
  273. - Default
  274. * - cdf
  275. - cdf
  276. - Seven
  277. - Default
  278. * - cdf
  279. - cdf
  280. - Updates
  281. - Default
  282. * - cgf
  283. - cgf
  284. - Lines
  285. - Default
  286. * - cgf
  287. - cgf
  288. - MLines
  289. - Default
  290. * - cgf
  291. - cgf
  292. - MPoints
  293. - Default
  294. * - cgf
  295. - cgf
  296. - MPolygons
  297. - Default
  298. * - cgf
  299. - cgf
  300. - Points
  301. - Default
  302. * - cgf
  303. - cgf
  304. - Polygons
  305. - Default
  306. * - cite
  307. - cite
  308. - BasicPolygons
  309. - BasicPolygons
  310. * - cite
  311. - cite
  312. - Bridges
  313. - Bridges
  314. * - cite
  315. - cite
  316. - Buildings
  317. - Buildings
  318. * - cite
  319. - cite
  320. - DividedRoutes
  321. - DividedRoutes
  322. * - cite
  323. - cite
  324. - Forests
  325. - Forests
  326. * - cite
  327. - cite
  328. - Geometryless
  329. - Default
  330. * - cite
  331. - cite
  332. - Lakes
  333. - Lakes
  334. * - cite
  335. - cite
  336. - MapNeatline
  337. - MapNeatLine
  338. * - cite
  339. - cite
  340. - NamedPlaces
  341. - NamedPlaces
  342. * - cite
  343. - cite
  344. - Ponds
  345. - Ponds
  346. * - cite
  347. - cite
  348. - RoadSegments
  349. - RoadSegments
  350. * - cite
  351. - cite
  352. - Streams
  353. - Streams
  354. * - sf
  355. - sf
  356. - AgregateGeoFeature
  357. - Default
  358. * - sf
  359. - sf
  360. - GenericEntity
  361. - Default
  362. * - sf
  363. - sf
  364. - PrimitiveGeoFeature
  365. - Default
  366. .. note::
  367. The ``gs`` workspace contains no layers. It is typically used as the
  368. workspace for layers that are added by test cases.
  369. Adding custom layers from a datastore
  370. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  371. If you need to provide your test with a specific layer from a local datastore, for example to test handling a
  372. 3D shapefile then you will need code like:
  373. .. code-block:: java
  374. @Override
  375. protected void setUpInternal(SystemTestData data) throws Exception {
  376. DataStoreInfo storeInfo =
  377. createShapefileDataStore(getCatalog(), "tasmania_roads", "tasmania_roads.shp");
  378. createShapeLayer(getCatalog(), storeInfo);
  379. }
  380. private static DataStoreInfo createShapefileDataStore(
  381. Catalog catalog, String name, String file) {
  382. // get the file
  383. URL url = MultiDimensionTest.class.getResource(file);
  384. assertThat(url, notNullValue());
  385. // build the data store
  386. CatalogBuilder catalogBuilder = new CatalogBuilder(catalog);
  387. DataStoreInfo storeInfo = catalogBuilder.buildDataStore(name);
  388. storeInfo.setType("Shapefile");
  389. storeInfo.getConnectionParameters().put(ShapefileDataStoreFactory.URLP.key, url);
  390. catalog.add(storeInfo);
  391. storeInfo = catalog.getStoreByName(name, DataStoreInfo.class);
  392. assertThat(storeInfo, notNullValue());
  393. return storeInfo;
  394. }
  395. private static LayerInfo createShapeLayer(Catalog catalog, DataStoreInfo storeInfo)
  396. throws Exception {
  397. CatalogBuilder catalogBuilder = new CatalogBuilder(catalog);
  398. catalogBuilder.setStore(storeInfo);
  399. Name typeName = storeInfo.getDataStore(null).getNames().get(0);
  400. FeatureTypeInfo featureTypeInfo = catalogBuilder.buildFeatureType(typeName);
  401. catalog.add(featureTypeInfo);
  402. LayerInfo layerInfo = catalogBuilder.buildLayer(featureTypeInfo);
  403. catalog.add(layerInfo);
  404. layerInfo = catalog.getLayerByName(typeName.getLocalPart());
  405. assertThat(layerInfo, notNullValue());
  406. return layerInfo;
  407. }
  408. Once the set up code has run you can request the layer as a WMS or WFS request using:
  409. .. code-block:: java
  410. Document dom = getAsDOM("wfs?request=GetFeature&typenames=gs:tasmania_roads&version=2.0.0&service=wfs");
  411. Writing Mock Tests
  412. ------------------
  413. Mock tests, also referred to as integration tests, are a good way to test a component that
  414. has dependencies on other components. It is often not simple to create the dependent component
  415. with the correct configuration.
  416. A mock test is just a regular unit test that uses functions from the EasyMock library to
  417. create mock objects. There is however a base class named ``GeoServerMockTestSupport`` that
  418. is designed to provide a pre-created set of mock objects. These pre-created mock objects are
  419. designed to mimic the objects as they would be found in an actual running system. For example:
  420. .. code-block:: java
  421. public class MyMockTest extends GeoServerMockTestSupport {
  422. @Test
  423. public void testFoo() {
  424. //get the mock catalog
  425. Catalog catalog = getCatalog();
  426. //create the object we actually want to test
  427. Foo foo = new Foo(catalog);
  428. }
  429. }
  430. Like system tests, mock tests do a one-time setup with the same setUpTestData and onSetUp callbacks.
  431. The benefit of mock tests over system tests is the setup cost. Mock tests essentially have no
  432. setup cost which means they can execute very quickly, which helps to keep overall build times down.
  433. EasyMock Class Extension
  434. ^^^^^^^^^^^^^^^^^^^^^^^^
  435. By default EasyMock can only mock up interfaces. To mock up classes requires the EasyMock classextension jar and also the cglib library. These can be declared in a maven pom like so:
  436. .. code-block:: xml
  437. <dependency>
  438. <groupId>org.easymock</groupId>
  439. <artifactId>easymockclassextension</artifactId>
  440. <scope>test</scope>
  441. </dependency>
  442. <dependency>
  443. <groupId>cglib</groupId>
  444. <artifactId>cglib-nodep</artifactId>
  445. <scope>test</scope>
  446. </dependency>
  447. The change is mostly transparent, however rather than importing ``org.easymock.EasyMock`` one
  448. must import ``org.easymock.classextension.EasyMock``.
  449. Maven Dependencies
  450. ------------------
  451. All of the GeoServer base test classes live in the gs-main module. However since they live in
  452. the test packages a special dependency must be set up in the pom of the module depending
  453. on main. This looks like:
  454. .. code-block:: xml
  455. <dependency>
  456. <groupId>org.geoserver</groupId>
  457. <artifactId>gs-main</artifactId>
  458. <version>${project.version}</version>
  459. <classifier>tests</classifier>
  460. <scope>test</scope>
  461. </dependency>
  462. Furthermore, in maven test scope dependencies are not transitive in the same way that
  463. regular dependencies are. Therefore some additional dependencies must also be declared:
  464. .. code-block:: xml
  465. <dependency>
  466. <groupId>com.mockrunner</groupId>
  467. <artifactId>mockrunner</artifactId>
  468. <scope>test</scope>
  469. </dependency>
  470. <dependency>
  471. <groupId>xmlunit</groupId>
  472. <artifactId>xmlunit</artifactId>
  473. <scope>test</scope>
  474. </dependency>
  475. <dependency>
  476. <groupId>org.easymock</groupId>
  477. <artifactId>easymock</artifactId>
  478. <scope>test</scope>
  479. </dependency>
  480. Online Tests
  481. ------------
  482. Often a test requires some external resource such as a database or a server to operate. Such
  483. tests should never assume that resource will be available and should skip test execution,
  484. rather than fail, when the test is not available.
  485. JUnit4 provides a handy way to do this with the ``org.junit.Asssume`` class. Methods of the
  486. class are called from a ``@Before`` hook or from a test method. For example consider the
  487. common case of connecting to a database:
  488. .. code-block:: java
  489. public class MyTest {
  490. Connection connect() {
  491. //create a connection to the database
  492. try {
  493. Conection cx = ...
  494. return cx;
  495. }
  496. catch(Exception e) {
  497. LOGGER.log(Level.WARNING, "Connection failed", e);
  498. return null;
  499. }
  500. }
  501. @Before
  502. public void testConnection() {
  503. Connection cx = connect();
  504. org.junit.Assume.assumeNotNull(cx);
  505. cx.close();
  506. }
  507. @Test
  508. public void test1() {
  509. // test something
  510. }
  511. }
  512. In the above example the ``assumeNotNull`` method will throw back an exception telling JUnit
  513. to skip execution of the test.