The sample code in this text has been validated in the following environment:
Spock is a testing and specification framework for Java and Groovy applications. Compared to other tools, it features a beautiful and expressive specification language. https://spock-framework-reference-documentation-ja.readthedocs.io/ja/latest/introduction.html
The amount of test coding is overwhelmingly small compared to JUnit! In other words, you can simply increase the efficiency of your unit tests by reducing the amount of coding.
So how can you reduce the amount of test coding?
Spock writes tests in groovy. Groovy's linguistic characteristics allow for lighter coding than Java. If the build is gradle, both will be groovy, so I think it will be quick to learn.
Groovy is a dynamic programming language that runs on the Java platform. https://ja.wikipedia.org/wiki/Groovy
For those who have never used groovy, learning costs may be a burden. However, groovy can write Java code almost as it is, so if you don't know the worst groovy code, you can write it in Java.
How different is the same test coded in JUnit and Spock (groovy)? There was sample code published on Spock's github, so here is an example of coding the same test in JUnit.
JUnit
import static org.junit.Assert.assertEquals;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.Driver;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class DatabaseDrivenTest {
private static Connection con;
@BeforeClass
public static void setup() throws Exception {
Driver.load();
con = DriverManager.getConnection("jdbc:h2:mem:");
Statement stmt = con.createStatement();
stmt.execute("create table maxdata (id int primary key, a int, b int, c int)");
stmt.execute("insert into maxdata values (1, 3, 7, 7), (2, 5, 4, 5), (3, 9, 9, 9)");
stmt.close();
}
@Test
public void testMaximumOfTwoNumbers() throws Exception {
// Math.max(a, b) == c;
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select a, b, c from maxdata");
rs.next();
rs.getInt("a");
assertEquals(Math.max(rs.getInt("a"), rs.getInt("b")), rs.getInt("c"));
stmt.close();
}
@AfterClass
public static void after() throws SQLException {
con.close();
}
}
Here is the code for Spock. https://github.com/spockframework/spock-example/blob/master/src/test/groovy/DatabaseDrivenSpec.groovy
You'll find that groovy is overwhelmingly simple.
Another powerful mechanism is the data launch test. Iterative testing of parameter and result combinations can be easily achieved.
import spock.lang.*
@Unroll
class DataDrivenSpec extends Specification {
def "minimum of #a and #b is #c"() {
expect:
Math.min(a, b) == c //Test execution and result verification
where:
a | b || c //Parameter 1|Parameter 2||result
3 | 7 || 3
5 | 4 || 4
9 | 9 || 9
}
}
This code is a test with the following content: The result of executing Math.min (a, b) with a = 3 and b = 7 is 3 The result of executing Math.min (a, b) with a = 5 and b = 3 is 4 The result of executing Math.min (a, b) with a = 9 and b = 9 is 9
If you want to increase the variation of parameters and results, you can imagine adding a pattern to where. It is a form that is easy to convert from the pattern table of the combination test created in Excel.
If the test fails, an error report will be printed to the console. It's kinder than Java's error stack trace.
Condition not satisfied:
validator.validate() == true
| | |
| false false
<Validator@5aa9e4eb>
at ValidatorSpec.validate test(ValidatorSpec.groovy:40)
Spock also has a mocking function.
Mock HttpServletRequest and rewrite the return value of getRequestURI to "/some/path.html".
def request = Mock(HttpServletRequest)
request.getRequestURI() >> "/some/path.html"
However, there are various restrictions on mocking, so that point will be described later.
You can install it with gradle or maven. Please note that there is a version dependency with groovy.
gradle
testCompile group: 'org.spockframework', name: 'spock-core', version: '1.3-groovy-2.5'
testCompile group: 'org.objenesis', name: 'objenesis', version: '3.0.1'
testCompile group: 'cglib', name: 'cglib', version: '3.2.10'
Maven
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.3-groovy-2.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>3.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
build.gradle
apply plugin: 'java'
apply plugin: 'groovy'
sourceSets {
test {
java {
srcDir 'src/test/groovy'
}
}
}
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'org.spockframework', name: 'spock-core', version: '1.3-groovy-2.5'
testCompile group: 'org.objenesis', name: 'objenesis', version: '3.0.1'
testCompile group: 'cglib', name: 'cglib', version: '3.2.10'
}
Please note that the source directory must be set to'src / test / groovy'.
Eclipse Plugins You need to install the following plug-ins to use it in Eclipse. Please install from Market Place.
Spock Plugin http://marketplace.eclipse.org/content/spock-plugin
Groovy Development Tools http://marketplace.eclipse.org/content/groovy-development-tools
The basic form of the test looks like this.
class ValidatorSpec extends Specification {
@Unroll
def "Sample test param1=#param1 param2=#param2" () {
setup: // 1.Preprocessing
def target =new Tested class()
def mockTarget = Mock(Class you want to mock)
mockTarget.method() >>Value you want to rewrite
target.mockTarget = mockTarget //Assign a mock to a member variable of the class under test
expect: // 2.Execution of the test target
def returnObj = target.targetMethod(param1, param2)
then: // 3.Verification of results
returnObj == result
1 * mockTarget.method()
where: // 4
.Parameter and result combination
| param1 | param2 || result
| "a" | 1 || "ok"
| "b" | 20 || "ng"
}
}
There are the following types of blocks:
block | Description |
---|---|
expect | Test execution and result verification(when + then) |
where | Parameters and results |
when | Run the test target |
then | Verification of results |
setup(given) | Pre-processing |
cleanup | post process |
There are tests that Spock alone cannot do, such as static, final, and private constructors used in legacy code. If you need these tests, we recommend using JMockit.
setup:
new MockUp<LegacyUtil>() {
@Mock
public boolean check() {
return true
}
}
Aiming for 100% test coverage is very difficult, and achieving 100% does not mean high quality. However, there are actually development projects that are required as one of the quality index values. It is difficult to achieve with spock alone, so please make full use of jMockit or use JUnit together.
The principle is to design a class that is easy to test, but I think that there are many opportunities to encounter spaghetti code at the actual development site. Please refactor it to make the test easier.
Spock http://spockframework.org
Spock Framework Reference Document https://spock-framework-reference-documentation-ja.readthedocs.io/ja/latest/
Spock sample code https://github.com/spockframework/spock-example
Recommended Posts