Archive for the ‘Tests’ Category

WS testing

Tuesday, March 8th, 2011

This post recaps some results of my experiments with WS testing. In last few months I have been working on several projects – Spring WS Test, Spring WS 2.0 testing support and finally on Smock. All of them reflect different approaches to WS testing. Today I am going recapitulate my findings focusing on testing producer/server side code. Consumer/client side will be covered in a following post.

Simple service, simple test

You may wonder what's the problem? Testing web services is easy. We just need to call the endpoint end verify the response. For a simple web service we can write a simple test like this

@Test
public void testPlus()
{
	assertEquals(3, endpoint.plus(1, 2));
}

The test is same as if I was testing a normal method.

Web Services tend to be complex

The trouble is that web services tend to be complex. Normal Java methods usually have few relatively simple parameters. On the other hand, web service request or response could be quite complicated which makes the service endpoint hard to test.

GetFlightsRequest request = new GetFlightsRequest();
request.setFrom("PRG");
request.setTo("DUB");
request.setServiceClass(ServiceClass.BUSINESS);
request.setDepartureDate(DatatypeFactory.newInstance().newXMLGregorianCalendarDate(2011, 02, 23, 0));
//more setter calls could be here
		
GetFlightsResponse response = endpoint.getFlights(request);

List<Flight> flights = response.getFlight();
assertEquals(1, flights.size());
assertEquals(ServiceClass.BUSINESS, flights.get(0).getServiceClass());
//more assertions here

Such tests are bearable only until certain level of complexity. From certain level it's real pain. I really hate to call twenty setters to construct several levels of nested XML hierarchy and then to call dozens of getters to validate the response.

To define XML use ... the XML
The problem is that we want to create or validate an XML structure using wrong language. Java is just not suitable for the task. The best language to describe XML is, surprise, surprise, the XML. Would not it be easier to create XML files containing the request and response and just use them in the test?

GetFlightsRequest request = JAXB.unmarshal(getStream("request1.xml"), GetFlightsRequest.class);
		
GetFlightsResponse response = endpoint.getFlights(request);
		
DOMResult domResponse = new DOMResult();
JAXB.marshal(response, domResponse);
		
XMLUnit.setIgnoreWhitespace(true);
XMLAssert.assertXMLEqual(getDocument("response1.xml"), (Document)domResponse.getNode());

It's much better. This test will look exactly the same way regardless the complexity of the service. But it still have some shortcomings. The biggest one is that usually testing using only one request is not sufficient. I'd like to test the service using different inputs. In such case I have to create lot of XML files that differ just in few elements. It's a maintenance nightmare. If you change the service, you also have to change all the XML files.

Smock comes to help
Up until now we managed to test without any special framework except XMLUnit. Now we will start to use my precious Smock library. The test above can be rewritten to this form.

Map<String, Object> params = new HashMap<String, Object>();
params.put("from", "DUB");
params.put("to", "JFK");
params.put("serviceClass", "economy");
GetFlightsRequest request = createRequest(
					withMessage("request-context-groovy.xml")
					.withParameters(params), GetFlightsRequest.class);

GetFlightsResponse response = endpoint.getFlights(request);

validate(response, request)
	.andExpect(message("response-context-groovy.xml")
	.withParameter("serviceClass", "economy"));

The test is quite similar as the previous one. The difference is that it uses statically imported method createRequest to create a request and validate method to validate the response. The main advantage is that Smock methods support templates. So we can set just values we want to, the rest can be driven by the template. Moreover we do not need to deal with the request structure, parameters can be set into a simple flat map.

<ns1:GetFlightsRequest>
	<ns1:from>${from}</ns1:from>
	<ns1:to>${to}</ns1:to>
	<ns1:departureDate>2001-01-01</ns1:departureDate>
	<ns1:serviceClass>${serviceClass}</ns1:serviceClass>
</ns1:GetFlightsRequest>

Templates are even more useful when comparing the response

<ns3:GetFlightsResponse>
	<ns3:flight>
		<ns2:number>OK1324</ns2:number>
		<ns2:departureTime>2011-02-19T10:00:00</ns2:departureTime>
		<ns2:from>
			<ns2:code>${GetFlightsRequest.from}</ns2:code>
			<ns2:name>${IGNORE}</ns2:name>
			<ns2:city>${IGNORE}</ns2:city>
		</ns2:from>
		<ns2:arrivalTime>2011-02-19T12:00:00</ns2:arrivalTime>
		<ns2:to>
			<ns2:code>${GetFlightsRequest.to}</ns2:code>
			<ns2:name>${IGNORE}</ns2:name>
			<ns2:city>${IGNORE}</ns2:city>
		</ns2:to>
		<ns2:serviceClass>${serviceClass}</ns2:serviceClass>
	</ns3:flight>
</ns3:GetFlightsResponse>

