Tests du futur avec Spock LEMEE Mathilde Indépendante Email : mathilde@java-freelance.fr Twitter : @MathildeLemee LinkedIn : http://fr.linkedin.com/in/ MathildeLemee Blog : http://www.java-freelance.fr
Problématique
Spock
def "stack should pus h" (){ given: def stack = new Stack(); def elem = "push me" when: stack.push(elem) then: !stack.empty stack.size() == 1 stack.peek() == elem }
Map<String, User> dummyData ; public UserService() { dummyData = new HashMap<String, User>(); dummyData .put( &quot;user1&quot; , new User( &quot;John&quot; , &quot;Doe&quot; , 123456)); dummyData .put( &quot;user2&quot; , new User( &quot;Jane&quot; , &quot;Doe&quot; , 654321)); dummyData .put( &quot;user3&quot; , new User( &quot;John&quot; , &quot;Smith&quot; , 789987));} public User get(String username) { return dummyData .get(username);}
public void userShouldBeWellFilled() { given: UserService service = new UserService() when: User user = service.get( &quot;user1&quot; ) then: user. firstName == 'John' user. lastName == 'Doe' user. id == 123456 } }
Blocs given = setup when – then ou expect where cleanup
when: def x = Math.max(1, 2) then: x == 2 expect: Math.max(1, 2) == 2
Equivalent Spock - jUnit setup()  @Before cleanup()  @After setupSpec()  @BeforeClass cleanupSpec()  @AfterClass
setup SecurityService securityService ; UserService service ; def setup() { securityService = Mock(SecurityService) service = new UserService() service . securityService = securityService }
Rappel Stub = Bouchon = Stupide => Test d'état Mock = Simulacre = Intelligent => Test comportement
public User getManager(String username) { User manager = securityService .getManager(username); if (manager == null ) { manager = dummyData .get( &quot;user1&quot; ); } return manager; }
+ Spock securityService.getManager( &quot;user2&quot; )>> new User(firstName: &quot;John&quot; , lastName: &quot;Smith&quot; , id: 789986 ) securityService.getManager( &quot;user2&quot; ) >>>[ new User( &quot;John&quot; ),new User( &quot;J&quot;)]
Stub given: securityService .getManager( &quot;user2&quot; )>> new User( &quot;John&quot; , &quot;Smith&quot; , 789986 ) when: User manager = service .getManager( &quot;user2&quot; ) then: manager. id == 789986
Interaction mock.receive()  mock.receive(_)  mock.receive(!null)  mock.receive(event)  mock.receive(!event)  mock.receive(_ as Message)  mock./set.*/(_)
def &quot;getManager should securityService.getManage r&quot; () { when: service . getManager &quot;user1&quot; then: 1 * securityService .getManager ( _ )
Cardinalité 1 * (n.._) (_..n) 0 * _
Argument Matcher public Boolean auditByUser(String username) { User user =  dummyData .get(username); return securityService .hasAccess(user); }
@Test public void testArgumentMatcher() { userService .auditByUser( &quot;user1&quot; ); verify ( securityService ).hasAccess( argThat ( new isUserId123456())); } class isUserId123456 extends ArgumentMatcher<User> { public boolean matches(Object object) { return ((User) object).getId() == 123456; }}
when: service.auditByUser( &quot;user1&quot; ) then: 1 *securityService.hasAccess { it . id == 123456 } }
Exceptions @Test (expected = RuntimeException. class ) public void testRuntimeExceptionShouldNotBeCatch() { when ( userService .getManager( &quot;user1&quot; )).thenThrow( new RuntimeException()); userService .getManager( &quot;user1&quot; ); }
securityService.getManager( &quot;user2&quot; )>>{ throw new RuntimeException()} when: service.getManager( &quot;user2&quot; ) then: thrown(RuntimeException)
Exceptions … >> {throw …} thrown / notThrown @FailsWith
Data Driven Testing
def &quot;user should be well file d&quot; () { setup: User service = new UserService() expect: User user = service.get( username ) user. firstName == firstname user. id == userID where: username << [ 'user1' , 'user2' , 'user3' ] firstname << [ 'John' , 'Jane' , 'John' ] UserID << [ 123456 , 654321 , 789987 ] }
def &quot;user should be well filed with table forma t&quot; () { setup: UserService service = new UserService() expect: User user = service.get( username ) user. firstName == firstname user. id == userID where: username | firstname | lastname | userID 'user1' | 'John' | 'Doe' | 123456 'user2' | 'Jane' | 'Doe' | 654321 'user3' | 'John' | 'Smith' | 789987
@Unroll @Unroll ( &quot;user #username should be #firstname #lastname with id #userID&quot; ) def &quot;user should be well filed with table format and unrol l&quot; () { [...] where: username | firstname | lastname | userID 'user1' | 'John' | 'Doe' | 123456 'user2' | 'Jane' | 'Doe' | 654321 'user3' | 'John' | 'Smith' | 789987 }
Comparaison Junit
@RunWith (Parameterized. class ) public class DataDrivenSimpleTest { private String username ; private String firstName ; private String lastName ; private int id ; @Parameters public static Collection<Object[]> data() { return Arrays. asList ( new Object[][] {   { &quot;user1&quot; , &quot;John&quot; , &quot;Doe&quot; , 123456},   { &quot;user2&quot; , &quot;Jane&quot; , &quot;Doe&quot; , 654321},   { &quot;user3&quot; , &quot;John&quot; , &quot;Smith&quot; , 789987 }}); }
public DataDrivenSimpleTest(String username,String firstName, String lastName, int id) { super (); this . username =username; this . firstName = firstName; this . lastName = lastName; this . id = id; } @Test public void testUserService() { UserService userService = new UserService(); User user = (User)userService.get( username ); assertEquals (user.getFirstName(), firstName ); assertEquals (user.getLastName(), lastName ); assertEquals (user.getId(), id );
@Test public void testUserService() { def userService = new UserService() [ user1: [ 'John' , 'Doe' , 123456 ], user2: [ 'Jane' , 'Doe' , 654321 ], user3: [ 'John' , 'Smith' , 789987 ], ]. each { username, userData -> def user = userService.get(username) assert user. firstName == userData[ 0 ] assert user. lastName == userData[ 1 ] assert user. id == userData[ 2 ]
def &quot;user should be well filed with table forma t&quot; () { setup: def service = new UserService() expect: def user = service.get( username ) user. firstName == firstname user. id == userID where: username | firstname | lastname | userID 'user1' | 'John' | 'Doe' | 123456 'user2' | 'Jane' | 'Doe' | 654321 'user3' | 'John' | 'Smith' | 789987
Léger
Vraiment différent
Nouveautés Dynamic reordering Hamcrest IDE
Démo 1
Et ailleurs ?
Geb class GebGoogleTest extends GebTest { String getBaseUrl() { &quot;http://geb.codehaus.org&quot; } void testSomething() { go &quot;/manual/latest/index.html&quot; assert $ ( &quot;h1&quot; ). text () == &quot;The Book Of Geb&quot; }
void testGoogleHomePage(){ go &quot;/wiki/Wikip%C3%A9dia:Accueil_principal&quot; assert $ ( &quot;title&quot; ). text () == &quot;Wikipédia, l'encyclopédie libre&quot; } void testGoogleHomePage(){ when : go &quot;/wiki/Wikip%C3%A9dia:Accueil_principal&quot; then : $ ( &quot;title&quot; ). text () == &quot;Wikipédia, l'encyclopédie libre&quot; }
void testWikipediaSearch() { go &quot;/wiki/Wikip%C3%A9dia:Accueil_principal&quot; // enter wikipedia into the search field $ ( &quot;input&quot; , name: &quot;search&quot; ). value ( &quot;spock&quot; ) def firstLink = $ ( &quot;button&quot; , name: &quot;button&quot; ) firstLink. click () assert $ ( &quot;a&quot; , title: &quot;Spock (Star Trek)&quot; ). text () == &quot;Spock&quot; }
void testWikipediaSearch() { given : go &quot;/wiki/Wikip%C3%A9dia:Accueil_principal&quot; $ ( &quot;input&quot; , name: &quot;search&quot; ). value ( &quot;spock&quot; ) def firstLink = $ ( &quot;button&quot; , name: &quot;button&quot; ) when : firstLink. click () then : $ ( &quot;a&quot; , title: &quot;Spock (Star Trek)&quot; ). text () == &quot;Spock&quot; }
Page Object Pattern
Demo 2
Grails mockDomain(class, testInstances = )mockForConstraintsTests(class,testInstances= mockLogging(class, enableDebug = false) mockController(class) mockTagLib(class)
class Author { String firstname String lastname static hasMany = [books:Book] static constraints = { firstname (blank: false ,maxSize: 20 ) lastname (blank: false ,maxSize: 20 ) } String toString(){ &quot;$ firstname $ lastname ($ id )&quot; }
Grails Domain void testLastNameConstraints() { mockForConstraintsTests(Author) [test1: [ &quot;123456789012345678901&quot; , false ], test2: [ &quot;12345678901234567890&quot; , true ], test3: [ &quot;&quot; , false ], ]. each { test, testData -> def author = new Author(firstname: &quot;John&quot; , lastname: testData[ 0 ]) author.validate() assert author. hasErrors () == !testData[ 1 ] }
def &quot;lastname constraint s&quot; () { setup: mockForConstraintsTests(Author) when: def author = new Author(firstname: &quot;John&quot; , lastname: lastname ) author.validate() then: author. hasErrors () == ! valid where: lastname | valid &quot;123456789012345678901&quot; | false &quot;12345678901234567890&quot; | true &quot;&quot; | false }
Grails Controller class AuthorController { def index = { redirect (action: &quot;list&quot; , params : params ) } def list = { params . max = Math. min ( params . max ? params . int ( 'max' ) : 10 , 100 ) [authorInstanceList: Author. list ( params ), authorInstanceTotal: Author. count ()] }
void testIndex() { def controller = new AuthorController() controller. index () assertEquals &quot;list&quot; , controller. redirectArgs . action } def 'index actio n' () { when: controller . index () then: redirectArgs . action == &quot;list&quot; }
void testList () { def authorInstance = new Author ( firstname : &quot;John&quot; ,  lastname : &quot; Doe &quot; ) mockDomain ( Author , [ authorInstance ]) def controller = new AuthorController () assert controller. list () == [ authorInstanceList :  [ authorInstance ], authorInstanceTotal : 1 ] } def ' list action: 1 autho r ' () { setup: mockDomain ( Author , [ authorInstance ]) expect : controller . list () == [ authorInstanceList : [ authorInstance ], authorInstanceTotal : 1 ] where : authorInstance = new Author ( firstname : &quot;John&quot; , lastname : &quot; Doe &quot; )
Questions  ? Code Source sur : https://github.com/MathildeLemee/Spock-MixIt Et pour s’amuser en ligne : http://meetspock.appspot.com/

Test du futur avec Spock

  • 1.
    Tests du futuravec Spock LEMEE Mathilde Indépendante Email : mathilde@java-freelance.fr Twitter : @MathildeLemee LinkedIn : http://fr.linkedin.com/in/ MathildeLemee Blog : http://www.java-freelance.fr
  • 2.
  • 3.
  • 4.
    def &quot;stack shouldpus h&quot; (){ given: def stack = new Stack(); def elem = &quot;push me&quot; when: stack.push(elem) then: !stack.empty stack.size() == 1 stack.peek() == elem }
  • 5.
    Map<String, User> dummyData; public UserService() { dummyData = new HashMap<String, User>(); dummyData .put( &quot;user1&quot; , new User( &quot;John&quot; , &quot;Doe&quot; , 123456)); dummyData .put( &quot;user2&quot; , new User( &quot;Jane&quot; , &quot;Doe&quot; , 654321)); dummyData .put( &quot;user3&quot; , new User( &quot;John&quot; , &quot;Smith&quot; , 789987));} public User get(String username) { return dummyData .get(username);}
  • 6.
    public void userShouldBeWellFilled(){ given: UserService service = new UserService() when: User user = service.get( &quot;user1&quot; ) then: user. firstName == 'John' user. lastName == 'Doe' user. id == 123456 } }
  • 7.
    Blocs given =setup when – then ou expect where cleanup
  • 8.
    when: def x= Math.max(1, 2) then: x == 2 expect: Math.max(1, 2) == 2
  • 9.
    Equivalent Spock -jUnit setup() @Before cleanup() @After setupSpec() @BeforeClass cleanupSpec() @AfterClass
  • 10.
    setup SecurityService securityService; UserService service ; def setup() { securityService = Mock(SecurityService) service = new UserService() service . securityService = securityService }
  • 11.
    Rappel Stub =Bouchon = Stupide => Test d'état Mock = Simulacre = Intelligent => Test comportement
  • 12.
    public User getManager(Stringusername) { User manager = securityService .getManager(username); if (manager == null ) { manager = dummyData .get( &quot;user1&quot; ); } return manager; }
  • 13.
    + Spock securityService.getManager(&quot;user2&quot; )>> new User(firstName: &quot;John&quot; , lastName: &quot;Smith&quot; , id: 789986 ) securityService.getManager( &quot;user2&quot; ) >>>[ new User( &quot;John&quot; ),new User( &quot;J&quot;)]
  • 14.
    Stub given: securityService.getManager( &quot;user2&quot; )>> new User( &quot;John&quot; , &quot;Smith&quot; , 789986 ) when: User manager = service .getManager( &quot;user2&quot; ) then: manager. id == 789986
  • 15.
    Interaction mock.receive() mock.receive(_) mock.receive(!null) mock.receive(event) mock.receive(!event) mock.receive(_ as Message) mock./set.*/(_)
  • 16.
    def &quot;getManager shouldsecurityService.getManage r&quot; () { when: service . getManager &quot;user1&quot; then: 1 * securityService .getManager ( _ )
  • 17.
    Cardinalité 1 *(n.._) (_..n) 0 * _
  • 18.
    Argument Matcher publicBoolean auditByUser(String username) { User user = dummyData .get(username); return securityService .hasAccess(user); }
  • 19.
    @Test public voidtestArgumentMatcher() { userService .auditByUser( &quot;user1&quot; ); verify ( securityService ).hasAccess( argThat ( new isUserId123456())); } class isUserId123456 extends ArgumentMatcher<User> { public boolean matches(Object object) { return ((User) object).getId() == 123456; }}
  • 20.
    when: service.auditByUser( &quot;user1&quot;) then: 1 *securityService.hasAccess { it . id == 123456 } }
  • 21.
    Exceptions @Test (expected= RuntimeException. class ) public void testRuntimeExceptionShouldNotBeCatch() { when ( userService .getManager( &quot;user1&quot; )).thenThrow( new RuntimeException()); userService .getManager( &quot;user1&quot; ); }
  • 22.
    securityService.getManager( &quot;user2&quot; )>>{throw new RuntimeException()} when: service.getManager( &quot;user2&quot; ) then: thrown(RuntimeException)
  • 23.
    Exceptions … >>{throw …} thrown / notThrown @FailsWith
  • 24.
  • 25.
    def &quot;user shouldbe well file d&quot; () { setup: User service = new UserService() expect: User user = service.get( username ) user. firstName == firstname user. id == userID where: username << [ 'user1' , 'user2' , 'user3' ] firstname << [ 'John' , 'Jane' , 'John' ] UserID << [ 123456 , 654321 , 789987 ] }
  • 26.
    def &quot;user shouldbe well filed with table forma t&quot; () { setup: UserService service = new UserService() expect: User user = service.get( username ) user. firstName == firstname user. id == userID where: username | firstname | lastname | userID 'user1' | 'John' | 'Doe' | 123456 'user2' | 'Jane' | 'Doe' | 654321 'user3' | 'John' | 'Smith' | 789987
  • 27.
    @Unroll @Unroll (&quot;user #username should be #firstname #lastname with id #userID&quot; ) def &quot;user should be well filed with table format and unrol l&quot; () { [...] where: username | firstname | lastname | userID 'user1' | 'John' | 'Doe' | 123456 'user2' | 'Jane' | 'Doe' | 654321 'user3' | 'John' | 'Smith' | 789987 }
  • 28.
  • 29.
    @RunWith (Parameterized. class) public class DataDrivenSimpleTest { private String username ; private String firstName ; private String lastName ; private int id ; @Parameters public static Collection<Object[]> data() { return Arrays. asList ( new Object[][] { { &quot;user1&quot; , &quot;John&quot; , &quot;Doe&quot; , 123456}, { &quot;user2&quot; , &quot;Jane&quot; , &quot;Doe&quot; , 654321}, { &quot;user3&quot; , &quot;John&quot; , &quot;Smith&quot; , 789987 }}); }
  • 30.
    public DataDrivenSimpleTest(String username,StringfirstName, String lastName, int id) { super (); this . username =username; this . firstName = firstName; this . lastName = lastName; this . id = id; } @Test public void testUserService() { UserService userService = new UserService(); User user = (User)userService.get( username ); assertEquals (user.getFirstName(), firstName ); assertEquals (user.getLastName(), lastName ); assertEquals (user.getId(), id );
  • 31.
    @Test public voidtestUserService() { def userService = new UserService() [ user1: [ 'John' , 'Doe' , 123456 ], user2: [ 'Jane' , 'Doe' , 654321 ], user3: [ 'John' , 'Smith' , 789987 ], ]. each { username, userData -> def user = userService.get(username) assert user. firstName == userData[ 0 ] assert user. lastName == userData[ 1 ] assert user. id == userData[ 2 ]
  • 32.
    def &quot;user shouldbe well filed with table forma t&quot; () { setup: def service = new UserService() expect: def user = service.get( username ) user. firstName == firstname user. id == userID where: username | firstname | lastname | userID 'user1' | 'John' | 'Doe' | 123456 'user2' | 'Jane' | 'Doe' | 654321 'user3' | 'John' | 'Smith' | 789987
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
    Geb class GebGoogleTestextends GebTest { String getBaseUrl() { &quot;http://geb.codehaus.org&quot; } void testSomething() { go &quot;/manual/latest/index.html&quot; assert $ ( &quot;h1&quot; ). text () == &quot;The Book Of Geb&quot; }
  • 39.
    void testGoogleHomePage(){ go&quot;/wiki/Wikip%C3%A9dia:Accueil_principal&quot; assert $ ( &quot;title&quot; ). text () == &quot;Wikipédia, l'encyclopédie libre&quot; } void testGoogleHomePage(){ when : go &quot;/wiki/Wikip%C3%A9dia:Accueil_principal&quot; then : $ ( &quot;title&quot; ). text () == &quot;Wikipédia, l'encyclopédie libre&quot; }
  • 40.
    void testWikipediaSearch() {go &quot;/wiki/Wikip%C3%A9dia:Accueil_principal&quot; // enter wikipedia into the search field $ ( &quot;input&quot; , name: &quot;search&quot; ). value ( &quot;spock&quot; ) def firstLink = $ ( &quot;button&quot; , name: &quot;button&quot; ) firstLink. click () assert $ ( &quot;a&quot; , title: &quot;Spock (Star Trek)&quot; ). text () == &quot;Spock&quot; }
  • 41.
    void testWikipediaSearch() {given : go &quot;/wiki/Wikip%C3%A9dia:Accueil_principal&quot; $ ( &quot;input&quot; , name: &quot;search&quot; ). value ( &quot;spock&quot; ) def firstLink = $ ( &quot;button&quot; , name: &quot;button&quot; ) when : firstLink. click () then : $ ( &quot;a&quot; , title: &quot;Spock (Star Trek)&quot; ). text () == &quot;Spock&quot; }
  • 42.
  • 43.
  • 44.
    Grails mockDomain(class, testInstances= )mockForConstraintsTests(class,testInstances= mockLogging(class, enableDebug = false) mockController(class) mockTagLib(class)
  • 45.
    class Author {String firstname String lastname static hasMany = [books:Book] static constraints = { firstname (blank: false ,maxSize: 20 ) lastname (blank: false ,maxSize: 20 ) } String toString(){ &quot;$ firstname $ lastname ($ id )&quot; }
  • 46.
    Grails Domain voidtestLastNameConstraints() { mockForConstraintsTests(Author) [test1: [ &quot;123456789012345678901&quot; , false ], test2: [ &quot;12345678901234567890&quot; , true ], test3: [ &quot;&quot; , false ], ]. each { test, testData -> def author = new Author(firstname: &quot;John&quot; , lastname: testData[ 0 ]) author.validate() assert author. hasErrors () == !testData[ 1 ] }
  • 47.
    def &quot;lastname constraints&quot; () { setup: mockForConstraintsTests(Author) when: def author = new Author(firstname: &quot;John&quot; , lastname: lastname ) author.validate() then: author. hasErrors () == ! valid where: lastname | valid &quot;123456789012345678901&quot; | false &quot;12345678901234567890&quot; | true &quot;&quot; | false }
  • 48.
    Grails Controller classAuthorController { def index = { redirect (action: &quot;list&quot; , params : params ) } def list = { params . max = Math. min ( params . max ? params . int ( 'max' ) : 10 , 100 ) [authorInstanceList: Author. list ( params ), authorInstanceTotal: Author. count ()] }
  • 49.
    void testIndex() {def controller = new AuthorController() controller. index () assertEquals &quot;list&quot; , controller. redirectArgs . action } def 'index actio n' () { when: controller . index () then: redirectArgs . action == &quot;list&quot; }
  • 50.
    void testList (){ def authorInstance = new Author ( firstname : &quot;John&quot; , lastname : &quot; Doe &quot; ) mockDomain ( Author , [ authorInstance ]) def controller = new AuthorController () assert controller. list () == [ authorInstanceList : [ authorInstance ], authorInstanceTotal : 1 ] } def ' list action: 1 autho r ' () { setup: mockDomain ( Author , [ authorInstance ]) expect : controller . list () == [ authorInstanceList : [ authorInstance ], authorInstanceTotal : 1 ] where : authorInstance = new Author ( firstname : &quot;John&quot; , lastname : &quot; Doe &quot; )
  • 51.
    Questions  ? CodeSource sur : https://github.com/MathildeLemee/Spock-MixIt Et pour s’amuser en ligne : http://meetspock.appspot.com/

Editor's Notes

  • #4 Pas que du mock Test full stack
  • #5 Exemple 1
  • #6 Pour les prochains slides voilà la méthode sur laquelle nous allons nous appuyer
  • #7 Exemple 2 simple