├── .classpath ├── .gitignore ├── .project ├── .travis.yml ├── README.md ├── build.properties_sample ├── build.xml ├── ivy.xml └── src ├── main ├── java │ └── com │ │ └── zsoltfabok │ │ └── blog │ │ ├── Munger.java │ │ ├── SentenceHelper.java │ │ ├── SimpleTextMunger.java │ │ └── web │ │ └── controller │ │ └── MungerController.java ├── resources │ └── applicationContext.xml └── webapp │ └── WEB-INF │ ├── munger-servlet.xml │ ├── views │ └── index.jsp │ └── web.xml └── test ├── java └── com │ └── zsoltfabok │ └── blog │ ├── EmbeddedTomcat.java │ ├── SimpleTextMungerStepsdef.java │ ├── SimpleTextMunger_Test.java │ ├── WebTextMungerStepsdef.java │ ├── WebTextMunger_Test.java │ └── spring │ └── SimpleTextMungerTest.java └── resources ├── cucumber.xml ├── log4j.properties ├── simple_text_munger.feature └── web_text_munger.feature /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | >>>>>>> episode_3 11 | >>>>>>> episode_4 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | bin 3 | *.class 4 | .settings 5 | TEST-* 6 | TESTS-* 7 | build.properties 8 | target 9 | work 10 | tomcat.8080 11 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | blog.cucumberjvm 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | org.apache.ivyde.eclipse.ivynature 17 | 18 | 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | script: ant clean test 3 | # whitelist 4 | branches: 5 | only: 6 | - master 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains the java code I created while I was writing my posts about cucumber-jvm. Each post has its different branch: 2 | 3 | - **http://www.zsoltfabok.com/blog/2011/12/cucumber-jvm-preparation/** ([episode_1](https://github.com/ZsoltFabok/cucumber-jvm-post/tree/episode_1)): it is about the basics of BDD and cucumber and how to set up an eclipse project for executing cucumber features from java 4 | - **http://www.zsoltfabok.com/blog/2011/12/cucumber-jvm-more-scenarios/** ([episode_2](https://github.com/ZsoltFabok/cucumber-jvm-post/tree/episode_2)): the series continues with *scenarios* and with a couple of ideas on how to use them 5 | - **http://www.zsoltfabok.com/blog/2012/01/cucumber-jvm-di/** ([episode_3](https://github.com/ZsoltFabok/cucumber-jvm-post/tree/episode_3)): in this episode I'm introducing *Spring* and dependency injection 6 | - **http://zsoltfabok.com/blog/2012/03/cucumber-jvm-mocking/** ([episode_4](https://github.com/ZsoltFabok/cucumber-jvm-post/tree/episode_4)): there is testing without mocking. This episode shows how to use *mockito* with *cucumber-jvm* 7 | - **http://zsoltfabok.com/blog/2012/09/cucumber-jvm-web-with-spring-mvc/** ([episode_5](https://github.com/ZsoltFabok/cucumber-jvm-post/tree/episode_5)): a huge step forward: introducing MVC and full-stack testing 8 | - **http://zsoltfabok.com/blog/2012/09/cucumber-jvm-hooks/** ([episode_6](https://github.com/ZsoltFabok/cucumber-jvm-post/tree/episode_6)): I'm using hooks for the sake of cleaner test code 9 | 10 | ### Building, configuring and running 11 | 12 | I'm using [ivy](http://ant.apache.org/ivy/) for managing the project dependencies, so after cloning the repository you have to run ivy: 13 | ```bash 14 | % ivy 15 | ``` 16 | and install the [ivyDE plugin](http://ant.apache.org/ivy/ivyde/index.html). 17 | 18 | Building the project: 19 | ```bash 20 | % ant compile 21 | ``` 22 | 23 | You may have a different location for `ivy` therefore the location of the `ivy.jar` is configurable via the `build.properties` file. Use the example `build.properties_example` for setting it properly. 24 | 25 | Running the test cases: 26 | ```bash 27 | % ant test 28 | ``` 29 | 30 | Cleaning up: 31 | ```bash 32 | % ant clean 33 | ``` -------------------------------------------------------------------------------- /build.properties_sample: -------------------------------------------------------------------------------- 1 | ivy.jar = /home/user/downloads/apache-ivy-2.3.0-rc1/ivy-2.3.0-rc1.jar 2 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /ivy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/com/zsoltfabok/blog/Munger.java: -------------------------------------------------------------------------------- 1 | package com.zsoltfabok.blog; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class Munger { 7 | 8 | public String munge(String word) { 9 | if (word.length() < 3) { 10 | return word; 11 | } else { 12 | StringBuilder temp = new StringBuilder(word); 13 | temp = temp.reverse(); 14 | return switchFirstAndLastCharacters(temp).toString(); 15 | } 16 | } 17 | 18 | private StringBuilder switchFirstAndLastCharacters(StringBuilder word) { 19 | char lastCharacter = word.charAt(word.length() - 1); 20 | word.setCharAt(word.length() - 1, word.charAt(0)); 21 | word.setCharAt(0, lastCharacter); 22 | return word; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/zsoltfabok/blog/SentenceHelper.java: -------------------------------------------------------------------------------- 1 | package com.zsoltfabok.blog; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class SentenceHelper { 10 | 11 | private static final String SEPARATOR = " "; 12 | 13 | public List split(String sentence) { 14 | List words = new ArrayList(); 15 | for (String word : sentence.split(SEPARATOR)) { 16 | words.add(word); 17 | } 18 | return words; 19 | } 20 | 21 | public String join(List words) { 22 | StringBuilder sentence = new StringBuilder(); 23 | for (String word : words) { 24 | sentence.append(word).append(SEPARATOR); 25 | } 26 | return sentence.toString().trim(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/zsoltfabok/blog/SimpleTextMunger.java: -------------------------------------------------------------------------------- 1 | package com.zsoltfabok.blog; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class SimpleTextMunger { 10 | 11 | private final SentenceHelper sentenceHelper; 12 | private final Munger munger; 13 | 14 | @Autowired 15 | public SimpleTextMunger(SentenceHelper sentenceHelper, Munger munger) { 16 | this.sentenceHelper = sentenceHelper; 17 | this.munger = munger; 18 | } 19 | 20 | public String execute(String sentence) { 21 | List words = sentenceHelper.split(sentence); 22 | for (int i = 0; i < words.size(); i++) { 23 | words.set(i, munger.munge(words.get(i))); 24 | } 25 | return sentenceHelper.join(words); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/zsoltfabok/blog/web/controller/MungerController.java: -------------------------------------------------------------------------------- 1 | package com.zsoltfabok.blog.web.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.ui.Model; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RequestMethod; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | 10 | import com.zsoltfabok.blog.SimpleTextMunger; 11 | 12 | @Controller 13 | public class MungerController { 14 | 15 | @Autowired 16 | private SimpleTextMunger munger; 17 | 18 | @RequestMapping("/") 19 | public String show() { 20 | return "index"; 21 | } 22 | 23 | @RequestMapping(value = "/munge", method=RequestMethod.POST) 24 | public String munge(@RequestParam("text") String text, Model model) { 25 | model.addAttribute("munged", munger.execute(text)); 26 | model.addAttribute("original", text); 27 | return "index"; 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/resources/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/munger-servlet.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | /WEB-INF/views/ 21 | 22 | 23 | .jsp 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/views/index.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 2 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> 3 | <%@ page isELIgnored="false" %> 4 | 5 | 6 | App 7 | 8 | 9 |
10 | your text: 11 | 12 | 13 |
14 | 15 | munged: ${munged} (${original}) 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | munger 8 | org.springframework.web.servlet.DispatcherServlet 9 | 1 10 | 11 | 12 | 13 | munger 14 | / 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/test/java/com/zsoltfabok/blog/EmbeddedTomcat.java: -------------------------------------------------------------------------------- 1 | package com.zsoltfabok.blog; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import org.apache.catalina.LifecycleException; 7 | import org.apache.catalina.startup.Tomcat; 8 | import org.apache.commons.io.FileUtils; 9 | 10 | public class EmbeddedTomcat { 11 | private Tomcat tomcat; 12 | 13 | public void start() { 14 | try { 15 | tomcat = new Tomcat(); 16 | // If I don't want to copy files around then the base directory must be '.' 17 | String baseDir = "."; 18 | tomcat.setPort(8090); 19 | tomcat.setBaseDir(baseDir); 20 | tomcat.getHost().setAppBase(baseDir); 21 | tomcat.getHost().setDeployOnStartup(true); 22 | tomcat.getHost().setAutoDeploy(true); 23 | tomcat.start(); 24 | } catch (LifecycleException e) { 25 | throw new RuntimeException(e); 26 | } 27 | } 28 | 29 | public void stop() { 30 | try { 31 | tomcat.stop(); 32 | tomcat.destroy(); 33 | // Tomcat creates a work folder where the temporary files are stored 34 | FileUtils.deleteDirectory(new File("work")); 35 | FileUtils.deleteDirectory(new File("tomcat.8080")); 36 | } catch (LifecycleException e) { 37 | throw new RuntimeException(e); 38 | } catch (IOException e) { 39 | throw new RuntimeException(e); 40 | } 41 | } 42 | 43 | public void deploy(String appName) { 44 | tomcat.addWebapp(tomcat.getHost(), "/" + appName, "src/main/webapp"); 45 | } 46 | 47 | public String getApplicationUrl(String appName) { 48 | return String.format("http://%s:%d/%s", tomcat.getHost().getName(), 49 | tomcat.getConnector().getLocalPort(), appName); 50 | } 51 | 52 | public boolean isRunning() { 53 | return tomcat != null; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/zsoltfabok/blog/SimpleTextMungerStepsdef.java: -------------------------------------------------------------------------------- 1 | package com.zsoltfabok.blog; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.mockito.Mockito.mock; 5 | import static org.mockito.Mockito.when; 6 | import cucumber.annotation.en.Given; 7 | import cucumber.annotation.en.Then; 8 | import cucumber.annotation.en.When; 9 | 10 | public class SimpleTextMungerStepsdef { 11 | 12 | private SimpleTextMunger simpleTextMunger; 13 | private Munger munger; 14 | private SentenceHelper sentenceHelper; 15 | private String result; 16 | private String inputWord; 17 | private String mungedWord; 18 | 19 | @Given("^I have a mocked munger which always returns \"([^\"]*)\" for \"([^\"]*)\"$") 20 | public void I_have_a_mocked_munger_which_always_returns_(String munged, String word) { 21 | munger = mock(Munger.class); 22 | inputWord = word; 23 | mungedWord = munged; 24 | } 25 | 26 | @Given("^I have an instance of my class$") 27 | public void I_have_an_instance_of_my_class() { 28 | sentenceHelper = new SentenceHelper(); 29 | simpleTextMunger = new SimpleTextMunger(sentenceHelper, munger); 30 | } 31 | 32 | 33 | @When("^I call my method with \"([^\"]*)\"$") 34 | public void I_call_my_method_with_(String word) { 35 | when(munger.munge(inputWord)).thenReturn(mungedWord); 36 | result = simpleTextMunger.execute(word); 37 | } 38 | 39 | @Then("^I receive \"([^\"]*)\"$") 40 | public void I_receive_(String expectation) { 41 | assertEquals(expectation, result); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/zsoltfabok/blog/SimpleTextMunger_Test.java: -------------------------------------------------------------------------------- 1 | package com.zsoltfabok.blog; 2 | 3 | import org.junit.runner.RunWith; 4 | import cucumber.junit.Cucumber; 5 | 6 | @RunWith(Cucumber.class) 7 | @Cucumber.Options(features="classpath:simple_text_munger.feature") 8 | public class SimpleTextMunger_Test { 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/com/zsoltfabok/blog/WebTextMungerStepsdef.java: -------------------------------------------------------------------------------- 1 | package com.zsoltfabok.blog; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.openqa.selenium.By; 6 | import org.openqa.selenium.WebDriver; 7 | import org.openqa.selenium.firefox.FirefoxDriver; 8 | 9 | import cucumber.annotation.Before; 10 | import cucumber.annotation.en.Given; 11 | import cucumber.annotation.en.Then; 12 | import cucumber.annotation.en.When; 13 | 14 | public class WebTextMungerStepsdef { 15 | 16 | private static EmbeddedTomcat tomcat = new EmbeddedTomcat(); 17 | private static WebDriver browser; 18 | 19 | @Before("@web") 20 | public void beforeScenario() { 21 | if (!tomcat.isRunning()) { 22 | tomcat.start(); 23 | tomcat.deploy("munger"); 24 | browser = new FirefoxDriver(); 25 | 26 | Runtime.getRuntime().addShutdownHook(new Thread() { 27 | public void run() { 28 | browser.close(); 29 | tomcat.stop(); 30 | } 31 | }); 32 | } 33 | } 34 | 35 | @Given("^I am on the home page") 36 | public void I_am_on_the_home_page() { 37 | browser.get(tomcat.getApplicationUrl("munger")); 38 | } 39 | 40 | @When("^I enter \"([^\"]*)\"$") 41 | public void I_enter_(String text) { 42 | browser.findElement(By.id("text")).sendKeys(text); 43 | } 44 | 45 | @When("^I press \"([^\"]*)\"$") 46 | public void I_press_(String buttonId) { 47 | browser.findElement(By.id(buttonId)).click(); 48 | } 49 | 50 | @Then("^I see \"([^\"]*)\" as the munged text$") 51 | public void I_see_as_the_munged_text(String text) { 52 | assertEquals(text, browser.findElement(By.id("munged")).getText()); 53 | } 54 | 55 | @Then("^I see \"([^\"]*)\" as the original$") 56 | public void I_see_as_the_original(String text) { 57 | assertEquals("(" + text + ")", browser.findElement(By.id("original")).getText()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/com/zsoltfabok/blog/WebTextMunger_Test.java: -------------------------------------------------------------------------------- 1 | package com.zsoltfabok.blog; 2 | 3 | import org.junit.runner.RunWith; 4 | 5 | import cucumber.junit.Cucumber; 6 | 7 | @RunWith(Cucumber.class) 8 | @Cucumber.Options(features="classpath:web_text_munger.feature") 9 | public class WebTextMunger_Test { 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/com/zsoltfabok/blog/spring/SimpleTextMungerTest.java: -------------------------------------------------------------------------------- 1 | package com.zsoltfabok.blog.spring; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.test.context.ContextConfiguration; 9 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 | 11 | import com.zsoltfabok.blog.SimpleTextMunger; 12 | 13 | @RunWith(SpringJUnit4ClassRunner.class) 14 | @ContextConfiguration(locations = { "/applicationContext.xml" }) 15 | public class SimpleTextMungerTest { 16 | @Autowired 17 | private SimpleTextMunger simpleTextMunger; 18 | 19 | @Test 20 | public void shouldMungeASimpleWord() { 21 | assertEquals("wrod", simpleTextMunger.execute("word")); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/cucumber.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=WARN, stdout 3 | 4 | # Direct log messages to stdout 5 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 6 | log4j.appender.stdout.Target=System.out 7 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 8 | log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n -------------------------------------------------------------------------------- /src/test/resources/simple_text_munger.feature: -------------------------------------------------------------------------------- 1 | Feature: simple text munger kata 2 | 3 | Scenario: It should process a sentence 4 | Given I have a mocked munger which always returns "folw" for "flow" 5 | And I have an instance of my class 6 | When I call my method with "flow flow" 7 | Then I receive "folw folw" -------------------------------------------------------------------------------- /src/test/resources/web_text_munger.feature: -------------------------------------------------------------------------------- 1 | Feature: web text munger kata 2 | 3 | @web 4 | Scenario: It should process a sentence 5 | Given I am on the home page 6 | When I enter "flow flow" 7 | And I press "submit" 8 | Then I see "folw folw" as the munged text 9 | And I see "flow flow" as the original 10 | --------------------------------------------------------------------------------