Polish HtmlUnit support in the reference manual

Issues SPR-13158
This commit is contained in:
Sam Brannen
2015-07-28 18:46:18 +02:00
parent b74377932c
commit 8de7848ab3

View File

@@ -4158,31 +4158,30 @@ coverage based on Spring MVC Test.
==== HtmlUnit Integration
Spring provides integration between <<spring-mvc-test-server,MockMvc>> and
http://htmlunit.sourceforge.net/[HtmlUnit]. This simplifies performing end to end testing
http://htmlunit.sourceforge.net/[HtmlUnit]. This simplifies performing end-to-end testing
when using HTML based views. This integration enables developers to:
* Easily test pages using tools (i.e. http://htmlunit.sourceforge.net/[HtmlUnit],
* Easily test HTML pages using tools such as http://htmlunit.sourceforge.net/[HtmlUnit],
http://seleniumhq.org/projects/webdriver/[WebDriver], &
http://www.gebish.org/manual/current/testing.html#spock_junit__testng[Geb]) that we
already use for integration testing without starting an application server
* Support testing of JavaScript
* Optionally test using mock services to speed up testing.
* Share logic between end-to-end tests and integration tests
http://www.gebish.org/manual/current/testing.html#spock_junit__testng[Geb] without the
need to deploy to a Servlet container
* Test JavaScript within pages
* Optionally test using mock services to speed up testing
* Share logic between in-container end-to-end tests and out-of-container integration tests
[NOTE]
====
MockMvc will work with templating technologies that do not rely on a Servlet Container
(i.e. Thymeleaf, Freemarker, Velocity, etc). It does not work with JSPs since they rely on
`MockMvc` works with templating technologies that do not rely on a Servlet Container (e.g.,
Thymeleaf, Freemarker, Velocity, etc.), but it does not work with JSPs since they rely on
the Servlet Container.
====
[[spring-mvc-test-server-htmlunit-why]]
===== Why HtmlUnit Integration?
The most obvious question that comes to mind is "Why do I need this?" The answer is best
The most obvious question that comes to mind is, "Why do I need this?". The answer is best
found by exploring a very basic sample application. Assume you have a Spring MVC web
application that allows CRUD operations on a `Message` object. The application also allows
application that supports CRUD operations on a `Message` object. The application also supports
paging through all messages. How would you go about testing it?
With Spring MVC Test, we can easily test if we are able to create a `Message`.
@@ -4228,9 +4227,9 @@ mockMvc.perform(get("/messages/form"))
.andExpect(xpath("//textarea[@name='text']").exists());
----
This test has some obvious problems. If we updated our controller to use the parameter
"message" instead of "text", our test would would incorrectly pass. To resolve this we
could combine our two tests:
This test has some obvious drawbacks. If we update our controller to use the parameter
`message` instead of `text`, our form test would continue to pass even though the HTML
form is out of synch with the controller. To resolve this we can combine our two tests.
[[spring-mvc-test-server-htmlunit-mock-mvc-test]]
[source,java]
@@ -4251,75 +4250,72 @@ mockMvc.perform(createMessage)
----
This would reduce the risk of our test incorrectly passing, but there are still some
problems:
problems.
* What if we had multiple forms on our page? Admittedly we could update our xpath
expressions, but they get more complicated the more factors we take into account (are the
fields the correct type, are the fields enabled, etc).
* What if we have multiple forms on our page? Admittedly we could update our xpath
expressions, but they get more complicated the more factors we take into account (Are the
fields the correct type? Are the fields enabled? etc.).
* Another issue is that we are doing double the work we would expect.
We must first verify the view and then we submit the view with the same parameters we just
verified.
Ideally this could be done all at once.
* Last, there are some things that we still cannot account for. For example, what if the
form has JavaScript validation that we wish to validate too?
We must first verify the view, and then we submit the view with the same parameters we just
verified. Ideally this could be done all at once.
* Finally, there are some things that we still cannot account for. For example, what if the
form has JavaScript validation that we wish to test as well?
The overall problem is that testing a web page is not a single interaction. Instead, it is
a combination of how the user interacts with a web page and how that web page interacts
with other resources. For example, the result of form view is used as an input to a user
for creating a message. Another example is that our form view utilizes additional
resources, like JavaScript validation, that impact the behavior of the page.
The overall problem is that testing a web page does not involve a single interaction.
Instead, it is a combination of how the user interacts with a web page and how that web
page interacts with other resources. For example, the result of a form view is used as
the input to a user for creating a message. In addition, our form view may potentially
utilize additional resources which impact the behavior of the page, such as JavaScript
validation.
[[spring-mvc-test-server-htmlunit-why-integration]]
====== Integration testing to the rescue?
To resolve the issues above we could perform integration testing, but this has some
obvious drawbacks. Consider testing the view that allows us to page through the messages.
We might need the following tests:
To resolve the issues above we could perform end-to-end integration testing, but this has
some obvious drawbacks. Consider testing the view that allows us to page through the messages.
We might need the following tests.
* Does our page display a message to the user indicating that no results are available
* Does our page display a notification to the user indicating that no results are available
when the messages are empty?
* Does our page properly display a single message?
* Does our page properly support paging?
To set these tests up we would need to ensure our database contained the proper messages
in it. This leads to a number of problems:
To set up these tests, we would need to ensure our database contained the proper messages
in it. This leads to a number of additional challenges.
* Ensuring the proper messages are in the database can be tedious (think possible foreign
keys).
* Testing would be slow since each test would require ensuring the database was in the
correct state.
* Since our database needs to be in a specific state, we cannot run the test in parallel.
* Assertions on things like auto generated ids, timestamps, etc can be challenging.
* Ensuring the proper messages are in the database can be tedious; consider foreign key
constraints.
* Testing can become slow since each test would need to ensure that the database is in the
correct state.
* Since our database needs to be in a specific state, we cannot run tests in parallel.
* Performing assertions on things like auto-generated ids, timestamps, etc. can be difficult.
These problems do not mean that we should abandon integration testing all together.
Instead, we can reduce the number of integration tests by moving our detailed tests to use
mock services which will perform much faster. We can then use fewer integration tests that
validate simple workflows to ensure that everything works together properly.
These challenges do not mean that we should abandon end-to-end integration testing
altogether. Instead, we can reduce the number of end-to-end integration tests by
refactoring our detailed tests to use mock services which will execute much faster, more
reliably, and without side effects. We can then implement a small number of _true_
end-to-end integration tests that validate simple workflows to ensure that everything
works together properly.
[[spring-mvc-test-server-htmlunit-why-mockmvc]]
====== Enter HtmlUnit Integration
So how can we provide a balance between testing the interactions of our pages and still
get performance? I'm sure you already guessed it...integrating with HtmlUnit
will allow us to:
* Easily test our pages using tools (i.e. HtmlUnit, WebDriver, & Geb) that we already use
for integration testing without starting an application server
* Support testing of JavaScript
* Optionally test using mock services to speed up testing.
* Share logic between end-to-end tests and integration tests
So how can we achieve a balance between testing the interactions of our pages and still
retain good performance within our test suite? The answer is: "By integrating MockMvc
with HtmlUnit."
[[spring-mvc-test-server-htmlunit-options]]
====== HtmlUnit Integration Options
There are a number of ways to integrate with HtmlUnit. You can find a summary below:
There are a number of ways to integrate `MockMvc` with HtmlUnit.
* <<spring-mvc-test-server-htmlunit-mah,MockMvc and HtmlUnit>> - Use this option if you want the raw libraries
* <<spring-mvc-test-server-htmlunit-webdriver,MockMvc and WebDriver>> - Use this option to ease development and be able to reuse code
between integration and end-to-end testing.
* <<spring-mvc-test-server-htmlunit-geb,MockMvc and Geb>> - Use this option if you like using Groovy for testing, would like to
ease development, and be able to reuse code between integration and end-to-end testing.
* <<spring-mvc-test-server-htmlunit-mah,MockMvc and HtmlUnit>>: Use this option if you
want to use the raw HtmlUnit libraries.
* <<spring-mvc-test-server-htmlunit-webdriver,MockMvc and WebDriver>>: Use this option to
ease development and reuse code between integration and end-to-end testing.
* <<spring-mvc-test-server-htmlunit-geb,MockMvc and Geb>>: Use this option if you would
like to use Groovy for testing, ease development, and reuse code between integration and
end-to-end testing.
[[spring-mvc-test-server-htmlunit-mah]]
===== MockMvc and HtmlUnit
@@ -4331,7 +4327,7 @@ want to use the raw HtmlUnit libraries.
====== MockMvc and HtmlUnit Setup
We can easily create an HtmlUnit `WebClient` that integrates with `MockMvc` using the
following:
`MockMvcWebClientBuilder` as follows.
[source,java]
----
@@ -4354,15 +4350,17 @@ This is a simple example of using `MockMvcWebClientBuilder`. For advanced usage
<<Advanced MockMvcWebClientBuilder>>
====
This will ensure any URL that has a host of "localhost" will be directed at our MockMvc
instance without the need for HTTP. Any other URL will be requested as normal. This allows
for easily testing with the use of CDNs.
This will ensure that any URL referencing `localhost` as the server will be directed to
our `MockMvc` instance without the need for a real HTTP connection. Any other URL will be
requested using a network connection as normal. This allows us to easily test the use of
CDNs.
[[spring-mvc-test-server-htmlunit-mah-usage]]
====== MockMvc and HtmlUnit Usage
Now we can use HtmlUnit as we normally would, but without the need to deploy our
application. For example, we can request the view to create a message with the following:
application to a Servlet container. For example, we can request the view to create
a message with the following.
[source,java]
----
@@ -4371,11 +4369,12 @@ HtmlPage createMsgFormPage = webClient.getPage("http://localhost/messages/form")
[NOTE]
====
The default context path is `""`. Alternatively, we could have specified the context
path as illustrated in <<Advanced MockMvcWebClientBuilder>>.
The default context path is `""`. Alternatively, we can specify the context path as
illustrated in <<Advanced MockMvcWebClientBuilder>>.
====
We can then fill out the form and submit it to create a message.
Once we have a reference to the `HtmlPage`, we can then fill out the form and submit
it to create a message.
[source,java]
----
@@ -4388,7 +4387,8 @@ HtmlSubmitInput submit = form.getOneHtmlElementByAttribute("input", "type", "sub
HtmlPage newMessagePage = submit.click();
----
Finally, we can verify that a new message was created successfully
Finally, we can verify that a new message was created successfully. The following
assertions use the https://code.google.com/p/fest/[FEST assertion library].
[source,java]
----
@@ -4404,12 +4404,11 @@ assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!");
This improves on our <<spring-mvc-test-server-htmlunit-mock-mvc-test,MockMvc test>> in a
number of ways. First we no longer have to explicitly verify our form and then create a
request that looks like the form. Instead, we request the form, fill it out, and submit
it. This reduces the overhead significantly.
it, thereby significantly reducing the overhead.
Another important factor is that
http://htmlunit.sourceforge.net/javascript.html[HtmlUnit uses Mozilla Rhino engine] to
evaluate JavaScript on your pages. This means, that we can verify our JavaScript methods
as well!
Another important factor is that http://htmlunit.sourceforge.net/javascript.html[HtmlUnit
uses the Mozilla Rhino engine] to evaluate JavaScript. This means that we can test the
behavior of JavaScript within our pages as well!
Refer to the http://htmlunit.sourceforge.net/gettingStarted.html[HtmlUnit documentation]
for additional information about using HtmlUnit.
@@ -4417,7 +4416,9 @@ for additional information about using HtmlUnit.
[[spring-mvc-test-server-htmlunit-mah-advanced-builder]]
====== Advanced MockMvcWebClientBuilder
In our example above we used `MockMvcWebClientBuilder` in the simplest way possible.
In the examples so far, we have used `MockMvcWebClientBuilder` in the simplest way possible,
by building a `WebClient` based on the `WebApplicationContext` loaded for us by the Spring
TestContext Framework. This approach is repeated here.
[source,java]
----
@@ -4434,10 +4435,12 @@ public void setup() {
}
----
We could also specify some optional arguments:
We can also specify additional configuration options.
[source,java]
----
WebClient webClient;
@Before
public void setup() {
webClient = MockMvcWebClientBuilder
@@ -4446,13 +4449,14 @@ public void setup() {
// for illustration only - defaults to ""
.contextPath("")
// By default MockMvc is used for localhost only;
// the following will use MockMvc for example.com and example.org too
// the following will use MockMvc for example.com and example.org as well
.useMockMvcForHosts("example.com","example.org")
.build();
}
----
We could also perform the exact same setup using the following:
As an alternative, we can perform the exact same setup by configuring the `MockMvc`
instance separately and supplying it to the `MockMvcWebClientBuilder` as follows.
[source,java]
----
@@ -4466,7 +4470,7 @@ webClient = MockMvcWebClientBuilder
// for illustration only - defaults to ""
.contextPath("")
// By default MockMvc is used for localhost only;
// the following will use MockMvc for example.com and example.org too
// the following will use MockMvc for example.com and example.org as well
.useMockMvcForHosts("example.com","example.org")
.build();
----
@@ -4483,16 +4487,16 @@ For additional information on creating a `MockMvc` instance refer to
[[spring-mvc-test-server-htmlunit-webdriver]]
===== MockMvc and WebDriver
In the previous section, we have already seen how to use MockMvc with HtmlUnit.
In this section, we will leverage additional abstractions within
In the previous sections, we have seen how to use `MockMvc` in conjunction with the raw
HtmlUnit APIs. In this section, we will leverage additional abstractions within the Selenium
http://docs.seleniumhq.org/projects/webdriver/[WebDriver] to make things even easier.
[[spring-mvc-test-server-htmlunit-webdriver-why]]
====== Why WebDriver and MockMvc?
We can already use HtmlUnit and MockMvc, so why would we want to use WebDriver? WebDriver
provides a very elegant API and allows us to easily organize our code. To better
understand, let's explore an example.
We can already use HtmlUnit and `MockMvc`, so why would we want to use `WebDriver`? The
Selenium `WebDriver` provides a very elegant API that allows us to easily organize our code.
To better understand, let's explore an example.
[NOTE]
====
@@ -4501,14 +4505,15 @@ a Selenium Server to run your tests.
====
Suppose we need to ensure that a message is created properly. The tests involve finding
the html inputs, filling them out, and making various assertions.
the HTML form input elements, filling them out, and making various assertions.
There are many tests because we want to test error conditions as well. For example, we
want to ensure that if we fill out only part of the form we get an error. If we fill out
the entire form, the newly created message is displayed afterwards.
This approach results in numerous, separate tests because we want to test error
conditions as well. For example, we want to ensure that we get an error if we fill out
only part of the form. If we fill out the entire form, the newly created message should
be displayed afterwards.
If one of the fields was named "summary", then we might have something like the following
repeated everywhere within our tests:
If one of the fields were named "summary", then we might have something like the
following repeated in multiple places within our tests.
[source,java]
----
@@ -4516,15 +4521,15 @@ HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
summaryInput.setValueAttribute(summary);
----
So what happens if we change the id to be "smmry".
This means we would have to update all of our tests! Instead we would hope that we wrote a
bit more elegant code where filling out the form was in its own method:
So what happens if we change the `id` to "smmry"? Doing so would force us to update all
of our tests to incorporate this change! Of course, this violates the _DRY Principle_; so
we should ideally extract this code into its own method as follows.
[source,java]
----
public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {
setSummary(currentPage, summary);
...
// ...
}
public void setSummary(HtmlPage currentPage, String summary) {
@@ -4533,19 +4538,20 @@ public void setSummary(HtmlPage currentPage, String summary) {
}
----
This ensures that if we change the UI we do not have to update all of our tests.
This ensures that we do not have to update all of our tests if we change the UI.
We might take it a step further and place this logic within an Object that represents the
`HtmlPage` we are currently on.
We might even take this a step further and place this logic within an Object that
represents the `HtmlPage` we are currently on.
[source,java]
----
public class CreateMessagePage {
HtmlPage currentPage;
HtmlTextInput summaryInput;
final HtmlPage currentPage;
HtmlSubmitInput submit;
final HtmlTextInput summaryInput;
final HtmlSubmitInput submit;
public CreateMessagePage(HtmlPage currentPage) {
this.currentPage = currentPage;
@@ -4575,13 +4581,13 @@ public class CreateMessagePage {
Formerly, this pattern is known as the
https://code.google.com/p/selenium/wiki/PageObjects[Page Object Pattern]. While we can
certainly do this with HtmlUnit, WebDriver provides some tools that we will explore in the
following sections make this pattern much easier.
following sections to make this pattern much easier to implement.
[[spring-mvc-test-server-htmlunit-webdriver-setup]]
====== MockMvc and WebDriver Setup
We can easily create a WebDriver implementation that integrates with MockMvc using the
following:
We can easily create a Selenium `WebDriver` that integrates with `MockMvc` using the
`MockMvcHtmlUnitDriverBuilder` as follows.
[source,java]
----
@@ -4604,15 +4610,17 @@ This is a simple example of using `MockMvcHtmlUnitDriverBuilder`.
For more advanced usage, refer to <<Advanced MockMvcHtmlUnitDriverBuilder>>
====
This will ensure any URL that has a host of "localhost" will be directed at our MockMvc
instance without the need for HTTP. Any other URL will be requested as normal. This allows
for easily testing with the use of CDNs.
This will ensure that any URL referencing `localhost` as the server will be directed to
our `MockMvc` instance without the need for a real HTTP connection. Any other URL will be
requested using a network connection as normal. This allows us to easily test the use of
CDNs.
[[spring-mvc-test-server-htmlunit-webdriver-usage]]
====== MockMvc and WebDriver Usage
Now we can use WebDriver as we normally would, but without the need to deploy our
application. For example, we can request the view to create a message with the following:
application to a Servlet container. For example, we can request the view to create
a message with the following.
[source,java]
----
@@ -4628,10 +4636,10 @@ ViewMessagePage viewMessagePage =
----
This improves on the design of our
<<spring-mvc-test-server-htmlunit-mah-usage,HtmlUnit test>> by leveraging the Page Object
Pattern. As we mentioned in <<spring-mvc-test-server-htmlunit-webdriver-why>>, we could
use the Page Object Pattern with HtmlUnit, but it is much easier now. Let's take a look at
our `CreateMessagePage`.
<<spring-mvc-test-server-htmlunit-mah-usage,HtmlUnit test>> by leveraging the _Page Object
Pattern_. As we mentioned in <<spring-mvc-test-server-htmlunit-webdriver-why>>, we can
use the Page Object Pattern with HtmlUnit, but it is much easier with WebDriver. Let's
take a look at our new `CreateMessagePage` implementation.
[source,java]
----
@@ -4664,27 +4672,28 @@ public class CreateMessagePage
}
----
<1> The first thing you will notice is that our `CreateMessagePage` extends the
`AbstractPage`. We won't go over the details of `AbstractPage`, but in summary it contains
all the common functionality of all our pages. For example, if your application has a
navigational bar, global error messages, etc. This logic can be placed in a shared
<1> The first thing you will notice is that `CreateMessagePage` extends the
`AbstractPage`. We won't go over the details of `AbstractPage`, but in summary it
contains common functionality for all of our pages. For example, if our application has
a navigational bar, global error messages, etc., this logic can be placed in a shared
location.
<2> The next thing you will find is that we have a member variable for each of the parts
of the HTML, `WebElement`, we are interested in. ``WebDriver``'s
https://code.google.com/p/selenium/wiki/PageFactory[PageFactory] allows us to remove a lot
of code from HtmlUnit version of `CreateMessagePage` by automatically resolving each
`WebElement`. The
<2> The next thing you will notice is that we have a member variable for each of the
parts of the HTML page that we are interested in. These are of type `WebElement`.
``WebDriver``'s https://code.google.com/p/selenium/wiki/PageFactory[PageFactory] allows
us to remove a lot of code from the HtmlUnit version of `CreateMessagePage` by
automatically resolving each `WebElement`. The
http://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[PageFactory#initElements(WebDriver,Class<T>)]
method will automatically resolve each `WebElement` by using the field name and trying to
look it up by id or name of the element on the HTML page.
method will automatically resolve each `WebElement` by using the field name and looking it
up by the `id` or `name` of the element within the HTML page.
<3> We can use the
https://code.google.com/p/selenium/wiki/PageFactory#Making_the_Example_Work_Using_Annotations[@FindBy annotation]
to override the default. Our example demonstrates how we can use the `@FindBy` annotation
to lookup our submit button using the css selector of *input[type=submit]*.
to override the default lookup behavior. Our example demonstrates how to use the `@FindBy`
annotation to look up our submit button using a css selector, *input[type=submit]*.
Finally, we can verify that a new message was created successfully
Finally, we can verify that a new message was created successfully. The following
assertions use the https://code.google.com/p/fest/[FEST assertion library].
[source,java]
----
@@ -4692,7 +4701,7 @@ assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage);
assertThat(viewMessagePage.getSuccess()).isEqualTo("Successfully created a new message");
----
We can see that our `ViewMessagePage` can allow us to interact with our custom domain
We can see that our `ViewMessagePage` allows us to interact with our custom domain
model. For example, it exposes a method that returns a `Message` object.
[source,java]
@@ -4709,7 +4718,7 @@ public Message getMessage() throws ParseException {
We can then leverage the rich domain objects in our assertions.
Last, don't forget to close the `WebDriver` instance when we are done.
Lastly, don't forget to _close_ the `WebDriver` instance when the test is complete.
[source,java]
----
@@ -4721,13 +4730,15 @@ public void destroy() {
}
----
For additional information on using WebDriver, refer to the
For additional information on using WebDriver, refer to the Selenium
https://code.google.com/p/selenium/wiki/GettingStarted[WebDriver documentation].
[[spring-mvc-test-server-htmlunit-webdriver-advanced-builder]]
====== Advanced MockMvcHtmlUnitDriverBuilder
In our example above we used `MockMvcHtmlUnitDriverBuilder` in the simplest way possible.
In the examples so far, we have used `MockMvcHtmlUnitDriverBuilder` in the simplest way
possible, by building a `WebDriver` based on the `WebApplicationContext` loaded for us by
the Spring TestContext Framework. This approach is repeated here.
[source,java]
----
@@ -4744,7 +4755,7 @@ public void setup() {
}
----
We could also specify some optional arguments:
We can also specify additional configuration options.
[source,java]
----
@@ -4758,13 +4769,14 @@ public void setup() {
// for illustration only - defaults to ""
.contextPath("")
// By default MockMvc is used for localhost only;
// the following will use MockMvc for example.com and example.org too
// the following will use MockMvc for example.com and example.org as well
.useMockMvcForHosts("example.com","example.org")
.build();
}
----
We could also perform the exact same setup using the following:
As an alternative, we can perform the exact same setup by configuring the `MockMvc`
instance separately and supplying it to the `MockMvcHtmlUnitDriverBuilder` as follows.
[source,java]
----
@@ -4778,7 +4790,7 @@ driver = MockMvcHtmlUnitDriverBuilder
// for illustration only - defaults to ""
.contextPath("")
// By default MockMvc is used for localhost only;
// the following will use MockMvc for example.com and example.org too
// the following will use MockMvc for example.com and example.org as well
.useMockMvcForHosts("example.com","example.org")
.build();
----
@@ -4795,29 +4807,29 @@ For additional information on creating a `MockMvc` instance refer to
[[spring-mvc-test-server-htmlunit-geb]]
===== MockMvc and Geb
In the previous section, we saw how to use MockMvc with WebDriver.
In this section, we will use http://www.gebish.org/[Geb] to make our tests more Groovy.
In the previous section, we saw how to use `MockMvc` with `WebDriver`. In this section,
we will use http://www.gebish.org/[Geb] to make our tests even Groovy-er.
[[spring-mvc-test-server-htmlunit-geb-why]]
====== Why Geb and MockMvc?
Geb is backed by WebDriver, so it offers many of the
<<spring-mvc-test-server-htmlunit-webdriver-why,same benefits>> we got from WebDriver.
However, Geb makes things even easier by taking care of some of the boiler plate code for
us.
<<spring-mvc-test-server-htmlunit-webdriver-why,same benefits>> that we get from
WebDriver. However, Geb makes things even easier by taking care of some of the
boilerplate code for us.
[[spring-mvc-test-server-htmlunit-geb-setup]]
====== MockMvc and Geb Setup
We can easily initialize Geb with a WebDriver implementation that uses `MockMvc` with the
following:
We can easily initialize a Geb `Browser` with a Selenium `WebDriver` that uses `MockMvc`
as follows.
[source,groovy]
----
def setup() {
browser.driver = MockMvcHtmlUnitDriverBuilder
.webAppContextSetup(context, springSecurity())
.webAppContextSetup(context)
.build()
}
----
@@ -4828,15 +4840,17 @@ This is a simple example of using `MockMvcHtmlUnitDriverBuilder`.
For more advanced usage, refer to <<Advanced MockMvcHtmlUnitDriverBuilder>>
====
This will ensure any URL that has a host of "localhost" will be directed at our MockMvc
instance without the need for HTTP. Any other URL will be requested as normal. This allows
for easily testing with the use of CDNs.
This will ensure that any URL referencing `localhost` as the server will be directed to
our `MockMvc` instance without the need for a real HTTP connection. Any other URL will be
requested using a network connection as normal. This allows us to easily test the use of
CDNs.
[[spring-mvc-test-server-htmlunit-geb-usage]]
====== MockMvc and Geb Usage
Now we can use Geb as we normally would, but without the need to deploy our application.
For example, we can request the view to create a message with the following:
Now we can use Geb as we normally would, but without the need to deploy our
application to a Servlet container. For example, we can request the view to create
a message with the following:
[source,groovy]
----
@@ -4857,19 +4871,17 @@ Any unrecognized method calls or property accesses/references that are not found
forwarded to the current page object. This removes a lot of the boilerplate code we needed
when using WebDriver directly.
Additionally, this improves on the design of our
<<spring-mvc-test-server-htmlunit-mah-usage,HtmlUnit test>>. The most obvious change is
that we are now using the Page Object Pattern. As we mentioned in
<<spring-mvc-test-server-htmlunit-webdriver-why>>, we could use the Page Object Pattern
with HtmlUnit, but it is much easier now.
Let's take a look at our `CreateMessagePage`.
As with direct WebDriver usage, this improves on the design of our
<<spring-mvc-test-server-htmlunit-mah-usage,HtmlUnit test>> by leveraging the _Page Object
Pattern_. As mentioned previously, we can use the Page Object Pattern with HtmlUnit and
WebDriver, but it is even easier with Geb. Let's take a look at our new Groovy-based
`CreateMessagePage` implementation.
[source,groovy]
----
class CreateMessagePage extends Page {
static at = { assert title == 'Messages : Create'; true }
static url = 'messages/form'
static at = { assert title == 'Messages : Create'; true }
static content = {
submit { $('input[type=submit]') }
form { $('form') }
@@ -4878,20 +4890,19 @@ class CreateMessagePage extends Page {
}
----
The first thing you will notice is that our `CreateMessagePage` extends the `Page`.
We won't go over the details of `Page`, but in summary it contains base functionality for all our pages.
The next thing you will notice is that we define a URL in which this page can be found.
This allows us to navigate to the page with:
The first thing you will notice is that our `CreateMessagePage` extends `Page`. We won't
go over the details of `Page`, but in summary it contains common functionality for all of
our pages. The next thing you will notice is that we define a URL in which this page can
be found. This allows us to navigate to the page as follows.
[source,groovy]
----
to CreateMessagePage
----
We also have a closure that determines if we are at the specified page.
It should return true if we are on the correct page.
This is why we can assert that we are on the correct page with:
We also have an `at` closure that determines if we are at the specified page. It should return
`true` if we are on the correct page. This is why we can assert that we are on the correct
page as follows.
[source,groovy]
----
@@ -4902,16 +4913,16 @@ errors.contains('This field is required.')
[NOTE]
====
We use an assertion in the closure, so we can determine where things went wrong if we were
at the wrong page.
We use an assertion in the closure, so that we can determine where things went wrong if
we were at the wrong page.
====
We last create a content closure that specifies all the areas of interest within the page.
Next we create a `content` closure that specifies all the areas of interest within the page.
We can use a
http://www.gebish.org/manual/current/intro.html#the_jquery_ish_navigator_api[jQuery-ish Navigator API]
to select the content we are interested in.
Finally, we can verify that a new message was created successfully
Finally, we can verify that a new message was created successfully.
[source,groovy]
----
@@ -4924,6 +4935,9 @@ summary == expectedSummary
message == expectedMessage
----
For further details on how to get the most out of Geb, consult
http://www.gebish.org/manual/current/[The Book of Geb] user's manual.
[[spring-mvc-test-client]]
==== Client-Side REST Tests