We can ignore some elements, we can compare values based on the request elements and so on. Moreover, when we are using Smock library, we can leverage Spring WS 2.0 testing capabilities that Smock builds on. We can for example validate that the response is valid regarding to its schema.

validate(response).andExpect(validPayload(resource("xsd/messages.xsd")));

Cool, isn't it?

Testing the configuration
Sometimes unit test are not sufficient. Sometimes we want to test the configuration, interceptors, error handling and so on. No problem. If you are using Spring WS 2, you can use the built-in testing library. If you need templates or if you are using different framework like Axis 2, you can use Smock. The test looks similar.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring-ws-servlet.xml")
public class GroovyEndpointTest {

	private MockWebServiceClient wsMockClient;
	
	@Autowired
	public void setApplicationContext(ApplicationContext context)
	{
		wsMockClient = createClient(context);
	}
	

	@Test
	public void testResponseTemplate() throws Exception {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("from", "DUB");
		params.put("to", "JFK");
		params.put("serviceClass", "economy");
		wsMockClient.sendRequest(
			withMessage("request-context-groovy.xml")
			  .withParameters(params))
			.andExpect(message("response-context-groovy.xml")
			  .withParameter("serviceClass", "economy"));
	}

The only difference is that we have to bootstrap the application context. Having done this, we can use the mock web service client to call our services. Even though this kind of tests is more complicated, we can use it to test object-XML mapping, endpoint resolution, error handling and configuration in general.

We have seen several different methods of WS testing. Each of them have advantages and disadvantages. If you keep you services simple, your tests are simple too. If the service is more complex, it makes sense to use XML for the test. Either using your own helper methods, Spring WS 2.0 test support or Smock.

All the samples could be downloaded from here.

Testujeme WS klienty

Thursday, November 5th, 2009

Chystám se přihřát si vlastní polívčičku a budu psát o tom, na čem právě ve svém volném čase dělám. Jde o knihovnu, která mi pomáhá s testováním Spring WS klientských aplikací. Jako obvykle jsem si chtěl ušetřit sám sobě práci. Jsem totiž docela nakažen testováním, takže se snažím psát unit testy skoro na všechno co dělám.

Některé části aplikace se ale testují dost špatně. Většinou jde o „okraje“ aplikace jako například JSP, komunikaci s databází, volání webových služeb nebo proprietárního kódu. Jinými slovy, pokud je testovaný kód používaný pouze mým kódem, testuje se docela dobře. Pokud je ale na rozhraní mezi mnou a uživatelem, databází nebo něčím jiným, testování se komplikuje.

Proto je důležité psát takové části co nejtenčí. Co to znamená? Vaše JSP stránky by měly být co nejjednodušší, DAO vrstva by neměla obsahovat žádnou logiku a knihovny které používáte by měly být co nejméně invazivní.

Pokud ale tyto věci na periferii chci otestovat, musím se často uchýlit k funkčním testům. K testům, které jsou sice automatizované, ale které vyžadují běžící aplikační server, Spring kontext nebo třeba databázi v paměti jako HSQL. Takové testy jsou obvykle komplikovanější a také jejich spouštění trvá výrazně děle.

Podobný problém mívám i s testy volání webové služby. Samosebou mám toto volání zapouzdřeno ve speciální vrstvě, ale i tu bych rád otestoval. Kód často vypadá podobně jako tento.

public List<Flight> getFlights(String origin, String destination) {
	//prepare request
	GetFlightsRequest request = new GetFlightsRequest();
	request.setFrom(origin);
	request.setTo(destination);
	request.setServiceClass(ServiceClass.ECONOMY);
	request.setDepartureDate(Calendar.getInstance());
	//call the service
	GetFlightsResponse response = (GetFlightsResponse)webServiceTemplate.marshalSendAndReceive(request);
	//convert response
	return response.getFlight();
}

Na začátku musím vyrobit dotaz. Obvykle musím naplnit nějaký objekt vygenerovaný XML mapovacím nástrojem. Pak zavolám službu a její odpověď musím zase převést na něco čemu rozumí moje aplikace.

Jak něco takového otestovat? Samozřejmě to jde, jenom mě to vůbec nebaví. Nejdřív potřebuji otestovat, že jsem správně vytvořil dotaz. Pokud to testuji klasicky, tak ten test vypadá podezřele stejně jako kód samotný. Hrozně mě nebaví testovat, jestli když nastavím datum odletu, tak je opravdu dobře nastaveno. Navíc je to hrozně odtrženo od požadavků. Nikdo po mě přeci nechce, abych plnil nějaký hloupý vygenerovaný objekt. Chtějí po mě, abych vygeneroval XML. Věřím sice, že XML mapovací knihova funguje správně, ale pořád mám pocit, že by bylo pěkné to otestovat nějak lépe.

Pak by bylo dobré otestovat, jestli opravdu volám tu webovou službu. Další trapný a nudný test. Nastavím mock a ověřím, jestli se opravdu zavolá. Nůůůda.

No a pak mě čeká otestovaní, jestli se dobře konvertují data z odpovědi do objektu, který má často podobnou strukturu. Další nuda.

Dá se to dělat lépe? Samozřejmě. Za pomocí našeho skvělého patentovaného přípravku, který vyčistí všechny vaše testovací skvrny bez narušení podstaty látky. Můžeme si totiž připravit požadovaný dotaz i odpověď jako XML, použít naši speciální knihovnu a napsat test například takto

@Test
public void testGetFligts()
{
	
	//create control
	WsMockControl mockControl = new WsMockControl();
	//teach mock what to do and create it
	WebServiceMessageSender mockMessageSender = mockControl.expectRequest("PRG-DUB-request.xml").returnResponse("PRG-DUB-response.xml").createMock();
	webServiceTemplate.setMessageSender(mockMessageSender);
	
	//do the test
	List<Flight> flights = client.getFlights("PRG","DUB");
	assertNotNull(flights);
	assertEquals(1, flights.size());
	assertEquals("PRG",flights.get(0).getFrom().getCode());
	assertEquals("DUB",flights.get(0).getTo().getCode());
	
	//verify that everything was called at least once
	mockControl.verify();
}

Krásné, jednoduché a elegantní. Popis XML dat je realizován v k tomu nejvhodnějším jazyce - v XML. Test mi i automaticky ohlídá, že jsem opravdu službu zavolal. A to nezmiňuji spoustu dalších vymožeností, které zde pro jistotu nezmiňuji, abyste ohromením neomdleli.

Pokud jste tedy zůstali při vědomí vězte, že to všechno je k mání zdarma, na adrese http://javacrumbs.net/spring-ws-test/. Jenom račte prominouti, že dokumentaci ještě nestačila býti plně dohotovena. Naše dokumentační oddělení na ní pilně pracuje tudíž se lze domnívati, že bude dohotovena v nedaleké budoucnosti. Prozatím račte vzíti v potaz příklad použití, který Vám návod jistojistě poskytne.

Test coverage – interesting only when low

Monday, September 28th, 2009

What a nice and controversial title. But I really mean it. Level of test coverage is really interesting only when it's low. Of course, we should aim for highest coverage possible, but the higher the coverage is, the less useful information it brings. Let take the following example and let's assume there are no tests for this method.

