////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
////

Functional tests involve making HTTP requests against the running application and verifying the resultant behaviour. This is useful for end-to-end testing scenarios, such as making REST calls against a JSON API.

Grails by default ships with support for writing functional tests using the https://groovy.apache.org/geb/[Geb framework]. To create a functional test you can use the `create-functional-test` command which will create a new functional test:

[source,groovy]
----
$ grails create-functional-test MyFunctional
----

The above command will create a new Spock spec called `MyFunctionalSpec.groovy` in the `src/integration-test/groovy` directory. The test is annotated with the link:{api}grails/testing/mixin/integration/Integration.html[Integration] annotation to indicate it is an integration test and extends the `GebSpec` super class:


[source,groovy]
----
@Integration
class HomeSpec extends GebSpec {

    def setup() {
    }

    def cleanup() {
    }

    void "Test the home page renders correctly"() {
        when:"The home page is visited"
            go('/')

        then:"The title is correct"
            $('title').text() == "Welcome to Grails"
    }
}
----

When the test is run the application container will be loaded up in the background and you can send requests to the running application using the Geb API.

Note that the application is only loaded once for the entire test run, so functional tests share the state of the application across the whole suite.

In addition the application is loaded in the JVM as the test, this means that the test has full access to the application state and can interact directly with data services such as GORM to setup and cleanup test data.

The `Integration` annotation supports an optional `applicationClass` attribute which may be used to specify the application class to use for the functional test.  The class must extend link:{api}grails/boot/config/GrailsAutoConfiguration.html[GrailsAutoConfiguration].

[source,groovy]
----
@Integration(applicationClass=com.demo.Application)
class HomeSpec extends GebSpec {

    // ...

}
----

If the `applicationClass` is not specified then the test runtime environment will attempt to locate the application class dynamically which can be problematic in multiproject builds where multiple application classes may be present.

When running the server port by default will be randomly assigned. The `Integration` annotation adds a property of `serverPort` to the test class that you can use if you want to know what port the application is running on this isn't needed if you are extending the `GebSpec` as shown above but can be useful information.

If you want to run the tests on a fixed port (defined by the `server.port` configuration property), you need to manually annotate your test with `@SpringBootTest`:

[source,groovy]
----
import grails.testing.mixin.integration.Integration
import org.springframework.boot.test.context.SpringBootTest
import spock.lang.Specification

@Integration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class MySpec extends Specification {

    // ...

}
----


==== Simulating Latency

Functional tests are prone to timing bugs: an assertion that races a page navigation, a missing
wait, or an assumption about response ordering may pass reliably on a fast local machine and fail
intermittently on slower CI infrastructure. The `grails-testing-support-latency` module makes such
bugs reproducible by injecting artificial latency into the application under test, so that requests
randomly take longer.

Add the dependency to your integration test configuration:

[source,groovy]
----
dependencies {
    integrationTestRuntimeOnly 'org.apache.grails:grails-testing-support-latency'
}
----

The module is inert until enabled. To activate it, set `grails.testing.latency.enabled` to `true`,
for example in `application.yml` under a test environment, or as a system property on the test JVM:

[source,groovy]
----
tasks.named('integrationTest', Test) {
    systemProperty('grails.testing.latency.enabled', 'true')
}
----

When enabled, a servlet filter is registered with the highest precedence that delays each matched
request by a random amount of time before it is processed, mirroring what a slow network or an
overloaded server looks like to the browser or HTTP client driving the test.

The following configuration properties are supported under the `grails.testing.latency` prefix:

[format="csv", options="header"]
|===
Property,Default Value,Description
enabled,`false`,Whether to inject artificial latency
min-delay,`0`,Lower bound (inclusive) of the random delay per request
max-delay,`2s`,Upper bound (inclusive) of the random delay per request
probability,`1.0`,Fraction of requests to delay between `0.0` (none) and `1.0` (all)
url-patterns,`/*`,Servlet url patterns the filter applies to
seed,none,Optional fixed random seed for reproducible delay sequences
|===

NOTE: The random generator is shared by all requests, so a fixed `seed` produces a reproducible
_sequence_ of delays, but with concurrent requests which request receives which delay depends on
arrival order. Delays are only fully reproducible per request when requests are serialized.

Durations without a unit suffix are interpreted as milliseconds; suffixes such as `500ms`, `2s` are
also supported. For example, to delay half of all requests by between a quarter and two seconds:

[source,yaml]
----
grails:
    testing:
        latency:
            enabled: true
            min-delay: 250ms
            max-delay: 2s
            probability: 0.5
----

A useful pattern is a dedicated CI job (or an occasional local run) that executes the functional
test suite with latency enabled and a generous `max-delay`. Tests that only pass on a fast server —
for example a Geb assertion that reads the page before a click-triggered navigation completes —
will fail deterministically under latency instead of flaking rarely in CI. Keep in mind that each
delay blocks a container thread for its duration, so a large `max-delay` combined with highly
parallel tests can exhaust the servlet thread pool and distort results — size delays and test
parallelism accordingly.

WARNING: This module is a testing aid. Never enable it in production configuration.
