Skip to content
starting update: Guidelines/Testing-Guide authored by Stefan Borufka's avatar Stefan Borufka
...@@ -17,7 +17,7 @@ Means to keep the test duration as short as possible ...@@ -17,7 +17,7 @@ Means to keep the test duration as short as possible
- Profile tests and identify slow tests, packages, components and operations. - Profile tests and identify slow tests, packages, components and operations.
- There is a gradle scan tool to create a report of the test performance. - There is a gradle scan tool to create a report of the test performance.
- Sort tests by test duration and try to reduce it (disadvantage: total test time depends on intrinsic complexity) - Sort tests by test duration and try to reduce it (disadvantage: total test time depends on intrinsic complexity)
- Methods that are used by normal users shouldn't only tested as admin or operator - Methods that are used by normal users shouldn't only tested as admin or operator or observer
Structure for tests Structure for tests
...@@ -33,7 +33,7 @@ Structure for tests ...@@ -33,7 +33,7 @@ Structure for tests
- cleanup - cleanup
- tests - tests
- abstract method - abstract method
- private help methods - private helper methods
- override methods - override methods
- help classes - help classes
...@@ -41,15 +41,15 @@ Structure for tests ...@@ -41,15 +41,15 @@ Structure for tests
Conventions for Tests Conventions for Tests
--------------------- ---------------------
The default testing framework in Grails was changed to Spock in recent versions. Spock's naming conventions are: The default testing framework in Grails is Spock. Spock's naming conventions are:
- Unit Test SHOULD end in "Spec" and MUST extend ```Specification```. - Unit Test (classes under ```test```) SHOULD end in "Spec" and MUST extend ```Specification``` and implements ```DataTest```.
- Integration Test should end in "IntegrationSpec" and MUST extend ```IntegrationSpec```. - Integration Test (classes under ```integration-test```) should end in "IntegrationSpec" and MUST extend ```Specification``` and annotated with ```@Rollback``` and ``` @Integration```.
For "legacy" tests (JUnit), the development team agreed on the following naming conventions for tests: For "legacy" tests (JUnit), the development team agreed on the following naming conventions for tests:
- Unit tests (classes under ```test/unit```) SHOULD end in "UnitTests". - Unit tests (classes under ```test```) SHOULD end in "UnitTests".
- Integration tests (classes under ```test/integration```) SHOULD end in "Tests". - Integration tests (classes under ```integration-test```) SHOULD end in "Tests".
Unit Testing (With Fake Objects) Unit Testing (With Fake Objects)
-------------------------------- --------------------------------
...@@ -82,16 +82,24 @@ It also provides its own mocking framework that is very easy to use. It should b ...@@ -82,16 +82,24 @@ It also provides its own mocking framework that is very easy to use. It should b
The mechanisms below are described since a lot of tests are not migrated to Spock and use at least one of those listed. The mechanisms below are described since a lot of tests are not migrated to Spock and use at least one of those listed.
They should not be used in Spock tests, although they may be handy for complicated cases. They should not be used in Spock tests, although they may be handy for complicated cases.
> **Note:** It is not possible in Spock to mock or stub (parts of) the class under test. Spock provides the for types for mocking:
> The whole point of mocking is to get defined behavior of the collaborators. - Stub: Stub a class or interface complete. Only used for overwrite the result, no check about how often it is called.
> Requiring to mock parts of the class is a strong indicator of a design smell, usually a violation of the [Single Responsibility Principle](https://en.wikipedia.org/wiki/Single_responsibility_principle). - Mock: Mock a class or interface complete. Allow mocking only defined methods. All methods not overridden return null or 0
> It should be fixed by improving the design. - Spy: Mock a class partially. Allow mocking only defined methods. Not mocked methods keep there origin functionality.
Note: Requiring to mock parts of the class is a strong indicator of a design smell, usually a violation of the [Single Responsibility Principle](https://en.wikipedia.org/wiki/Single_responsibility_principle).
It should be fixed by improving the design.
If possible avoid Spy.
- GroovyMock: Mock a class or interface with Groovy support, for example mocking dynamic added methods or static methods.
Note: Usually not needed. And should also avoided.
- GroovySpy: Mock a class partially with Groovy support, for example mocking dynamic added methods or static methods.
Note: Same as Spy: Bad design.
#### Mocking #### #### Mocking ####
Mocking is the way Spock does [Interaction Based Testing](https://spockframework.github.io/spock/docs/1.0/interaction_based_testing.html). Mocking is the way Spock does [Interaction Based Testing](https://spockframework.github.io/spock/docs/1.0/interaction_based_testing.html).
It allows you to check if some method you expect to be called in a collaborator is actually called (or not called at all). It allows you to check if some method you expect to be called in a collaborator is actually called (or not called at all).
Besides the number of invocations it allows to check for specific arguments and the order of method calls. Besides, the number of invocations it allows to check for specific arguments, and the order of method calls.
Spock is a lenient mocking framework, meaning that unexpected method calls are allowed and answered with a default response in order to prevent over-specification of a test. Spock is a lenient mocking framework, meaning that unexpected method calls are allowed and answered with a default response in order to prevent over-specification of a test.
(Which makes test brittle.) If you explicitly want to check that no other methods were called, the documentation lists a way to do that. (Which makes test brittle.) If you explicitly want to check that no other methods were called, the documentation lists a way to do that.
...@@ -109,6 +117,7 @@ or ...@@ -109,6 +117,7 @@ or
```groovy ```groovy
Subscriber subscriber = Mock() Subscriber subscriber = Mock()
``` ```
Spock's term for an assertion on a mock is interaction. Spock's term for an assertion on a mock is interaction.
An interaction is composed of a cardinality (how often a method is called) and the target and method constraint (which method on which object is called. An interaction is composed of a cardinality (how often a method is called) and the target and method constraint (which method on which object is called.
It can optionally have an argument constraint to check if the method was called with the correct value. It can optionally have an argument constraint to check if the method was called with the correct value.
...@@ -121,7 +130,7 @@ It can optionally have an argument constraint to check if the method was called ...@@ -121,7 +130,7 @@ It can optionally have an argument constraint to check if the method was called
| target constraint | target constraint
cardinality cardinality
``` ```
A full example may look like the following: Two ```Subscribers``` are registred with a ```Publisher``` in order to be informed. A full example may look like the following: Two ```Subscribers``` are registered with a ```Publisher``` in order to be informed.
If the ```Publisher``` sends a message, it is supposed to call the receive method on all of its Subscribers once. If the ```Publisher``` sends a message, it is supposed to call the receive method on all of its Subscribers once.
```groovy ```groovy
...@@ -146,7 +155,7 @@ class PublisherSpec extends Specification { ...@@ -146,7 +155,7 @@ class PublisherSpec extends Specification {
} }
``` ```
Here, an argument constrained is used to check that the received message is equal to the input message. Here, an argument constrained is used to check that the received message is equal to the input message.
If the argument is no concern, an underscore (_) can used to ignore the passed argument(s). If the argument is no concern, an underscore (_) can be used to ignore the passed argument(s).
It is also possible to use the underscore on all other constraints. It is also possible to use the underscore on all other constraints.
So if you do not care on which object the ```receive()``` method was called, you could write the above test like this: So if you do not care on which object the ```receive()``` method was called, you could write the above test like this:
...@@ -206,7 +215,8 @@ or ...@@ -206,7 +215,8 @@ or
Subscriber subscriber = Stub() Subscriber subscriber = Stub()
``` ```
> **Note:** In Spock, a stub can only be used for stubbing whereas a mock can be used for mocking and stubbing. Using a stub in the declaration shows the role of that object in the test setup and should be preferred if only stubbing is needed. > **Note:** In Spock, a stub can only be used for stubbing whereas a mock can be used for mocking and stubbing.
> Using a stub in the declaration shows the role of that object in the test setup and should be preferred if only stubbing is needed.
A stub usually has a very limited number of interactions, so they are often declared at construction time: A stub usually has a very limited number of interactions, so they are often declared at construction time:
...@@ -216,14 +226,17 @@ def subscriber = Stub(Subscriber) { ...@@ -216,14 +226,17 @@ def subscriber = Stub(Subscriber) {
receive("message2") >> "fail" receive("message2") >> "fail"
} }
``` ```
The above example shows that it is possible to return different values for different invocations using an argument constraint to differentiate between the two. Calling receive() with a message "message2" will always provide the "fail" status code. The above example shows that it is possible to return different values for different invocations using an argument constraint to differentiate between the two.
Calling receive() with a message "message2" will always provide the "fail" status code.
It is also possible to provide sequences of return values that will return the next value in the list on each following invocation. You can use the triple-arrow operator for this: It is also possible to provide sequences of return values that will return the next value in the list on each following invocation.
You can use the triple-arrow operator for this:
```groovy ```groovy
subscriber.receive(_) >>> ["ok", "error", "error", "ok"] subscriber.receive(_) >>> ["ok", "error", "error", "ok"]
``` ```
This will return "ok" for the first invocation, "error" for the second and third, and "ok" for all subsequent invocations. For more advanced uses, such as computing return values based on the arguments, see the documentation. This will return "ok" for the first invocation, "error" for the second and third, and "ok" for all subsequent invocations.
For more advanced uses, such as computing return values based on the arguments, see the documentation.
To perform side effects such as throwing an exception, use a closure: To perform side effects such as throwing an exception, use a closure:
...@@ -276,6 +289,8 @@ But for a lot of times stubbing is all that is needed. ...@@ -276,6 +289,8 @@ But for a lot of times stubbing is all that is needed.
### Faking via MetaClass ### ### Faking via MetaClass ###
> **Note:** This section is kept for legacy tests. It does not apply to Spock tests, as better mechanisms exist. (It can be used, though.)
Another method is using fakes by modifying the MetaClass of an object. Another method is using fakes by modifying the MetaClass of an object.
When used with singletons such as services, this has side effects that could break unrelated tests. When used with singletons such as services, this has side effects that could break unrelated tests.
It violates the principles of isolation and is discouraged. Usually one of the other methods above are sufficient. It violates the principles of isolation and is discouraged. Usually one of the other methods above are sufficient.
...@@ -291,6 +306,9 @@ controller.search() ...@@ -291,6 +306,9 @@ controller.search()
When using a Spock test the annotation ```@DirtiesRuntime``` can be used on methods that change MetaClass. When using a Spock test the annotation ```@DirtiesRuntime``` can be used on methods that change MetaClass.
Spock will take care that the runtime is cleaned so that no side effects arise. Spock will take care that the runtime is cleaned so that no side effects arise.
> **Attention:** If you change a complete class, it is necessary to clean up the changes after the test, otherwise the change stay and influence the next running test.
> Therefore we have an help method: ```TestCase.removeMetaClass```
### Faking via Inheritance ### ### Faking via Inheritance ###
This is the "old-school" method of faking. In this example, the service is extended and the public methods are overridden: This is the "old-school" method of faking. In this example, the service is extended and the public methods are overridden:
... ...
......