	/**
	 Adds positive numbers. If one of the parameters is negative,
	 IllegalArgumentException is thrown.
	 * @param a
	 * @param b
	 * @return
	 */
	public int addPositive(int a, int b)
	{
		if ((a<0) || (b<0))
		{
			throw new IllegalArgumentException("Argument is negative");
		}
		return a+a;
	}

No coverage - wow, that's interesting

The fact that some piece of code is not covered at all is really interesting. It either means, that we have to start writing tests at once or that the piece of code has to be deleted. Either way we know that we should do something. Moreover, we know exactly what to do. For example, we can start with the following test.

	@Test
	public void testAddition()
	{
		assertEquals(2, math.addPositive(1, 1));
	}

It will get us partial coverage like this
Partial coverage

Partial coverage - cool, I know what to test next

If I have partial coverage, I can see what other tests I am supposed to write. In our example I see that I have to test what happens if I pass in a negative numbers.

	@Test(expected=IllegalArgumentException.class)
	public void testNegative1()
	{
		math.addPositive(-1, 1);
	}
	@Test(expected=IllegalArgumentException.class)
	public void testNegative2()
	{
		math.addPositive(1, -1);
	}

Total coverage - I have no clue

Now we have 100% coverage. Well done! Unfortunately, I have no clue what to do next. I have no idea if I need to write more tests, I do not know if I have complete functional coverage. I have to stop looking at the coverage and I have to start thinking. You see, when the coverage was low it gave me lot of useful information, when it's high I am left on my own. For example I can think hard and come up with following complex test scenario.

	@Test
	public void testComplicatedAddition()
	{
		assertEquals(3, math.addPositive(1, 2));
	}

Please note, that the coverage was already 100% before I have written the test. So one more test could not make it higher. Nevertheless the test actually detected a bug that was there from the beginning. It means that 100% code coverage is not enough, we have to aim higher. We should care about functional coverage and not about code coverage!

That's also one of many reasons why we should do test driven development. Or at least test first development. If we write the tests before the code, we are forced to think from the beginning. In TDD we do not care about the coverage. We just think about the functionality, write corresponding tests and then add the code. Total coverage is automatically achieved.

In the opposite situation, when we write the code first and then add the tests as an afterthought, we are not forced to think. We can just write some tests, achieve the target coverage and be happy. Test coverage can be really deceitful. It can give us false sense of security. So please remember, test coverage is a good servant but a bad master.