├── .gitignore ├── README.md ├── bdd-helloworld ├── pom.xml └── src │ └── test │ ├── java │ └── com │ │ └── danidemi │ │ └── tdd │ │ └── bdd │ │ ├── IncreaseStepsFactory.java │ │ └── IncreaseStoryLiveTest.java │ └── resources │ └── increase.story ├── coverage ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── danidemi │ │ └── tutorial │ │ └── tdd │ │ └── coverage │ │ ├── Illusion.java │ │ └── RunIllusion.java │ └── test │ └── java │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── coverage │ └── IllusionTest.java ├── integration-jee-arquillan ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── danidemi │ │ └── tddclass │ │ └── arquillan │ │ ├── Greeter.java │ │ ├── Master.java │ │ └── Slave.java │ └── test │ └── java │ └── com │ └── danidemi │ └── tddclass │ └── arquillan │ ├── GreeterTest.java │ └── MasterSlaveTest.java ├── integration-jee-open-ejb ├── .gitignore ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── danidemi │ │ │ └── tddclass │ │ │ ├── Movie.java │ │ │ └── Movies.java │ └── resources │ │ └── persistence.xml │ └── test │ └── java │ └── com │ └── danidemi │ └── tddclass │ └── MoviesTest.java ├── integration-web-app ├── .gitignore ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── danidemi │ │ │ └── tutorial │ │ │ └── tdd │ │ │ └── web │ │ │ ├── controller │ │ │ ├── DeleteActorServlet.java │ │ │ ├── FlashMessageFilter.java │ │ │ ├── GetActorsListServlet.java │ │ │ └── NewActorServlet.java │ │ │ ├── init │ │ │ ├── Initializer.java │ │ │ ├── InitializerListener.java │ │ │ └── MemoryInitializer.java │ │ │ ├── model │ │ │ ├── Actor.java │ │ │ ├── ActorDao.java │ │ │ └── MemoryActorDao.java │ │ │ └── view │ │ │ └── FlashMessage.java │ └── webapp │ │ ├── WEB-INF │ │ └── web.xml │ │ └── actors.jsp │ └── test │ └── java │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── web │ ├── controller │ ├── ActorsServletFailingTest.java │ ├── ActorsServletSuccessTest.java │ ├── FlashMessageFilterTest.java │ └── NewActorServletTest.java │ ├── model │ └── ActorTest.java │ └── view │ └── FlashMessageTest.java ├── integration-web-htmlunit ├── .gitignore ├── pom.xml └── src │ └── test │ └── java │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── htmlunit │ └── WebPageTest.java ├── integration-web-selenium-ide ├── .gitignore ├── pom.xml └── src │ └── main │ └── resources │ ├── AddNewActor │ ├── ClickAround │ ├── DontAddActorWithSameName │ └── Suite ├── integration-web-selenium-webdriver ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── tdd │ │ └── selenium │ │ └── webdriver │ │ └── Test1.java │ └── test │ └── java │ └── tdd │ └── selenium │ └── webdriver │ ├── WebDriver.java │ └── WebDriverBacked.java ├── pom.xml ├── showcase-dbunit ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── danidemi │ │ └── tutorial │ │ └── tdd │ │ └── showcase │ │ └── dbunit │ │ └── MovieDao.java │ └── test │ ├── java │ └── com │ │ └── danidemi │ │ └── tutorial │ │ └── tdd │ │ └── showcase │ │ └── dbunit │ │ └── DbTest.java │ └── resources │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── showcase │ └── dbunit │ ├── CreateDb.sql │ ├── DbTest.doNotInsertAMovieWithUnknownPublisher.xml │ ├── DbTest.insertMovie.xml │ ├── DbTest.verifyDataSet.expected.xml │ └── DbTest.verifyDataSet.initial.xml ├── showcase-hamcrest ├── .gitignore ├── pom.xml └── src │ └── test │ └── java │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── showcase │ └── hamcrest │ ├── InvoiceIsPayed.java │ ├── InvoiceMatchers.java │ ├── InvoiceTest.java │ └── RedefineToString.java ├── showcase-junit ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── danidemi │ │ └── tutorial │ │ └── tdd │ │ └── showcase │ │ └── junit │ │ ├── parameterized │ │ ├── NotATriangleException.java │ │ └── Triangle.java │ │ └── theories │ │ ├── Device.java │ │ ├── Radio.java │ │ └── TV.java │ └── test │ └── java │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── showcase │ └── junit │ ├── exceptions │ └── TestExceptions.java │ ├── former │ └── FormerAssertions.java │ ├── matchers │ ├── HamcrestShowcaseOnBasics.java │ ├── HamcrestShowcaseOnCollectionsAndArrays.java │ ├── HamcrestShowcaseOnCompositions.java │ ├── HamcrestShowcaseOnMaps.java │ ├── HamcrestShowcaseOnNumbers.java │ └── HamcrestShowcaseOnStrings.java │ ├── parameterized │ ├── NotATriangleTest.java │ ├── PlusOperatorParametrizedTest.java │ └── TriangleTest.java │ ├── rules │ ├── ExceptionRuleTest.java │ ├── MyOwnRuleTest.java │ ├── TemporaryFolderRuleTest.java │ ├── TestNameRuleTest.java │ └── TimeoutRuleTest.java │ └── theories │ ├── PlusOperatorTheoryTest.java │ └── TheoryTest.java ├── showcase-main ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── showcase │ ├── accounting │ ├── Invoice.java │ ├── InvoiceStatus.java │ ├── PayedStatus.java │ └── WaitingPayementStatus.java │ └── authentication │ ├── DatabaseException.java │ ├── LoginException.java │ ├── Repository.java │ ├── Service.java │ └── User.java ├── showcase-mockito ├── .gitignore ├── pom.xml └── src │ └── test │ └── java │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── showcase │ └── mockito │ ├── ServiceTest.java │ ├── StubbingShowcase.java │ └── VerifyShowcase.java ├── showcase-powermock ├── .gitignore ├── pom.xml └── src │ └── test │ └── java │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── showcase │ └── powermock │ ├── Constructors.java │ ├── PrivateMethods.java │ ├── StaticMethods.java │ └── StatusTest.java ├── tdd-darts ├── .gitignore ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── danidemi │ │ └── tutorial │ │ └── tdd │ │ └── darts │ │ └── Darts.java │ └── test │ └── java │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── darts │ └── DartsTest.java ├── tdd-fill-the-code-starting-from-test ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── danidemi │ │ └── tutorial │ │ └── tdd │ │ └── calc │ │ └── Calc.java │ └── test │ └── java │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── calc │ └── CalcTest.java ├── tdd-helloworld ├── .gitignore ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── danidemi │ │ └── tutorial │ │ └── tdd │ │ └── helloworld │ │ └── Greeter.java │ └── test │ └── java │ └── com │ └── danidemi │ └── tutorial │ └── tdd │ └── helloworld │ └── GreeterTest.java ├── tdd-mocks ├── .gitignore ├── README.md ├── build.xml ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── danidemi │ │ └── tutorial │ │ └── tdd │ │ └── mocks │ │ ├── AbstractDao.java │ │ ├── InvalidCredentialException.java │ │ ├── JdbcConf.java │ │ ├── Login.java │ │ ├── SqlSandwich.java │ │ ├── TokenService.java │ │ ├── User.java │ │ ├── UserDao.java │ │ ├── UserDaoImpl.java │ │ ├── UserImpl.java │ │ └── UserLockedException.java │ └── test │ ├── java │ └── com │ │ └── danidemi │ │ └── tutorial │ │ └── tdd │ │ └── mocks │ │ ├── LoginTest_1.java │ │ ├── LoginTest_2.java │ │ └── UserDaoDbTest.java │ └── resources │ ├── UserDaoDbTest.xml │ ├── data.xml │ ├── database.sql │ └── tdddb.sql ├── tdd-refactoring-done ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── danidemi │ │ └── tdd │ │ └── refactoringdone │ │ ├── ChildrenStatus.java │ │ ├── Customer.java │ │ ├── DomainObject.java │ │ ├── JustReleasedStatus.java │ │ ├── Movie.java │ │ ├── MovieStatus.java │ │ ├── Registrar.java │ │ ├── RegularStatus.java │ │ ├── Render.java │ │ ├── Rental.java │ │ └── Tape.java │ └── test │ └── java │ └── com │ └── danidemi │ └── tdd │ └── refactoringdone │ ├── CustomerTest.java │ └── MovieTest.java └── tdd-refactoring ├── .gitignore ├── README ├── pom.xml └── src ├── main └── java │ └── com │ └── danidemi │ └── tdd │ └── refactoring │ ├── Customer.java │ ├── DomainObject.java │ ├── Movie.java │ ├── Registrar.java │ ├── Rental.java │ └── Tape.java └── test └── java └── com └── danidemi └── tdd └── refactoring └── CustomerTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /.project 3 | *.iml 4 | .idea 5 | **/pom.xml.versionsBackup 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Source code for the Test Driven Development class. 2 | 3 | # TDD Core Concepts 4 | 5 | * tdd-helloworld: What is this TDD about ? 6 | 7 | * tdd-fill-the-code-starting-from-test: just the tests, write the ocde to make them pass. 8 | 9 | * tdd-darts: A first example about developing a library without dependencies using Unit Tests. 10 | 11 | * tdd-mocks: An example about using mocks with the usual DAO pattern and DbUnit. 12 | 13 | * tdd-refactoring: An ugly piece of code fully covered, just wait to be refactored. 14 | 15 | * tdd-refactoring-done: The tdd-refactoring refactored. 16 | 17 | # TDD Coverage 18 | 19 | * coverage: Shows how 100% coverage does not mean software is correct. 20 | 21 | # TDD Libraries 22 | 23 | * showcase-hamcrest: Things you can do with Hamcrest matchers in JUnit. 24 | 25 | * showcase-junit: Things you can do with JUnit. 26 | 27 | * showcase-mockito: Things you can do with Mockito. 28 | 29 | * showcase-powermock: Things you can do with PowerMock and Mockito. 30 | 31 | * showcase-dbunit: Things you can do with DbUnit and Mockito. 32 | 33 | * showcase-main: some domain classes shared among different examples. 34 | 35 | # Integration tests 36 | 37 | * integration-web-app: sample web app. 38 | 39 | * integration-web-htmlunit: example of automatic testing with HtmlUnit. 40 | 41 | * integration-web-selenium-ide: example of automatic testing with SeleniumIde. 42 | 43 | * integration-web-selenium-webdriver: example of automatic testing with WebDriver. 44 | 45 | * integration-jee-arquillan: JEE tests with Arquillan. 46 | 47 | * integration-jee-open-ejb: JEE tests with Open EJB. 48 | -------------------------------------------------------------------------------- /bdd-helloworld/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | tdd-parent 7 | com.danidemi.tutorial.tdd 8 | 0.0.2-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | bdd-helloworld 13 | 14 | 15 | 16 | org.jbehave 17 | jbehave-core 18 | 19 | 20 | junit 21 | junit 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /bdd-helloworld/src/test/java/com/danidemi/tdd/bdd/IncreaseStepsFactory.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.bdd; 2 | 3 | import org.jbehave.core.annotations.Given; 4 | import org.jbehave.core.annotations.Then; 5 | import org.jbehave.core.annotations.When; 6 | 7 | import java.util.Random; 8 | 9 | import static org.junit.Assert.assertTrue; 10 | 11 | public class IncreaseStepsFactory { 12 | private int counter; 13 | private int previousValue; 14 | 15 | @Given("a counter") 16 | public void aCounter() { 17 | } 18 | 19 | @Given("the counter has any integral value") 20 | public void counterHasAnyIntegralValue() { 21 | counter = new Random().nextInt(); 22 | previousValue = counter; 23 | } 24 | 25 | @When("the user increases the counter") 26 | public void increasesTheCounter() { 27 | counter++; 28 | } 29 | 30 | @Then("the value of the counter must be 1 greater than previous value") 31 | public void theValueOfTheCounterMustBe1Greater() { 32 | assertTrue(1 == counter - previousValue); 33 | } 34 | } -------------------------------------------------------------------------------- /bdd-helloworld/src/test/java/com/danidemi/tdd/bdd/IncreaseStoryLiveTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.bdd; 2 | 3 | import org.jbehave.core.configuration.Configuration; 4 | import org.jbehave.core.configuration.MostUsefulConfiguration; 5 | import org.jbehave.core.failures.FailingUponPendingStep; 6 | import org.jbehave.core.io.LoadFromClasspath; 7 | import org.jbehave.core.junit.JUnitStories; 8 | import org.jbehave.core.reporters.StoryReporterBuilder; 9 | import org.jbehave.core.steps.InjectableStepsFactory; 10 | import org.jbehave.core.steps.InstanceStepsFactory; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | import static org.jbehave.core.io.CodeLocations.codeLocationFromClass; 16 | 17 | public class IncreaseStoryLiveTest extends JUnitStories { 18 | 19 | @Override 20 | public Configuration configuration() { 21 | return new MostUsefulConfiguration() 22 | .useStoryLoader(new LoadFromClasspath(this.getClass())) 23 | .useFailureStrategy(new FailingUponPendingStep()) 24 | .useStoryReporterBuilder(new StoryReporterBuilder() 25 | .withCodeLocation(codeLocationFromClass(this.getClass())) 26 | .withDefaultFormats()); 27 | } 28 | 29 | @Override 30 | public InjectableStepsFactory stepsFactory() { 31 | return new InstanceStepsFactory(configuration(), new IncreaseStepsFactory()); 32 | } 33 | 34 | /** 35 | * .story file path to be parsed by JBehave 36 | */ 37 | @Override 38 | protected List storyPaths() { 39 | return Arrays.asList("increase.story"); 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /bdd-helloworld/src/test/resources/increase.story: -------------------------------------------------------------------------------- 1 | Scenario: when a user increases a counter, its value is increased by 1 2 | 3 | Given a counter 4 | And the counter has any integral value 5 | When the user increases the counter 6 | Then the value of the counter must be 2 greater than previous value -------------------------------------------------------------------------------- /coverage/.gitignore: -------------------------------------------------------------------------------- 1 | /.classpath 2 | /.settings 3 | /.settings/* 4 | /.project 5 | /target 6 | *.iml 7 | 8 | -------------------------------------------------------------------------------- /coverage/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | coverage 6 | jar 7 | 8 | 9 | com.danidemi.tutorial.tdd 10 | tdd-parent 11 | 0.0.2-SNAPSHOT 12 | 13 | 14 | 15 | 16 | 17 | org.jmockit 18 | jmockit-coverage 19 | 1.23 20 | 21 | 22 | org.jmockit 23 | jmockit 24 | 1.23 25 | 26 | 27 | junit 28 | junit 29 | 30 | 31 | org.hamcrest 32 | hamcrest-library 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.pitest 43 | pitest-maven 44 | 1.2.0 45 | 46 | 47 | pitest 48 | 49 | mutationCoverage 50 | 51 | verify 52 | 53 | 54 | 55 | 56 | 67 | 68 | 69 | 70 | maven-surefire-plugin 71 | 72 | 73 | 74 | html 75 | all 76 | loaded 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /coverage/src/main/java/com/danidemi/tutorial/tdd/coverage/Illusion.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.coverage; 2 | 3 | public class Illusion { 4 | 5 | public void illude(int a, int b){ 6 | 7 | if(a < 0){ 8 | a = -a; 9 | }else{ 10 | a = 0; 11 | } 12 | 13 | if(b >= 0){ 14 | b = 2*b; 15 | }else{ 16 | b = b/a; 17 | } 18 | 19 | System.out.println("a=" + a); 20 | System.out.println("b=" + b); 21 | 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /coverage/src/main/java/com/danidemi/tutorial/tdd/coverage/RunIllusion.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.coverage; 2 | 3 | public class RunIllusion { 4 | 5 | public static void main(String[] args) { 6 | new Illusion().illude(3, -2); 7 | } 8 | 9 | } 10 | 11 | 12 | -------------------------------------------------------------------------------- /coverage/src/test/java/com/danidemi/tutorial/tdd/coverage/IllusionTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.coverage; 2 | 3 | import org.junit.Test; 4 | 5 | public class IllusionTest { 6 | 7 | @Test 8 | public void test1() { 9 | new Illusion().illude(2, 3); 10 | } 11 | 12 | @Test 13 | public void test2() { 14 | new Illusion().illude(-2, -3); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /integration-jee-arquillan/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.settings 3 | /.classpath 4 | /.project 5 | *.iml -------------------------------------------------------------------------------- /integration-jee-arquillan/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | 9 | tdd-j2e-arquillan 10 | 11 | 12 | 1.0.0.Final 13 | 1.0.2.Final 14 | 1.0.0.CR7 15 | 1.1.5.Final 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | org.jboss.spec 28 | jboss-javaee-6.0 29 | ${jboss-javaee-6.0.version} 30 | pom 31 | provided 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.jboss.arquillian 39 | arquillian-bom 40 | ${arquillian-bom.version} 41 | provided 42 | pom 43 | 44 | 45 | 46 | 47 | org.jboss.arquillian.junit 48 | arquillian-junit-container 49 | ${arquillian-bom.version} 50 | test 51 | 52 | 53 | 57 | 58 | org.jboss.arquillian.container 59 | arquillian-weld-se-embedded-1.1 60 | ${arquillian-weld-se-embedded-1.1.version} 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.jboss.weld 68 | weld-core 69 | ${weld-core.version} 70 | test 71 | 72 | 73 | 74 | 75 | org.slf4j 76 | slf4j-simple 77 | 78 | 79 | junit 80 | junit 81 | 82 | 83 | org.hamcrest 84 | hamcrest-library 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /integration-jee-arquillan/src/main/java/com/danidemi/tddclass/arquillan/Greeter.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tddclass.arquillan; 2 | 3 | import java.io.PrintStream; 4 | 5 | /** 6 | * A component for creating personal greetings. 7 | */ 8 | public class Greeter { 9 | 10 | public void greet(PrintStream to, String name) { 11 | to.println(createGreeting(name)); 12 | } 13 | 14 | public String createGreeting(String name) { 15 | return "Hello, " + name + "!"; 16 | } 17 | } -------------------------------------------------------------------------------- /integration-jee-arquillan/src/main/java/com/danidemi/tddclass/arquillan/Master.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tddclass.arquillan; 2 | 3 | import javax.inject.Inject; 4 | 5 | public class Master { 6 | 7 | private Slave slave; 8 | 9 | @Inject 10 | public void setSlave(Slave slave) { 11 | this.slave = slave; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return "master" + "->" + slave.toString(); 17 | } 18 | 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /integration-jee-arquillan/src/main/java/com/danidemi/tddclass/arquillan/Slave.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tddclass.arquillan; 2 | 3 | public class Slave { 4 | 5 | @Override 6 | public String toString() { 7 | return "slave"; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /integration-jee-arquillan/src/test/java/com/danidemi/tddclass/arquillan/GreeterTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tddclass.arquillan; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.jboss.arquillian.container.test.api.Deployment; 6 | import org.jboss.arquillian.junit.Arquillian; 7 | import org.jboss.shrinkwrap.api.ShrinkWrap; 8 | import org.jboss.shrinkwrap.api.asset.EmptyAsset; 9 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | /** 15 | * We want to verify that {@link Greeter} class behaves properly when invoked as a 16 | * CDI (Context and Dependency Injection) bean. 17 | */ 18 | // The Arquillian runner looks for a public static method annotated with the @Deployment annotation to retrieve the test archive (i.e., micro-deployment). 19 | // Then some magic happens and each @Test method is run inside the container environment. 20 | @RunWith(Arquillian.class) 21 | public class GreeterTest { 22 | 23 | /** A public static method annotated with @Deployment that returns a ShrinkWrap archive. 24 | * ShrinkWrap is a Java API for Archive Manipulation http://jboss.org/shrinkwrap. 25 | */ 26 | // The main idea is to deploy in an embedddable container only what is really needed for the integration test. 27 | @Deployment 28 | public static JavaArchive createDeployment() { 29 | JavaArchive jar = ShrinkWrap.create(JavaArchive.class).addClass(Greeter.class) 30 | .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); 31 | // this is just to print the content of the jar 32 | System.out.println( jar.toString(true) ); 33 | return jar; 34 | } 35 | 36 | @Inject 37 | // Identifies injectable constructors, methods, and fields. 38 | Greeter greeter; 39 | 40 | @Test 41 | public void should_create_greeting() { 42 | Assert.assertEquals("Hello, Earthling!", 43 | greeter.createGreeting("Earthling")); 44 | greeter.greet(System.out, "Earthling"); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /integration-jee-arquillan/src/test/java/com/danidemi/tddclass/arquillan/MasterSlaveTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tddclass.arquillan; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.jboss.arquillian.container.test.api.Deployment; 6 | import org.jboss.arquillian.junit.Arquillian; 7 | import org.jboss.shrinkwrap.api.ShrinkWrap; 8 | import org.jboss.shrinkwrap.api.asset.EmptyAsset; 9 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import static org.hamcrest.Matchers.*; 13 | import static org.junit.Assert.assertThat; 14 | 15 | @RunWith(Arquillian.class) 16 | public class MasterSlaveTest { 17 | 18 | // Let's embed in the jar just the master and the slave 19 | @Deployment 20 | public static JavaArchive createDeployment() { 21 | JavaArchive jar = ShrinkWrap 22 | .create(JavaArchive.class) 23 | .addClass(Master.class) 24 | .addClass(Slave.class) 25 | .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); 26 | System.out.println( jar.toString(true) ); 27 | return jar; 28 | } 29 | 30 | @Inject 31 | Master master; 32 | 33 | @Test 34 | public void shouldConcatenateMasterWithSlaveDescription() { 35 | assertThat( master.toString(), equalTo("master->slave") ); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /integration-jee-open-ejb/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.settings 3 | /.classpath 4 | /.project 5 | *.iml -------------------------------------------------------------------------------- /integration-jee-open-ejb/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | tdd-j2e-open-ejb 9 | 10 | 4.5.2 11 | 12 | 13 | 14 | org.apache.openejb 15 | openejb-core 16 | ${openejb-core.version} 17 | 18 | 19 | -------------------------------------------------------------------------------- /integration-jee-open-ejb/src/main/java/com/danidemi/tddclass/Movie.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tddclass; 2 | 3 | import javax.persistence.Entity; 4 | 5 | @Entity 6 | public class Movie { 7 | 8 | private String director; 9 | private String title; 10 | private int year; 11 | 12 | public Movie() { 13 | } 14 | 15 | public Movie(String director, String title, int year) { 16 | this.director = director; 17 | this.title = title; 18 | this.year = year; 19 | } 20 | 21 | public String getDirector() { 22 | return director; 23 | } 24 | 25 | public void setDirector(String director) { 26 | this.director = director; 27 | } 28 | 29 | public String getTitle() { 30 | return title; 31 | } 32 | 33 | public void setTitle(String title) { 34 | this.title = title; 35 | } 36 | 37 | public int getYear() { 38 | return year; 39 | } 40 | 41 | public void setYear(int year) { 42 | this.year = year; 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /integration-jee-open-ejb/src/main/java/com/danidemi/tddclass/Movies.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tddclass; 2 | 3 | import javax.ejb.Stateful; 4 | import javax.ejb.TransactionAttribute; 5 | import javax.persistence.EntityManager; 6 | import javax.persistence.PersistenceContext; 7 | import javax.persistence.PersistenceContextType; 8 | import javax.persistence.Query; 9 | import java.util.List; 10 | 11 | import static javax.ejb.TransactionAttributeType.MANDATORY; 12 | 13 | @Stateful 14 | @TransactionAttribute(MANDATORY) 15 | public class Movies { 16 | 17 | @PersistenceContext(unitName = "movie-unit", type = PersistenceContextType.TRANSACTION) 18 | private EntityManager entityManager; 19 | 20 | public void addMovie(Movie movie) throws Exception { 21 | entityManager.persist(movie); 22 | } 23 | 24 | public void deleteMovie(Movie movie) throws Exception { 25 | entityManager.remove(movie); 26 | } 27 | 28 | public List getMovies() throws Exception { 29 | Query query = entityManager.createQuery("SELECT m from Movie as m"); 30 | return query.getResultList(); 31 | } 32 | } -------------------------------------------------------------------------------- /integration-jee-open-ejb/src/main/resources/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | movieDatabase 5 | movieDatabaseUnmanaged 6 | com.danidemi.tddclass.Movie 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /integration-jee-open-ejb/src/test/java/com/danidemi/tddclass/MoviesTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tddclass; 2 | 3 | import java.util.List; 4 | import java.util.Properties; 5 | 6 | import javax.annotation.Resource; 7 | import javax.ejb.EJB; 8 | import javax.persistence.EntityManager; 9 | import javax.persistence.PersistenceContext; 10 | import javax.transaction.UserTransaction; 11 | 12 | import junit.framework.TestCase; 13 | 14 | import org.apache.openejb.jee.EjbJar; 15 | import org.apache.openejb.jee.StatefulBean; 16 | import org.apache.openejb.jee.jpa.unit.PersistenceUnit; 17 | import org.apache.openejb.junit.ApplicationComposer; 18 | import org.apache.openejb.testing.Configuration; 19 | import org.apache.openejb.testing.Module; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | 23 | //The org.apache.openejb.junit.ApplicationComposer is a JUnit test runner. 24 | //It involves no classpath scanning at all. 25 | //If you want something to be in the app, you must build it directly in your testcase. 26 | @RunWith(ApplicationComposer.class) 27 | public class MoviesTest extends TestCase { 28 | 29 | @EJB 30 | private Movies movies; 31 | 32 | @Resource 33 | private UserTransaction userTransaction; 34 | 35 | @PersistenceContext 36 | private EntityManager entityManager; 37 | 38 | //An open EJB annotation that mark a method that define the structure of the app to be deployed. 39 | //Multiple methods can be marked with @Module. This for instance configure persistence. 40 | @Module 41 | public PersistenceUnit persistence() { 42 | PersistenceUnit unit = new PersistenceUnit("movie-unit"); 43 | unit.setJtaDataSource("movieDatabase"); 44 | unit.setNonJtaDataSource("movieDatabaseUnmanaged"); 45 | unit.getClazz().add(Movie.class.getName()); 46 | unit.setProperty("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)"); 47 | return unit; 48 | } 49 | 50 | // This one configures the EJBs 51 | @Module 52 | public EjbJar beans() { 53 | EjbJar ejbJar = new EjbJar("movie-beans"); 54 | ejbJar.addEnterpriseBean(new StatefulBean(Movies.class)); 55 | return ejbJar; 56 | } 57 | 58 | // This one returns the configuration as a Properties. 59 | @Configuration 60 | public Properties config() throws Exception { 61 | Properties p = new Properties(); 62 | p.put("movieDatabase", "new://Resource?type=DataSource"); 63 | p.put("movieDatabase.JdbcDriver", "org.hsqldb.jdbcDriver"); 64 | p.put("movieDatabase.JdbcUrl", "jdbc:hsqldb:mem:moviedb"); 65 | return p; 66 | } 67 | 68 | @Test 69 | public void test00_shouldSaveAndDeleteMovies() throws Exception { 70 | 71 | userTransaction.begin(); 72 | 73 | try { 74 | entityManager.persist(new Movie("Quentin Tarantino", "Reservoir Dogs", 1992)); 75 | entityManager.persist(new Movie("Joel Coen", "Fargo", 1996)); 76 | entityManager.persist(new Movie("Joel Coen", "The Big Lebowski", 1998)); 77 | 78 | List list = movies.getMovies(); 79 | assertEquals("List.size()", 3, list.size()); 80 | 81 | for (Movie movie : list) { 82 | movies.deleteMovie(movie); 83 | } 84 | 85 | assertEquals("Movies.getMovies()", 0, movies.getMovies().size()); 86 | 87 | } finally { 88 | userTransaction.rollback(); 89 | } 90 | } 91 | 92 | @Test 93 | public void test10_shouldSaveMovies() throws Exception { 94 | 95 | userTransaction.begin(); 96 | 97 | try { 98 | 99 | entityManager.persist(new Movie("Quentin Tarantino", "Reservoir Dogs", 1992)); 100 | entityManager.persist(new Movie("Joel Coen", "Fargo", 1996)); 101 | entityManager.persist(new Movie("Joel Coen", "The Big Lebowski", 1998)); 102 | 103 | List list = movies.getMovies(); 104 | assertEquals("List.size()", 3, list.size()); 105 | 106 | } finally { 107 | userTransaction.rollback(); 108 | } 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /integration-web-app/.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings 4 | target 5 | /class 6 | *.iml 7 | -------------------------------------------------------------------------------- /integration-web-app/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | tdd-web-app 5 | war 6 | 7 | 8 | com.danidemi.tutorial.tdd 9 | tdd-parent 10 | 0.0.2-SNAPSHOT 11 | 12 | 13 | 14 | 15 | 16 | 17 | javax.servlet 18 | javax.servlet-api 19 | 3.1-b01 20 | provided 21 | 22 | 23 | commons-lang 24 | commons-lang 25 | 26 | 27 | joda-time 28 | joda-time 29 | 30 | 31 | junit 32 | junit 33 | 34 | 35 | org.mockito 36 | mockito-core 37 | 38 | 39 | org.springframework 40 | spring-test 41 | 42 | 43 | org.hamcrest 44 | hamcrest-library 45 | 46 | 47 | org.powermock 48 | powermock-module-junit4 49 | 50 | 51 | org.powermock 52 | powermock-api-mockito 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /integration-web-app/src/main/java/com/danidemi/tutorial/tdd/web/controller/DeleteActorServlet.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.controller; 2 | 3 | import java.io.IOException; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.List; 7 | 8 | import javax.servlet.RequestDispatcher; 9 | import javax.servlet.ServletException; 10 | import javax.servlet.annotation.WebServlet; 11 | import javax.servlet.http.HttpServlet; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | 15 | import org.apache.commons.lang.StringUtils; 16 | 17 | import com.danidemi.tutorial.tdd.web.init.Initializer; 18 | import com.danidemi.tutorial.tdd.web.init.InitializerListener; 19 | import com.danidemi.tutorial.tdd.web.model.Actor; 20 | import com.danidemi.tutorial.tdd.web.model.ActorDao; 21 | import com.danidemi.tutorial.tdd.web.view.FlashMessage; 22 | 23 | @WebServlet(urlPatterns="/delete") 24 | public class DeleteActorServlet extends HttpServlet { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | private ActorDao actorDao; 29 | 30 | public DeleteActorServlet() { 31 | // TODO Auto-generated constructor stub 32 | } 33 | 34 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 35 | // TODO Auto-generated method stub 36 | } 37 | 38 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 39 | 40 | Long id = Long.parseLong( request.getParameter("id") ); 41 | 42 | Actor deleted = actorDao.deleteById(id); 43 | 44 | 45 | response.sendRedirect( response.encodeRedirectURL(FlashMessageFilter.appendFlash(getServletContext().getContextPath(), FlashMessage.Priority.INFO, "Actor '" + deleted.getFirstName() + " " + deleted.getLastName() + "' removed")) ); 46 | 47 | } 48 | 49 | public void setActorDao(ActorDao mock) { 50 | this.actorDao = mock; 51 | } 52 | 53 | @Override 54 | public void init() throws ServletException { 55 | InitializerListener.getInitializer(getServletContext()).init(this); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /integration-web-app/src/main/java/com/danidemi/tutorial/tdd/web/controller/FlashMessageFilter.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.controller; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.Filter; 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.FilterConfig; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | import javax.servlet.annotation.WebFilter; 12 | 13 | import com.danidemi.tutorial.tdd.web.view.FlashMessage; 14 | import com.danidemi.tutorial.tdd.web.view.FlashMessage.Priority; 15 | 16 | @WebFilter(urlPatterns={"/*"}) 17 | public class FlashMessageFilter implements Filter{ 18 | 19 | public static final String FLASH_ATTRIBUTE = "flash"; 20 | public static final String FLASH_MESSAGE_PARAM = "flash.message"; 21 | public static final String FLASH_PRIORITY_PARAM = "flash.priority"; 22 | 23 | @Override 24 | public void init(FilterConfig filterConfig) throws ServletException { 25 | } 26 | 27 | @Override 28 | public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException { 29 | String parameter = request.getParameter(FLASH_PRIORITY_PARAM); 30 | String parameter2 = request.getParameter(FLASH_MESSAGE_PARAM); 31 | try{ 32 | FlashMessage flashMessage = new FlashMessage(FlashMessage.Priority.valueOf( parameter ), parameter2); 33 | request.setAttribute(FLASH_ATTRIBUTE, flashMessage); 34 | }catch(Exception e){ 35 | // no flash message available 36 | } 37 | chain.doFilter(request, response); 38 | } 39 | 40 | @Override 41 | public void destroy() { 42 | } 43 | 44 | public static String appendFlash(String originalUrl, FlashMessage flash){ 45 | return computeUrl(originalUrl, flash.getPriority(), flash.getMessage()); 46 | } 47 | 48 | public static String computeUrl(String originalUrl, Priority priority, String message) { 49 | String string = originalUrl; 50 | if(string.contains("?")){ 51 | string += "&"; 52 | }else{ 53 | string += "?"; 54 | } 55 | string += FlashMessageFilter.FLASH_PRIORITY_PARAM + "=" + priority + "&" + FlashMessageFilter.FLASH_MESSAGE_PARAM + "=" + message; 56 | return string; 57 | } 58 | 59 | public static String appendFlash(String originalUrl, Priority info, String string) { 60 | return computeUrl(originalUrl, info, string); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /integration-web-app/src/main/java/com/danidemi/tutorial/tdd/web/controller/GetActorsListServlet.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.controller; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | 6 | import javax.servlet.RequestDispatcher; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.annotation.WebServlet; 9 | import javax.servlet.http.HttpServlet; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | 13 | import com.danidemi.tutorial.tdd.web.init.Initializer; 14 | import com.danidemi.tutorial.tdd.web.init.InitializerListener; 15 | import com.danidemi.tutorial.tdd.web.model.Actor; 16 | import com.danidemi.tutorial.tdd.web.model.ActorDao; 17 | 18 | @WebServlet(urlPatterns={"/", "/index,jsp"}) 19 | public class GetActorsListServlet extends HttpServlet { 20 | 21 | private static final long serialVersionUID = 1L; 22 | 23 | private ActorDao actorDao; 24 | 25 | public GetActorsListServlet() { 26 | } 27 | 28 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 29 | 30 | List actors = actorDao.findAll(); 31 | 32 | RequestDispatcher requestDispatcher = getServletContext().getRequestDispatcher("/actors.jsp"); 33 | request.setAttribute("actors", actors); 34 | requestDispatcher.forward(request, response); 35 | } 36 | 37 | public void setActorDao(ActorDao mock) { 38 | this.actorDao = mock; 39 | } 40 | 41 | @Override 42 | public void init() throws ServletException { 43 | InitializerListener.getInitializer(getServletContext()).init(this); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /integration-web-app/src/main/java/com/danidemi/tutorial/tdd/web/controller/NewActorServlet.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.controller; 2 | 3 | import java.io.IOException; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.List; 7 | 8 | import javax.servlet.RequestDispatcher; 9 | import javax.servlet.ServletContext; 10 | import javax.servlet.ServletException; 11 | import javax.servlet.annotation.WebServlet; 12 | import javax.servlet.http.HttpServlet; 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | 16 | import org.apache.commons.lang.StringUtils; 17 | 18 | import com.danidemi.tutorial.tdd.web.init.Initializer; 19 | import com.danidemi.tutorial.tdd.web.init.InitializerListener; 20 | import com.danidemi.tutorial.tdd.web.model.Actor; 21 | import com.danidemi.tutorial.tdd.web.model.ActorDao; 22 | import com.danidemi.tutorial.tdd.web.view.FlashMessage; 23 | import com.danidemi.tutorial.tdd.web.view.FlashMessage.Priority; 24 | 25 | @WebServlet(urlPatterns="/new") 26 | public class NewActorServlet extends HttpServlet { 27 | 28 | private static final long serialVersionUID = 1L; 29 | 30 | private ActorDao actorDao; 31 | 32 | public NewActorServlet() { 33 | } 34 | 35 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 36 | } 37 | 38 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 39 | 40 | String firstName = request.getParameter("firstName"); 41 | String lastName = request.getParameter("lastName"); 42 | String birthDate = request.getParameter("birthDate"); 43 | 44 | if(StringUtils.isBlank( birthDate )){ 45 | throw new ServletException(); 46 | } 47 | if(StringUtils.isBlank( lastName )){ 48 | throw new ServletException(); 49 | } 50 | if(StringUtils.isBlank( firstName )){ 51 | throw new ServletException(); 52 | } 53 | 54 | Actor existingActor = actorDao.findBy(firstName, lastName); 55 | 56 | if(existingActor!=null) { 57 | 58 | ServletContext sc = getServletContext(); 59 | 60 | response.sendRedirect( response.encodeRedirectURL(FlashMessageFilter.appendFlash( 61 | sc.getContextPath(), 62 | FlashMessage.Priority.ERROR, "Actor '" + firstName + " " + lastName + "' already exists.")) ); 63 | 64 | }else{ 65 | 66 | Actor actor = new Actor(); 67 | actor.setFirstName(firstName); 68 | actor.setLastName(lastName); 69 | try { 70 | actor.setBirthDate(new SimpleDateFormat("yyyy-MM-dd").parse(birthDate)); 71 | } catch (ParseException e) { 72 | throw new ServletException(e); 73 | } 74 | actorDao.save(actor); 75 | 76 | response.sendRedirect( 77 | response.encodeRedirectURL( 78 | FlashMessageFilter.appendFlash( 79 | getServletContext().getContextPath(), 80 | FlashMessage.Priority.INFO, 81 | "Actor '" + firstName + " " + lastName + "' added")) 82 | ); 83 | 84 | } 85 | 86 | 87 | } 88 | 89 | 90 | 91 | public void setActorDao(ActorDao mock) { 92 | this.actorDao = mock; 93 | } 94 | 95 | @Override 96 | public void init() throws ServletException { 97 | Initializer initializer = InitializerListener.getInitializer(getServletContext()); 98 | if(initializer!=null) { 99 | initializer.init(this); 100 | } 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /integration-web-app/src/main/java/com/danidemi/tutorial/tdd/web/init/Initializer.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.init; 2 | 3 | import com.danidemi.tutorial.tdd.web.controller.DeleteActorServlet; 4 | import com.danidemi.tutorial.tdd.web.controller.GetActorsListServlet; 5 | import com.danidemi.tutorial.tdd.web.controller.NewActorServlet; 6 | 7 | public interface Initializer { 8 | 9 | void init(GetActorsListServlet getActorsListServlet); 10 | 11 | void init(NewActorServlet newActorServlet); 12 | 13 | void init(DeleteActorServlet deleteActorServlet); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /integration-web-app/src/main/java/com/danidemi/tutorial/tdd/web/init/InitializerListener.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.init; 2 | 3 | import javax.servlet.ServletContext; 4 | import javax.servlet.ServletContextEvent; 5 | import javax.servlet.ServletContextListener; 6 | import javax.servlet.annotation.WebListener; 7 | 8 | @WebListener 9 | public class InitializerListener implements ServletContextListener { 10 | 11 | private static final String INITIALIZER = "initializer"; 12 | 13 | @Override 14 | public void contextInitialized(ServletContextEvent sce) { 15 | sce.getServletContext().setAttribute(INITIALIZER, new MemoryInitializer()); 16 | } 17 | 18 | @Override 19 | public void contextDestroyed(ServletContextEvent sce) { 20 | } 21 | 22 | static public Initializer getInitializer(ServletContext ctx) { 23 | return ctx != null ? (Initializer) ctx.getAttribute(INITIALIZER) : null; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /integration-web-app/src/main/java/com/danidemi/tutorial/tdd/web/init/MemoryInitializer.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.init; 2 | 3 | import com.danidemi.tutorial.tdd.web.controller.DeleteActorServlet; 4 | import com.danidemi.tutorial.tdd.web.controller.GetActorsListServlet; 5 | import com.danidemi.tutorial.tdd.web.controller.NewActorServlet; 6 | import com.danidemi.tutorial.tdd.web.model.MemoryActorDao; 7 | 8 | public class MemoryInitializer implements Initializer { 9 | 10 | private MemoryActorDao memoryDao; 11 | 12 | @Override 13 | public void init(GetActorsListServlet getActorsListServlet) { 14 | getActorsListServlet.setActorDao( getMemoryDao() ); 15 | 16 | } 17 | 18 | private MemoryActorDao getMemoryDao() { 19 | if(memoryDao == null ){ 20 | memoryDao = new MemoryActorDao(); 21 | } 22 | return memoryDao; 23 | } 24 | 25 | @Override 26 | public void init(NewActorServlet newActorServlet) { 27 | newActorServlet.setActorDao( getMemoryDao() ); 28 | } 29 | 30 | @Override 31 | public void init(DeleteActorServlet deleteActorServlet) { 32 | deleteActorServlet.setActorDao( getMemoryDao() ); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /integration-web-app/src/main/java/com/danidemi/tutorial/tdd/web/model/Actor.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.model; 2 | 3 | import java.util.Date; 4 | 5 | import org.joda.time.Interval; 6 | 7 | public class Actor { 8 | 9 | private String firstName; 10 | private String lastName; 11 | private Date birthDate; 12 | private Long id; 13 | 14 | public Actor() { 15 | 16 | } 17 | 18 | public Actor(String firstName, String lastName, Date birthDate) { 19 | super(); 20 | this.firstName = firstName; 21 | this.lastName = lastName; 22 | this.birthDate = birthDate; 23 | } 24 | 25 | public String getFirstName() { 26 | return firstName; 27 | } 28 | 29 | public void setFirstName(String firstName) { 30 | this.firstName = firstName; 31 | } 32 | 33 | public String getLastName() { 34 | return lastName; 35 | } 36 | 37 | public void setLastName(String lastName) { 38 | this.lastName = lastName; 39 | } 40 | 41 | public Date getBirthDate() { 42 | return birthDate; 43 | } 44 | 45 | public void setBirthDate(Date birthDate) { 46 | this.birthDate = birthDate; 47 | } 48 | 49 | public int getAgeInYears(Date currentDate) { 50 | return currentDate.after(birthDate) ? new Interval(birthDate.getTime(), currentDate.getTime()).toPeriod().getYears() : 0; 51 | } 52 | 53 | public Long getId() { 54 | return id; 55 | } 56 | 57 | void setId(Long id) { 58 | this.id = id; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /integration-web-app/src/main/java/com/danidemi/tutorial/tdd/web/model/ActorDao.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.model; 2 | 3 | import java.util.List; 4 | 5 | public interface ActorDao { 6 | 7 | void save(Actor capture); 8 | 9 | Actor findBy(String firstName, String lastName); 10 | 11 | List findAll(); 12 | 13 | Actor deleteById(long actorId); 14 | 15 | Actor findById(Long id); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /integration-web-app/src/main/java/com/danidemi/tutorial/tdd/web/model/MemoryActorDao.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.List; 6 | 7 | public class MemoryActorDao implements ActorDao { 8 | 9 | private static long lastId = 0; 10 | 11 | private List actors; 12 | 13 | public MemoryActorDao() { 14 | actors = new ArrayList(); 15 | save( new Actor("aaa1", "aaa2", new Date()) ); 16 | save( new Actor("bbb1", "bbb2", new Date()) ); 17 | save( new Actor("ccc1", "ccc2", new Date()) ); 18 | } 19 | 20 | public void save(Actor actor) { 21 | actor.setId(++lastId); 22 | actors.add(actor); 23 | } 24 | 25 | public Actor findBy(String firstName, String lastName) { 26 | for (Actor actor : actors) { 27 | if(actor.getFirstName().equals(firstName) && actor.getLastName().equals(lastName)){ 28 | return actor; 29 | } 30 | } 31 | return null; 32 | } 33 | 34 | @Override 35 | public Actor findById(Long id) { 36 | for (Actor actor : actors) { 37 | if(actor.getId() == id){ 38 | return actor; 39 | } 40 | } 41 | return null; 42 | } 43 | 44 | @Override 45 | public Actor deleteById(long actorId){ 46 | Actor findById = findById(actorId); 47 | if(findById!=null){ 48 | actors.remove(findById); 49 | return findById; 50 | }else{ 51 | return null; 52 | } 53 | } 54 | 55 | public List findAll() { 56 | return actors; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /integration-web-app/src/main/java/com/danidemi/tutorial/tdd/web/view/FlashMessage.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.view; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | 5 | import com.danidemi.tutorial.tdd.web.view.FlashMessage.Priority; 6 | 7 | public class FlashMessage { 8 | 9 | public enum Priority { 10 | ERROR, WARNING, INFO 11 | } 12 | 13 | private Priority priority; 14 | private String message; 15 | 16 | public FlashMessage(Priority priority, String message) { 17 | super(); 18 | 19 | if(StringUtils.isBlank(message)){ 20 | throw new IllegalArgumentException("message cannot be blank"); 21 | } 22 | 23 | this.priority = priority; 24 | this.message = message; 25 | } 26 | 27 | public String getPriorityName() { 28 | return priority.toString().toLowerCase(); 29 | } 30 | 31 | public String getMessage() { 32 | return message; 33 | } 34 | 35 | public Priority getPriority() { 36 | return priority; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /integration-web-app/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | tdd.web 7 | 8 | index.html 9 | index.htm 10 | index.jsp 11 | default.html 12 | default.htm 13 | default.jsp 14 | 15 | -------------------------------------------------------------------------------- /integration-web-app/src/main/webapp/actors.jsp: -------------------------------------------------------------------------------- 1 | <%@page import="com.danidemi.tutorial.tdd.web.controller.FlashMessageFilter"%> 2 | <%@page import="com.danidemi.tutorial.tdd.web.view.FlashMessage"%> 3 | <%@page import="javax.swing.text.StyledEditorKit.ForegroundAction"%> 4 | <%@ page contentType="text/html; charset=UTF-8" import="java.util.*,com.danidemi.tutorial.tdd.web.model.*" %> 5 | 6 | <% 7 | List actors = (List)request.getAttribute("actors"); 8 | FlashMessage flash = (FlashMessage)request.getAttribute(FlashMessageFilter.FLASH_ATTRIBUTE); 9 | 10 | %> 11 | 12 | 13 | 14 | 20 | 21 | Actor List 22 | 23 | 82 | 83 | 84 | 85 | 86 | 87 | Actor List 88 | 89 | 90 | <%if(flash!=null) {%> 91 | 92 | <%= flash.getMessage() %> 93 | 94 | [X] 95 | 96 | 97 | <%}%> 98 | 99 | <%if(actors!=null && !actors.isEmpty()){ %> 100 | 101 | 102 | 103 | 104 | First Name 105 | Last Name 106 | Birth Date 107 | Age 108 | 109 | 110 | 111 | 112 | 113 | <%for(Actor actor: actors){%> 114 | 115 | <%= actor.getFirstName() %> 116 | <%= actor.getLastName() %> 117 | <%= actor.getBirthDate() %> 118 | <%= actor.getAgeInYears(new Date()) %> 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | <%}%> 127 | 128 | 129 | 130 | 131 | <%}else{%> 132 | Still empty... 133 | <%}%> 134 | 135 | 136 | Add new actor 137 | 138 | First Name 139 | 140 | 141 | Last Name 142 | 143 | 144 | Birth Date 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /integration-web-app/src/test/java/com/danidemi/tutorial/tdd/web/controller/ActorsServletFailingTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.controller; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.junit.Assert.*; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.verify; 7 | 8 | import java.io.IOException; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | import javax.servlet.ServletException; 13 | import javax.servlet.ServletResponse; 14 | 15 | import org.joda.time.DateTime; 16 | import org.joda.time.DateTimeConstants; 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | import org.junit.runner.RunWith; 20 | import org.junit.runners.Parameterized; 21 | import org.junit.runners.Parameterized.Parameters; 22 | import org.mockito.ArgumentCaptor; 23 | import org.mockito.Captor; 24 | import org.mockito.MockitoAnnotations; 25 | import org.mockito.runners.MockitoJUnitRunner; 26 | import org.springframework.mock.web.MockHttpServletRequest; 27 | import org.springframework.mock.web.MockHttpServletResponse; 28 | 29 | import com.danidemi.tutorial.tdd.web.model.Actor; 30 | import com.danidemi.tutorial.tdd.web.model.ActorDao; 31 | 32 | import static org.hamcrest.Matchers.*; 33 | 34 | @RunWith(Parameterized.class) 35 | public class ActorsServletFailingTest { 36 | 37 | @Captor ArgumentCaptor actorArg; 38 | String firstName; 39 | String lastName; 40 | String birthDate; 41 | 42 | 43 | public ActorsServletFailingTest(String firstName, 44 | String lastName, String birthDate) { 45 | super(); 46 | this.firstName = firstName; 47 | this.lastName = lastName; 48 | this.birthDate = birthDate; 49 | } 50 | 51 | @Parameters 52 | public static List params(){ 53 | return Arrays.asList( new Object[][]{ 54 | // nulls 55 | {"Carl", null, "1974-02-23"}, 56 | {null, "Doe", "1974-02-23"}, 57 | {"X", "Y", null}, 58 | // empties 59 | {"", "Ross", "1974-02-23"}, 60 | {"Carl", "", "1974-02-23"}, 61 | {"Carl", "Ross", ""} 62 | }); 63 | } 64 | 65 | @Before 66 | public void setUp(){ 67 | MockitoAnnotations.initMocks(this); 68 | } 69 | 70 | @Test 71 | public void should() 72 | throws ServletException, IOException { 73 | // given 74 | ActorDao actorDao = mock(ActorDao.class); 75 | 76 | NewActorServlet tested = new NewActorServlet(); 77 | tested.setActorDao( actorDao ); 78 | 79 | MockHttpServletRequest req = new MockHttpServletRequest(); 80 | req.setMethod("POST"); 81 | req.setParameter("firstName", firstName); 82 | req.setParameter("lastName", lastName); 83 | req.setParameter("birthDate", birthDate); 84 | 85 | ServletResponse res = new MockHttpServletResponse(); 86 | 87 | // when 88 | Exception ex = null; 89 | try{ 90 | tested.service(req, res); 91 | }catch (Exception e) { 92 | ex = e; 93 | } 94 | 95 | // then 96 | assertThat( ex, instanceOf(ServletException.class)); 97 | 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /integration-web-app/src/test/java/com/danidemi/tutorial/tdd/web/controller/ActorsServletSuccessTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.controller; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.junit.Assert.assertThat; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.verify; 7 | import static org.mockito.Mockito.when; 8 | 9 | import java.io.IOException; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | import javax.servlet.ServletConfig; 14 | import javax.servlet.ServletContext; 15 | import javax.servlet.ServletException; 16 | 17 | import org.joda.time.DateTime; 18 | import org.joda.time.DateTimeConstants; 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.junit.runners.Parameterized; 23 | import org.junit.runners.Parameterized.Parameters; 24 | import org.mockito.ArgumentCaptor; 25 | import org.mockito.Captor; 26 | import org.mockito.MockitoAnnotations; 27 | import org.springframework.mock.web.MockHttpServletRequest; 28 | import org.springframework.mock.web.MockHttpServletResponse; 29 | 30 | import com.danidemi.tutorial.tdd.web.model.Actor; 31 | import com.danidemi.tutorial.tdd.web.model.ActorDao; 32 | 33 | @RunWith(Parameterized.class) 34 | public class ActorsServletSuccessTest { 35 | 36 | @Captor ArgumentCaptor actorArg; 37 | String firstName; 38 | String lastName; 39 | String birthDate; 40 | int expectedAge; 41 | NewActorServlet tested; 42 | 43 | 44 | 45 | public ActorsServletSuccessTest(String firstName, 46 | String lastName, String birthDate, int expectedAge) { 47 | super(); 48 | this.firstName = firstName; 49 | this.lastName = lastName; 50 | this.birthDate = birthDate; 51 | this.expectedAge = expectedAge; 52 | } 53 | 54 | @Parameters 55 | public static List params(){ 56 | return Arrays.asList( new Object[][]{ 57 | {"Carl", "Ross", "1974-02-23", 10}, 58 | {"Jean", "Doe", "1974-02-23", 10}, 59 | {"X", "Y", "2010-02-23", 0} 60 | }); 61 | } 62 | 63 | @Before 64 | public void setUp(){ 65 | MockitoAnnotations.initMocks(this); 66 | } 67 | 68 | @Before 69 | public void setUpServlet() throws ServletException { 70 | tested = new NewActorServlet(); 71 | ServletConfig config = mock(ServletConfig.class); 72 | ServletContext context = mock(ServletContext.class); 73 | when(config.getServletContext()).thenReturn(context); 74 | when(context.getContextPath()).thenReturn("/ctx"); 75 | tested.init( config ); 76 | } 77 | 78 | @Test 79 | public void should() 80 | throws ServletException, IOException { 81 | // given 82 | ActorDao actorDao = mock(ActorDao.class); 83 | 84 | 85 | tested.setActorDao( actorDao ); 86 | 87 | 88 | ServletContext sc; 89 | MockHttpServletRequest req = new MockHttpServletRequest( mock(ServletContext.class) ); 90 | req.setMethod("POST"); 91 | req.setParameter("firstName", firstName); 92 | req.setParameter("lastName", lastName); 93 | req.setParameter("birthDate", birthDate); 94 | 95 | MockHttpServletResponse res = new MockHttpServletResponse(); 96 | 97 | // when 98 | tested.service(req, res); 99 | 100 | // then 101 | verify(actorDao).save(actorArg.capture()); 102 | 103 | assertThat(actorArg.getValue().getFirstName(), equalTo( firstName )); 104 | assertThat(actorArg.getValue().getLastName(), equalTo( lastName )); 105 | assertThat(actorArg.getValue().getAgeInYears( new DateTime(1985, DateTimeConstants.JANUARY, 10, 0, 0).toDate() ), equalTo( expectedAge )); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /integration-web-app/src/test/java/com/danidemi/tutorial/tdd/web/controller/FlashMessageFilterTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.controller; 2 | 3 | import org.junit.Test; 4 | 5 | import com.danidemi.tutorial.tdd.web.view.FlashMessage; 6 | import com.danidemi.tutorial.tdd.web.view.FlashMessage.Priority; 7 | 8 | import static org.hamcrest.Matchers.equalTo; 9 | import static org.junit.Assert.assertThat; 10 | public class FlashMessageFilterTest { 11 | 12 | @Test 13 | public void shouldAppendFlashInfo() { 14 | 15 | // given 16 | String originalUrl = "/url"; 17 | 18 | // when 19 | String urlWithFlash = FlashMessageFilter.appendFlash(originalUrl, new FlashMessage(Priority.INFO, "this is info")); 20 | 21 | // then 22 | assertThat( urlWithFlash, equalTo("/url?flash.priority=INFO&flash.message=this is info") ); 23 | 24 | } 25 | 26 | @Test 27 | public void shouldAppendFlashInfoWithoutQuestionMark() { 28 | 29 | // given 30 | String originalUrl = "/url?param1=hello"; 31 | 32 | // when 33 | String urlWithFlash = FlashMessageFilter.appendFlash(originalUrl, new FlashMessage(Priority.INFO, "this is info")); 34 | 35 | // then 36 | assertThat( urlWithFlash, equalTo("/url?param1=hello&flash.priority=INFO&flash.message=this is info") ); 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /integration-web-app/src/test/java/com/danidemi/tutorial/tdd/web/controller/NewActorServletTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.controller; 2 | 3 | import java.io.IOException; 4 | import java.util.Date; 5 | 6 | import javax.servlet.ServletConfig; 7 | import javax.servlet.ServletContext; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletResponse; 10 | 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.mockito.Mock; 15 | import org.mockito.runners.MockitoJUnitRunner; 16 | import org.springframework.mock.web.MockHttpServletRequest; 17 | import org.springframework.mock.web.MockHttpServletResponse; 18 | 19 | import com.danidemi.tutorial.tdd.web.model.Actor; 20 | import com.danidemi.tutorial.tdd.web.model.ActorDao; 21 | 22 | import static org.mockito.Mockito.*; 23 | import static org.hamcrest.Matchers.equalTo; 24 | import static org.junit.Assert.*; 25 | @RunWith(MockitoJUnitRunner.class) 26 | public class NewActorServletTest { 27 | 28 | @Mock ActorDao actorDao; 29 | @Mock Actor foundActor; 30 | NewActorServlet tested; 31 | ServletContext context; 32 | 33 | 34 | @Before 35 | public void setUpServlet() throws ServletException { 36 | tested = new NewActorServlet(); 37 | ServletConfig config = mock(ServletConfig.class); 38 | context = mock(ServletContext.class); 39 | when(config.getServletContext()).thenReturn(context); 40 | when(context.getContextPath()).thenReturn("/ctx"); 41 | tested.init( config ); 42 | } 43 | 44 | @Test public void shouldNotCreateAnActorCalledTheSameWay() throws IOException { 45 | 46 | // given 47 | tested.setActorDao(actorDao); 48 | when(actorDao.findBy( any(String.class), any(String.class) )).thenReturn( foundActor ); 49 | 50 | MockHttpServletRequest req = new MockHttpServletRequest( context ); 51 | req.setMethod("POST"); 52 | req.setParameter("firstName", "Mark"); 53 | req.setParameter("lastName", "Zuck"); 54 | req.setParameter("birthDate", "2010-12-31"); 55 | 56 | MockHttpServletResponse res = new MockHttpServletResponse( ); 57 | 58 | try{ 59 | // when 60 | tested.service(req, res); 61 | }catch(ServletException se){ 62 | fail(); 63 | } 64 | 65 | // then 66 | 67 | // assertThat( 68 | // sex.getMessage(), 69 | // equalTo("Actor is already present.") ); 70 | 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /integration-web-app/src/test/java/com/danidemi/tutorial/tdd/web/model/ActorTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.model; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.Calendar; 6 | 7 | import org.joda.time.DateTime; 8 | import org.joda.time.DateTimeConstants; 9 | import org.joda.time.JodaTimePermission; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.powermock.modules.junit4.PowerMockRunner; 13 | 14 | import static org.hamcrest.Matchers.*; 15 | 16 | @RunWith(PowerMockRunner.class) 17 | public class ActorTest extends Actor { 18 | 19 | @Test 20 | public void shouldComputeProperAge() { 21 | 22 | // given 23 | Actor tested = new Actor(); 24 | tested.setFirstName("Paul"); 25 | tested.setLastName("Oldman"); 26 | tested.setBirthDate( new DateTime(1940, DateTimeConstants.JANUARY, 12, 0, 0).toDate() ); 27 | 28 | // when 29 | int years = tested.getAgeInYears( new DateTime(1970, DateTimeConstants.JANUARY, 12, 0, 0).toDate() ); 30 | 31 | // then 32 | assertThat( years, equalTo(30) ); 33 | 34 | } 35 | 36 | @Test 37 | public void shouldReturnZeroIfCurrentDateIsBeforeBirthDate() { 38 | 39 | // given 40 | Actor tested = new Actor(); 41 | tested.setFirstName("Paul"); 42 | tested.setLastName("Oldman"); 43 | tested.setBirthDate( new DateTime(2010, DateTimeConstants.JANUARY, 1, 0, 0).toDate() ); 44 | 45 | // when 46 | int years = tested.getAgeInYears( new DateTime(1995, DateTimeConstants.JANUARY, 12, 0, 0).toDate() ); 47 | 48 | // then 49 | assertThat( years, equalTo(0) ); 50 | 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /integration-web-app/src/test/java/com/danidemi/tutorial/tdd/web/view/FlashMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.web.view; 2 | 3 | import org.junit.Test; 4 | 5 | import com.danidemi.tutorial.tdd.web.view.FlashMessage.Priority; 6 | 7 | 8 | public class FlashMessageTest { 9 | 10 | @Test(expected=IllegalArgumentException.class) 11 | public void messageCannotBeNull() { 12 | 13 | // when 14 | new FlashMessage(Priority.INFO, null); 15 | 16 | } 17 | 18 | @Test(expected=IllegalArgumentException.class) 19 | public void messageCannotBeEmpty() { 20 | 21 | // when 22 | new FlashMessage(Priority.WARNING, ""); 23 | 24 | } 25 | 26 | @Test(expected=IllegalArgumentException.class) 27 | public void messageCannotBeBlank() { 28 | 29 | // when 30 | new FlashMessage(Priority.ERROR, " "); 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /integration-web-htmlunit/.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings 4 | target 5 | *.iml -------------------------------------------------------------------------------- /integration-web-htmlunit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | tdd-web-htmlunit 9 | 10 | 11 | 12 | 13 | junit 14 | junit 15 | 16 | 17 | net.sourceforge.htmlunit 18 | htmlunit 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /integration-web-htmlunit/src/test/java/com/danidemi/tutorial/tdd/htmlunit/WebPageTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.htmlunit; 2 | 3 | import java.io.IOException; 4 | import java.net.MalformedURLException; 5 | 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.Ignore; 9 | import org.junit.Test; 10 | 11 | import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; 12 | import com.gargoylesoftware.htmlunit.Page; 13 | import com.gargoylesoftware.htmlunit.WebClient; 14 | import com.gargoylesoftware.htmlunit.html.HtmlForm; 15 | import com.gargoylesoftware.htmlunit.html.HtmlPage; 16 | 17 | public class WebPageTest { 18 | 19 | private WebClient webClient; 20 | 21 | @Before 22 | public void setUp() { 23 | webClient = new WebClient(); 24 | } 25 | 26 | @After 27 | public void tearDown() { 28 | webClient.closeAllWindows(); 29 | } 30 | 31 | @Ignore 32 | @Test 33 | public void test() throws FailingHttpStatusCodeException, MalformedURLException, IOException { 34 | 35 | final HtmlPage page = webClient.getPage("http://localhost:8080"); 36 | 37 | HtmlForm formByName = page.getFormByName("form"); 38 | 39 | 40 | 41 | formByName.getInputByName("firstName").setValueAttribute("Mark"); 42 | formByName.getInputByName("lastName").setValueAttribute("Ross"); 43 | Page nextPage = formByName.getButtonByName("submit").click(); 44 | } 45 | 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /integration-web-selenium-ide/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.settings 3 | **/.settings 4 | /.classpath 5 | /.project 6 | *.iml 7 | -------------------------------------------------------------------------------- /integration-web-selenium-ide/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | tdd-web-selenium-ide 9 | 10 | -------------------------------------------------------------------------------- /integration-web-selenium-ide/src/main/resources/AddNewActor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AddNewActor 8 | 9 | 10 | 11 | 12 | AddNewActor 13 | 14 | 15 | open 16 | /tdd-web-app/?flash.priority=INFO&flash.message=Actor%20%27John%20Back%27%20removed 17 | 18 | 19 | 20 | type 21 | id=firstName 22 | John 23 | 24 | 25 | type 26 | id=lastName 27 | Smith 28 | 29 | 30 | type 31 | id=birthDate 32 | 1965-10-23 33 | 34 | 35 | clickAndWait 36 | css=#form > form > input[type="submit"] 37 | 38 | 39 | 40 | verifyText 41 | css=#flash 42 | glob:*John*Smith* 43 | 44 | 45 | verifyText 46 | css=tbody > tr > td 47 | exact:John 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /integration-web-selenium-ide/src/main/resources/ClickAround: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ClickAround 8 | 9 | 10 | 11 | 12 | ClickAround 13 | 14 | 15 | open 16 | /tdd-web-app/?flash.priority=INFO&flash.message=Actor%20%27John%20Back%27%20added 17 | 18 | 19 | 20 | type 21 | id=firstName 22 | xxx 23 | 24 | 25 | type 26 | id=lastName 27 | xxx 28 | 29 | 30 | type 31 | id=birthDate 32 | xxx 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /integration-web-selenium-ide/src/main/resources/DontAddActorWithSameName: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | DontAddActorWithSameName 8 | 9 | 10 | 11 | 12 | DontAddActorWithSameName 13 | 14 | 15 | open 16 | /tdd-web-app/?flash.priority=INFO&flash.message=Actor%20%27John%20Smith%27%20added 17 | 18 | 19 | 20 | type 21 | id=firstName 22 | Carl 23 | 24 | 25 | type 26 | id=lastName 27 | Reed 28 | 29 | 30 | type 31 | id=birthDate 32 | 1910-10-10 33 | 34 | 35 | clickAndWait 36 | css=#form > form > input[type="submit"] 37 | 38 | 39 | 40 | type 41 | id=firstName 42 | Carl 43 | 44 | 45 | type 46 | id=lastName 47 | Reed 48 | 49 | 50 | type 51 | id=birthDate 52 | 1910-11-24 53 | 54 | 55 | clickAndWait 56 | css=#form > form > input[type="submit"] 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /integration-web-selenium-ide/src/main/resources/Suite: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test Suite 7 | 8 | 9 | 10 | Test Suite 11 | ClickAround 12 | AddNewActor 13 | DontAddActorWithSameName 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /integration-web-selenium-webdriver/.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings 4 | target 5 | *.iml 6 | -------------------------------------------------------------------------------- /integration-web-selenium-webdriver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | tdd-web-selenium-webdriver 8 | 9 | 10 | com.danidemi.tutorial.tdd 11 | tdd-parent 12 | 0.0.2-SNAPSHOT 13 | 14 | 15 | 16 | 17 | 18 | org.seleniumhq.selenium 19 | selenium-java 20 | 2.42.0 21 | 22 | 23 | com.opera 24 | operadriver 25 | 26 | 27 | junit 28 | junit 29 | 30 | 31 | org.hamcrest 32 | hamcrest-library 33 | 34 | 35 | 36 | 37 | 38 | com.opera 39 | operadriver 40 | 1.5 41 | 42 | 43 | org.seleniumhq.selenium 44 | selenium-remote-driver 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /integration-web-selenium-webdriver/src/main/java/tdd/selenium/webdriver/Test1.java: -------------------------------------------------------------------------------- 1 | package tdd.selenium.webdriver; 2 | 3 | import org.openqa.selenium.By; 4 | import org.openqa.selenium.WebElement; 5 | import org.openqa.selenium.WebDriver; 6 | import org.openqa.selenium.firefox.FirefoxDriver; 7 | import org.openqa.selenium.support.ui.ExpectedCondition; 8 | import org.openqa.selenium.support.ui.WebDriverWait; 9 | 10 | 11 | public class Test1 { 12 | public static void main(String[] args) { 13 | // Create a new instance of the Firefox driver 14 | // Notice that the remainder of the code relies on the interface, 15 | // not the implementation. 16 | WebDriver driver = new FirefoxDriver(); 17 | 18 | // And now use this to visit Google 19 | driver.get("http://www.google.com"); 20 | // Alternatively the same thing can be done like this 21 | // driver.navigate().to("http://www.google.com"); 22 | 23 | // Find the text input element by its name 24 | WebElement element = driver.findElement(By.name("q")); 25 | 26 | // Enter something to search for 27 | element.sendKeys("Cheese!"); 28 | 29 | // Now submit the form. WebDriver will find the form for us from the element 30 | element.submit(); 31 | 32 | // Check the title of the page 33 | System.out.println("Page title is: " + driver.getTitle()); 34 | 35 | // Google's search is rendered dynamically with JavaScript. 36 | // Wait for the page to load, timeout after 10 seconds 37 | (new WebDriverWait(driver, 10)).until(new ExpectedCondition() { 38 | public Boolean apply(WebDriver d) { 39 | return d.getTitle().toLowerCase().startsWith("cheese!"); 40 | } 41 | }); 42 | 43 | // Should see: "cheese! - Google Search" 44 | System.out.println("Page title is: " + driver.getTitle()); 45 | 46 | //Close the browser 47 | driver.quit(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /integration-web-selenium-webdriver/src/test/java/tdd/selenium/webdriver/WebDriverBacked.java: -------------------------------------------------------------------------------- 1 | package tdd.selenium.webdriver; 2 | 3 | import java.util.Arrays; 4 | import java.util.Date; 5 | 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.openqa.selenium.WebDriver; 10 | import org.openqa.selenium.firefox.FirefoxDriver; 11 | 12 | import com.thoughtworks.selenium.Selenium; 13 | import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium; 14 | 15 | import static org.hamcrest.Matchers.*; 16 | import static org.junit.Assert.*; 17 | 18 | public class WebDriverBacked { 19 | private Selenium selenium; 20 | private String baseUrl; 21 | 22 | @Before 23 | public void setUp() throws Exception { 24 | WebDriver driver = new FirefoxDriver(); 25 | baseUrl = "http://127.0.1.1:8080/tdd-web-app/"; 26 | selenium = new WebDriverBackedSelenium(driver, baseUrl); 27 | } 28 | 29 | @Test 30 | public void shouldAddNewActor() throws Exception { 31 | 32 | String firstName = "Carl" + new Date().getTime(); 33 | String lastName = "Reed" + new Date().getTime(); 34 | String birthDate = "1910-10-10"; 35 | 36 | 37 | 38 | selenium.open(baseUrl); 39 | 40 | int rowsBefore = selenium.getCssCount("tr").intValue(); 41 | selenium.type("id=firstName", firstName); 42 | selenium.type("id=lastName", lastName); 43 | selenium.type("id=birthDate", birthDate); 44 | selenium.click("css=#form > form > input[type=\"submit\"]"); 45 | selenium.waitForPageToLoad("30000"); 46 | int rowsAfter = selenium.getCssCount("tr").intValue(); 47 | 48 | String flash = selenium.getText("id=flash"); 49 | assertThat(flash, stringContainsInOrder( Arrays.asList( new String[]{firstName, lastName} )) ); 50 | 51 | assertThat( rowsAfter- rowsBefore, equalTo(1)); 52 | 53 | } 54 | 55 | @After 56 | public void tearDown() throws Exception { 57 | selenium.stop(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /showcase-dbunit/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.settings 3 | /.classpath 4 | /.project 5 | *.iml -------------------------------------------------------------------------------- /showcase-dbunit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | showcase-dbunit 9 | 10 | 11 | 12 | org.dbunit 13 | dbunit 14 | test 15 | 16 | 17 | org.hsqldb 18 | hsqldb 19 | 20 | 21 | org.slf4j 22 | slf4j-jdk14 23 | 24 | 25 | org.hamcrest 26 | hamcrest-library 27 | 28 | 29 | org.mockito 30 | mockito-core 31 | 32 | 33 | commons-io 34 | commons-io 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /showcase-dbunit/src/main/java/com/danidemi/tutorial/tdd/showcase/dbunit/MovieDao.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.dbunit; 2 | 3 | import java.sql.Connection; 4 | import java.sql.PreparedStatement; 5 | import java.sql.ResultSet; 6 | import java.sql.SQLException; 7 | 8 | public class MovieDao { 9 | 10 | public MovieDao() { 11 | } 12 | 13 | public void createMovie(Connection conn, String title, int year, String publisher) throws SQLException { 14 | 15 | PreparedStatement ps = conn.prepareStatement("SELECT Id FROM Publisher WHERE name=?"); 16 | ps.setString(1, publisher); 17 | ResultSet rs = ps.executeQuery(); 18 | 19 | Long long1 = null; 20 | boolean publisherIsKnown = rs.next(); 21 | if(publisherIsKnown){ 22 | long1 = rs.getLong("Id"); 23 | rs.close(); 24 | ps.close(); 25 | }else{ 26 | rs.close(); 27 | ps.close(); 28 | throw new IllegalArgumentException("Unknown publisher '" + publisher + "'"); 29 | } 30 | 31 | 32 | ps = conn.prepareStatement("INSERT INTO Movie(Title, Year, Publisher_Id) VALUES (?,?,?)"); 33 | ps.setString(1, title); 34 | ps.setInt(2, year); 35 | ps.setLong(3, long1); 36 | 37 | ps.execute(); 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /showcase-dbunit/src/test/java/com/danidemi/tutorial/tdd/showcase/dbunit/DbTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.dbunit; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.hamcrest.Matchers.notNullValue; 5 | import static org.junit.Assert.*; 6 | import static org.mockito.Matchers.isNotNull; 7 | 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.Reader; 11 | import java.sql.Connection; 12 | import java.sql.DriverManager; 13 | import java.sql.SQLException; 14 | 15 | import org.apache.commons.io.IOUtils; 16 | import org.dbunit.Assertion; 17 | import org.dbunit.JdbcDatabaseTester; 18 | import org.dbunit.database.IDatabaseConnection; 19 | import org.dbunit.dataset.DataSetException; 20 | import org.dbunit.dataset.IDataSet; 21 | import org.dbunit.dataset.ITable; 22 | import org.dbunit.dataset.xml.FlatXmlDataSetBuilder; 23 | import org.dbunit.dataset.xml.XmlDataSet; 24 | import org.dbunit.operation.DatabaseOperation; 25 | import org.junit.Before; 26 | import org.junit.BeforeClass; 27 | import org.junit.Rule; 28 | import org.junit.Test; 29 | import org.junit.rules.TestName; 30 | 31 | public class DbTest { 32 | 33 | static String driverClass = org.hsqldb.jdbcDriver.class.getName(); 34 | static String connectionUrl = "jdbc:hsqldb:mem:aname"; 35 | static String username = "sa"; 36 | static String password = ""; 37 | 38 | JdbcDatabaseTester dbTester; 39 | 40 | @Rule public TestName testName = new TestName(); 41 | 42 | @BeforeClass 43 | public static void setUpDatabase() throws Exception { 44 | Class.forName(org.hsqldb.jdbcDriver.class.getName()); 45 | Connection connection = DriverManager.getConnection(connectionUrl, username, password); 46 | connection.createStatement().executeUpdate( IOUtils.toString( DbTest.class.getResourceAsStream("CreateDb.sql") ) ); 47 | } 48 | 49 | @Before 50 | public void setUp() throws SQLException, IOException, Exception { 51 | dbTester = new JdbcDatabaseTester( driverClass, connectionUrl, username, password); 52 | } 53 | 54 | @Test 55 | public void insertMovie() throws Exception { 56 | 57 | // given 58 | // ...the initial dataset that should be loaded into the db 59 | InputStream initialDataSetResource = getClass().getResourceAsStream( getClass().getSimpleName() + "." + testName.getMethodName() + ".xml" ); 60 | XmlDataSet initialDataSet = new XmlDataSet( initialDataSetResource ); 61 | 62 | // ...the db initialized with the initial dataset 63 | IDatabaseConnection conn = dbTester.getConnection(); 64 | DatabaseOperation.CLEAN_INSERT.execute(conn, initialDataSet); 65 | 66 | // ...a new movie dao 67 | MovieDao tested = new MovieDao(); 68 | 69 | 70 | 71 | // when 72 | // ...a new movie is created 73 | int rowsBefore = conn.getRowCount("Movie"); 74 | tested.createMovie(dbTester.getConnection().getConnection(), "MyMovie", 1999, "MGM" ); 75 | int rowsAfter = conn.getRowCount("Movie"); 76 | 77 | 78 | 79 | // then 80 | // ...one row has to be created in movie 81 | assertThat( rowsAfter - rowsBefore, equalTo(1) ); 82 | 83 | } 84 | 85 | @Test 86 | public void doNotInsertAMovieWithUnknownPublisher() throws Exception { 87 | 88 | // given 89 | // ...the initial dataset that should be loaded into the db 90 | InputStream initialDataSetResource = getClass().getResourceAsStream( getClass().getSimpleName() + "." + testName.getMethodName() + ".xml" ); 91 | XmlDataSet initialDataSet = new XmlDataSet( initialDataSetResource ); 92 | 93 | // ...the db initialized with the initial dataset 94 | IDatabaseConnection conn = dbTester.getConnection(); 95 | DatabaseOperation.CLEAN_INSERT.execute(conn, initialDataSet); 96 | int originalNumberOfMovies = initialDataSet.getTable("Movie").getRowCount(); 97 | 98 | // ...a new movie dao 99 | MovieDao tested = new MovieDao(); 100 | 101 | 102 | 103 | // when 104 | // ...a new movie is created 105 | try{ 106 | tested.createMovie(dbTester.getConnection().getConnection(), "MyMovie", 1999, "Unknow Movies Production" ); 107 | }catch(IllegalArgumentException iae){ 108 | 109 | }catch(Exception e){ 110 | fail(); 111 | } 112 | int moviesCount = conn.getRowCount("Movie"); 113 | 114 | 115 | 116 | // then 117 | // ...one row has to be created in movie 118 | assertThat( moviesCount, equalTo(originalNumberOfMovies) ); 119 | 120 | } 121 | 122 | @Test 123 | public void verifyDataSet() throws Exception { 124 | 125 | // given 126 | // ...the initial dataset that should be loaded into the db 127 | XmlDataSet initialDataSet = loadDataSet("initial"); 128 | 129 | // ...the expected dataset after the operation 130 | XmlDataSet expectedDataSet = loadDataSet("expected"); 131 | 132 | // ...the db initialized with the initial dataset 133 | IDatabaseConnection conn = dbTester.getConnection(); 134 | DatabaseOperation.CLEAN_INSERT.execute(conn, initialDataSet); 135 | 136 | // ...a new movie dao 137 | MovieDao tested = new MovieDao(); 138 | 139 | // when 140 | // ...a new movie is created 141 | tested.createMovie(dbTester.getConnection().getConnection(), "My Movie", 2014, "MGM" ); 142 | 143 | // then 144 | ITable actualMovieTable = conn.createQueryTable("ExpTable", "Select TITLE, YEAR, PUBLISHER_ID from Movie ORDER BY TITLE"); 145 | ITable expectedMovieTable = expectedDataSet.getTable("Movie"); 146 | Assertion.assertEquals(expectedMovieTable, actualMovieTable); 147 | } 148 | 149 | private XmlDataSet loadDataSet(String flavour) throws DataSetException { 150 | InputStream initialDataSetResource = getClass().getResourceAsStream( getClass().getSimpleName() + "." + testName.getMethodName() + "." 151 | + flavour 152 | + ".xml" ); 153 | XmlDataSet initialDataSet = new XmlDataSet( initialDataSetResource ); 154 | return initialDataSet; 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /showcase-dbunit/src/test/resources/com/danidemi/tutorial/tdd/showcase/dbunit/CreateDb.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE Publisher ( 2 | Id BIGINT IDENTITY, 3 | Name VARCHAR(128) NOT NULL 4 | ); 5 | 6 | CREATE TABLE Movie ( 7 | Id BIGINT IDENTITY, 8 | Title VARCHAR(128) NOT NULL, 9 | Year INTEGER NOT NULL, 10 | Publisher_Id BIGINT FOREIGN KEY REFERENCES Publisher(Id) 11 | ); -------------------------------------------------------------------------------- /showcase-dbunit/src/test/resources/com/danidemi/tutorial/tdd/showcase/dbunit/DbTest.doNotInsertAMovieWithUnknownPublisher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Id 5 | Name 6 | 7 | 1 8 | MGM 9 | 10 | 11 | 2 12 | LucasFilm 13 | 14 | 15 | 16 | Id 17 | Title 18 | Year 19 | Publisher_Id 20 | 21 | 1 22 | Start Wars 23 | 1977 24 | 2 25 | 26 | 27 | 2 28 | The Departed 29 | 2006 30 | 1 31 | 32 | 33 | 3 34 | The Matrix 35 | 1999 36 | 1 37 | 38 | 39 | -------------------------------------------------------------------------------- /showcase-dbunit/src/test/resources/com/danidemi/tutorial/tdd/showcase/dbunit/DbTest.insertMovie.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Id 5 | Name 6 | 7 | 1 8 | MGM 9 | 10 | 11 | 2 12 | LucasFilm 13 | 14 | 15 | 16 | Id 17 | Title 18 | Year 19 | Publisher_Id 20 | 21 | 1 22 | Start Wars 23 | 1977 24 | 2 25 | 26 | 27 | 2 28 | The Departed 29 | 2006 30 | 1 31 | 32 | 33 | 3 34 | The Matrix 35 | 1999 36 | 1 37 | 38 | 39 | -------------------------------------------------------------------------------- /showcase-dbunit/src/test/resources/com/danidemi/tutorial/tdd/showcase/dbunit/DbTest.verifyDataSet.expected.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Id 5 | Name 6 | 7 | 1 8 | MGM 9 | 10 | 11 | 12 | TITLE 13 | YEAR 14 | PUBLISHER_ID 15 | 16 | My Movie 17 | 2014 18 | 1 19 | 20 | 21 | -------------------------------------------------------------------------------- /showcase-dbunit/src/test/resources/com/danidemi/tutorial/tdd/showcase/dbunit/DbTest.verifyDataSet.initial.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Id 5 | Name 6 | 7 | 1 8 | MGM 9 | 10 | 11 | 12 | Id 13 | Title 14 | Year 15 | Publisher_Id 16 | 17 | -------------------------------------------------------------------------------- /showcase-hamcrest/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.classpath 3 | /.project 4 | /.settings 5 | *.iml -------------------------------------------------------------------------------- /showcase-hamcrest/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | showcase-hamcrest 9 | 10 | 11 | 12 | com.danidemi.tutorial.tdd 13 | tdd-showcase-main 14 | ${project.version} 15 | 16 | 17 | org.hamcrest 18 | hamcrest-library 19 | 20 | 21 | org.powermock 22 | powermock-module-junit4 23 | 24 | 25 | -------------------------------------------------------------------------------- /showcase-hamcrest/src/test/java/com/danidemi/tutorial/tdd/showcase/hamcrest/InvoiceIsPayed.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.hamcrest; 2 | 3 | import org.hamcrest.Description; 4 | import org.hamcrest.TypeSafeMatcher; 5 | 6 | import com.danidemi.tutorial.tdd.showcase.accounting.Invoice; 7 | 8 | class InvoiceIsPayed extends TypeSafeMatcher { 9 | 10 | @Override 11 | public void describeTo(Description description) { 12 | description.appendText("supposed to be payed"); 13 | } 14 | 15 | @Override 16 | protected boolean matchesSafely(Invoice invoice) { 17 | return invoice.hasBeenPayed(); 18 | } 19 | 20 | 21 | 22 | } -------------------------------------------------------------------------------- /showcase-hamcrest/src/test/java/com/danidemi/tutorial/tdd/showcase/hamcrest/InvoiceMatchers.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.hamcrest; 2 | 3 | public abstract class InvoiceMatchers { 4 | 5 | public static final InvoiceIsPayed payed(){ 6 | return new InvoiceIsPayed(); 7 | } 8 | 9 | public static final RedefineToString redefineToString() { 10 | return new RedefineToString(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /showcase-hamcrest/src/test/java/com/danidemi/tutorial/tdd/showcase/hamcrest/InvoiceTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.hamcrest; 2 | 3 | import static org.junit.Assert.assertThat; 4 | import static org.hamcrest.Matchers.*; 5 | 6 | import org.junit.Test; 7 | 8 | import com.danidemi.tutorial.tdd.showcase.accounting.Invoice; 9 | 10 | import static com.danidemi.tutorial.tdd.showcase.hamcrest.InvoiceMatchers.*; 11 | 12 | 13 | public class InvoiceTest { 14 | 15 | @Test 16 | public void newInvoiceShouldNotBePayed() { 17 | 18 | // given 19 | // ...a new invoice 20 | Invoice tested = new Invoice(12L, 3400); 21 | 22 | // then 23 | // ...it should not be payed 24 | assertThat(tested, not( payed() )); 25 | 26 | } 27 | 28 | @Test 29 | public void payedInvoiceShouldBePayed() { 30 | 31 | // given 32 | // ...a new invoice 33 | Invoice tested = new Invoice(12L, 3400); 34 | 35 | // when 36 | tested.pay(); 37 | 38 | // then 39 | // ...it should not be payed 40 | assertThat(tested, payed()); 41 | 42 | } 43 | 44 | @Test 45 | public void shouldRedefineToString() { 46 | 47 | 48 | // given 49 | // ...a new invoice 50 | Invoice tested = new Invoice(12L, 3400); 51 | 52 | // then 53 | // ...it should not be payed 54 | assertThat(tested, redefineToString()); 55 | 56 | } 57 | 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /showcase-hamcrest/src/test/java/com/danidemi/tutorial/tdd/showcase/hamcrest/RedefineToString.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.hamcrest; 2 | 3 | import org.hamcrest.BaseMatcher; 4 | import org.hamcrest.Description; 5 | 6 | public class RedefineToString extends BaseMatcher { 7 | 8 | @Override 9 | public boolean matches(Object item) { 10 | boolean isDefaultToString = true; 11 | String string = item.toString(); 12 | if(string.matches("[\\w\\.]*@[\\w]+")){ 13 | String className = string.split("@")[0]; 14 | try { 15 | Class.forName(className); 16 | isDefaultToString = true; 17 | } catch (ClassNotFoundException e) { 18 | isDefaultToString = false; 19 | } 20 | }else{ 21 | isDefaultToString = false; 22 | } 23 | return !isDefaultToString; 24 | } 25 | 26 | @Override 27 | public void describeTo(Description description) { 28 | description.appendText("should refedine 'toString()'"); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /showcase-junit/.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings 4 | target 5 | *.iml -------------------------------------------------------------------------------- /showcase-junit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | tdd-showcase-junit 9 | 10 | 11 | junit 12 | junit 13 | 14 | 15 | org.hamcrest 16 | hamcrest-library 17 | 18 | 19 | -------------------------------------------------------------------------------- /showcase-junit/src/main/java/com/danidemi/tutorial/tdd/showcase/junit/parameterized/NotATriangleException.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.parameterized; 2 | 3 | class NotATriangleException extends RuntimeException { 4 | 5 | public NotATriangleException() { 6 | super(); 7 | } 8 | 9 | public NotATriangleException(String message, Throwable cause, 10 | boolean enableSuppression, boolean writableStackTrace) { 11 | super(message, cause, enableSuppression, writableStackTrace); 12 | } 13 | 14 | public NotATriangleException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | 18 | public NotATriangleException(String message) { 19 | super(message); 20 | } 21 | 22 | public NotATriangleException(Throwable cause) { 23 | super(cause); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /showcase-junit/src/main/java/com/danidemi/tutorial/tdd/showcase/junit/parameterized/Triangle.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.parameterized; 2 | 3 | 4 | class Triangle { 5 | 6 | Triangle( long side1, long side2, long side3 ){ 7 | 8 | long min = Math.min( Math.min(side1, side2), side3); 9 | if(min<=0) throw new NotATriangleException(); 10 | 11 | long max = Math.max( Math.max(side1, side2), side3); 12 | long sum = side1 + side2 + side3; 13 | if(max > (sum - max)) throw new NotATriangleException(); 14 | } 15 | } -------------------------------------------------------------------------------- /showcase-junit/src/main/java/com/danidemi/tutorial/tdd/showcase/junit/theories/Device.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.theories; 2 | 3 | interface Device { 4 | void turnOn(); 5 | void turnOff(); 6 | boolean isOn(); 7 | } -------------------------------------------------------------------------------- /showcase-junit/src/main/java/com/danidemi/tutorial/tdd/showcase/junit/theories/Radio.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.theories; 2 | 3 | class Radio implements Device { 4 | 5 | private boolean isOff; 6 | 7 | @Override 8 | public void turnOn() { 9 | this.isOff = false; 10 | 11 | } 12 | 13 | @Override 14 | public void turnOff() { 15 | this.isOff = true; 16 | } 17 | 18 | @Override 19 | public boolean isOn() { 20 | return !isOff; 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /showcase-junit/src/main/java/com/danidemi/tutorial/tdd/showcase/junit/theories/TV.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.theories; 2 | 3 | class TV implements Device { 4 | 5 | private boolean isOn; 6 | 7 | @Override 8 | public void turnOn() { 9 | this.isOn = true; 10 | 11 | } 12 | 13 | @Override 14 | public void turnOff() { 15 | this.isOn = false; 16 | } 17 | 18 | @Override 19 | public boolean isOn() { 20 | return isOn; 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/exceptions/TestExceptions.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.exceptions; 2 | 3 | import static org.junit.Assert.*; 4 | import static org.hamcrest.Matchers.instanceOf; 5 | 6 | import java.text.ParseException; 7 | 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | import org.junit.rules.ExpectedException; 11 | 12 | public class TestExceptions { 13 | 14 | @Test 15 | public void doNotParseANumber0() { 16 | 17 | try{ 18 | Integer.parseInt("abc"); 19 | fail("Exception expected"); 20 | }catch(NumberFormatException nfe){ 21 | 22 | }catch(Exception e){ 23 | fail("Wrong Exception"); 24 | } 25 | 26 | } 27 | 28 | @Test(expected=NumberFormatException.class) 29 | public void doNotParseANumber1() { 30 | 31 | Integer.parseInt("abc"); 32 | 33 | } 34 | 35 | @Test 36 | public void doNotParseANumber2() { 37 | 38 | Exception e = null; 39 | try{ 40 | Integer.parseInt("abc"); 41 | }catch(Exception ee){ 42 | e = ee; 43 | } 44 | 45 | assertThat( e, instanceOf(NumberFormatException.class)); 46 | 47 | } 48 | 49 | @Rule public ExpectedException asError = ExpectedException.none(); 50 | 51 | @Test 52 | public void doNotParseANumber3() { 53 | 54 | asError.expect(NumberFormatException.class); 55 | Integer.parseInt("abc"); 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/former/FormerAssertions.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.former; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | public class FormerAssertions { 8 | 9 | @Test 10 | public void test() { 11 | 12 | assertEquals(Math.PI, 3.14, 0.01); 13 | assertArrayEquals("Hello".getBytes(), new byte[]{72, 101, 108, 108, 111}); 14 | 15 | int dividend = 3; 16 | assertTrue( (12 % dividend) == 0 ); 17 | 18 | String helloWorldInCapital = "Hello World"; 19 | String helloWorld = "hello world"; 20 | assertNotEquals(helloWorldInCapital, helloWorld); 21 | assertSame( "Hello World", helloWorldInCapital ); 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/matchers/HamcrestShowcaseOnCollectionsAndArrays.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.matchers; 2 | 3 | import static org.hamcrest.CoreMatchers.equalTo; 4 | import static org.hamcrest.CoreMatchers.is; 5 | import static org.hamcrest.CoreMatchers.startsWith; 6 | import static org.hamcrest.Matchers.anyOf; 7 | import static org.hamcrest.Matchers.array; 8 | import static org.hamcrest.Matchers.arrayContaining; 9 | import static org.hamcrest.Matchers.arrayContainingInAnyOrder; 10 | import static org.hamcrest.Matchers.arrayWithSize; 11 | import static org.hamcrest.Matchers.either; 12 | import static org.hamcrest.Matchers.emptyArray; 13 | import static org.hamcrest.Matchers.emptyCollectionOf; 14 | import static org.hamcrest.Matchers.everyItem; 15 | import static org.hamcrest.Matchers.hasItem; 16 | import static org.hamcrest.number.OrderingComparison.greaterThan; 17 | import static org.hamcrest.text.StringContainsInOrder.stringContainsInOrder; 18 | import static org.junit.Assert.assertThat; 19 | 20 | import java.util.Arrays; 21 | 22 | import org.hamcrest.Matchers; 23 | import org.junit.Test; 24 | 25 | 26 | public class HamcrestShowcaseOnCollectionsAndArrays { 27 | 28 | // arrays, list, collections 29 | @Test public void testCollectionsAndArrays() { 30 | 31 | Integer fibonacci[] = new Integer[] { 1, 1, 2, 3, 5, 8 }; 32 | 33 | assertThat(fibonacci, array( is(1), is(1), is(2), is(3), is(5), is(8)) ); 34 | 35 | assertThat(fibonacci, arrayContaining( is(1), is(1), is(2), is(3), is(5), is(8)) ); 36 | 37 | assertThat(fibonacci, arrayContainingInAnyOrder( is(1), is(8), is(1), is(3), is(5), is(2)) ); 38 | 39 | assertThat(fibonacci, arrayWithSize(6) ); 40 | 41 | assertThat(fibonacci, arrayWithSize( greaterThan(3) ) ); 42 | 43 | 44 | assertThat(Arrays.asList( fibonacci ), everyItem(greaterThan(0))); 45 | 46 | assertThat(Arrays.asList( fibonacci ), hasItem(equalTo(2))); 47 | 48 | assertThat(new String[]{}, emptyArray()); 49 | 50 | assertThat(Arrays.asList( new String[]{} ), emptyCollectionOf(String.class)); 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/matchers/HamcrestShowcaseOnCompositions.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.matchers; 2 | 3 | import static org.hamcrest.CoreMatchers.is; 4 | import static org.hamcrest.CoreMatchers.startsWith; 5 | import static org.hamcrest.Matchers.anyOf; 6 | import static org.hamcrest.Matchers.both; 7 | import static org.hamcrest.Matchers.either; 8 | import static org.hamcrest.number.OrderingComparison.greaterThan; 9 | import static org.hamcrest.number.OrderingComparison.lessThanOrEqualTo; 10 | import static org.hamcrest.text.StringContainsInOrder.stringContainsInOrder; 11 | import static org.junit.Assert.assertThat; 12 | 13 | import java.util.Arrays; 14 | 15 | import org.hamcrest.Matchers; 16 | import org.junit.Test; 17 | 18 | 19 | public class HamcrestShowcaseOnCompositions { 20 | 21 | @Test public void testCollections() { 22 | 23 | assertThat( 24 | "+39 02112233", 25 | Matchers.allOf( 26 | startsWith("+39"), 27 | stringContainsInOrder( Arrays.asList( new String[]{ " ", "0" } ) ) 28 | ) 29 | ); 30 | 31 | 32 | // A gennaio 33 | // B febbraio 34 | // C marzo 35 | // D aprile 36 | // E maggio 37 | // H giugno 38 | // L luglio 39 | // M agosto 40 | // P settembre 41 | // R ottobre 42 | // S novembre 43 | // T dicembre 44 | String identificationCode = "XXXYYY60A01F123Z"; 45 | assertThat( String.valueOf((identificationCode.charAt(8))), 46 | anyOf( 47 | is("A"), is("B"), is("C"), is("D"), 48 | is("E"), is("H"), is("L"), is("M"), 49 | is("P"), is("R"), is("S"), is("T") 50 | ) 51 | ); 52 | 53 | String myFriend = "tom"; 54 | assertThat( myFriend, either(is("tom")).or(is("jerry")) ); 55 | 56 | Double percentage = 0.12; 57 | assertThat( percentage, both(greaterThan(0.0)).and(lessThanOrEqualTo(1.0)) ); 58 | 59 | 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/matchers/HamcrestShowcaseOnMaps.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.matchers; 2 | 3 | import static org.hamcrest.CoreMatchers.is; 4 | import static org.hamcrest.CoreMatchers.not; 5 | import static org.hamcrest.Matchers.hasEntry; 6 | import static org.hamcrest.Matchers.hasKey; 7 | import static org.hamcrest.Matchers.hasValue; 8 | import static org.junit.Assert.assertThat; 9 | 10 | import java.util.HashMap; 11 | import java.util.TimeZone; 12 | 13 | import org.hamcrest.core.StringContains; 14 | import org.junit.Test; 15 | 16 | 17 | public class HamcrestShowcaseOnMaps { 18 | 19 | @Test public void testMaps() { 20 | 21 | TimeZone rome = TimeZone.getTimeZone("Europe/Rome"); 22 | TimeZone paris = TimeZone.getTimeZone("Europe/Paris"); 23 | TimeZone tokio = TimeZone.getTimeZone("Asia/Tokio"); 24 | TimeZone newYork = TimeZone.getTimeZone("America/New York"); 25 | 26 | HashMap city2timezone = new HashMap(); 27 | city2timezone.put("Rome", rome); 28 | city2timezone.put("Paris", paris); 29 | city2timezone.put("New York", newYork); 30 | 31 | assertThat( city2timezone, hasEntry("Rome", rome) ); 32 | assertThat( city2timezone, hasKey("New York")); 33 | assertThat( city2timezone, hasKey( StringContains.containsString(" ") )); 34 | assertThat( city2timezone, hasValue( paris ) ); 35 | assertThat( city2timezone, hasValue( not(is(tokio)) ) ); 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/matchers/HamcrestShowcaseOnNumbers.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.matchers; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.Arrays; 6 | import java.util.HashMap; 7 | import java.util.Locale; 8 | import java.util.StringTokenizer; 9 | import java.util.TimeZone; 10 | 11 | import org.hamcrest.Matcher; 12 | import org.hamcrest.Matchers; 13 | import org.hamcrest.core.IsNull; 14 | import org.hamcrest.core.StringContains; 15 | import org.hamcrest.number.IsCloseTo; 16 | import org.junit.Test; 17 | import org.junit.internal.ArrayComparisonFailure; 18 | 19 | import static org.junit.matchers.JUnitMatchers.isException; 20 | import static org.hamcrest.core.StringContains.containsString; 21 | import static org.hamcrest.CoreMatchers.allOf; 22 | import static org.hamcrest.CoreMatchers.startsWith; 23 | import static org.hamcrest.CoreMatchers.containsString; 24 | import static org.hamcrest.CoreMatchers.endsWith; 25 | import static org.hamcrest.CoreMatchers.nullValue; 26 | import static org.hamcrest.CoreMatchers.not; 27 | import static org.hamcrest.CoreMatchers.instanceOf; 28 | import static org.hamcrest.CoreMatchers.isA; 29 | import static org.hamcrest.Matchers.either; 30 | import static org.hamcrest.Matchers.array; 31 | import static org.hamcrest.Matchers.arrayContaining; 32 | import static org.hamcrest.Matchers.arrayContainingInAnyOrder; 33 | import static org.hamcrest.Matchers.arrayWithSize; 34 | import static org.hamcrest.number.IsCloseTo.closeTo; 35 | import static org.hamcrest.number.OrderingComparison.comparesEqualTo; 36 | import static org.hamcrest.number.OrderingComparison.greaterThan; 37 | import static org.hamcrest.number.OrderingComparison.lessThanOrEqualTo; 38 | import static org.hamcrest.CoreMatchers.is; 39 | import static org.hamcrest.CoreMatchers.equalTo; 40 | import static org.hamcrest.Matchers.allOf; 41 | import static org.hamcrest.Matchers.anyOf; 42 | import static org.hamcrest.Matchers.emptyArray; 43 | import static org.hamcrest.Matchers.emptyCollectionOf; 44 | import static org.hamcrest.Matchers.everyItem; 45 | import static org.hamcrest.Matchers.hasItem; 46 | import static org.hamcrest.Matchers.hasEntry; 47 | import static org.hamcrest.Matchers.hasKey; 48 | import static org.hamcrest.Matchers.hasValue; 49 | import static org.hamcrest.text.StringContainsInOrder.stringContainsInOrder; 50 | import static org.hamcrest.text.IsEmptyString.isEmptyString; 51 | import static org.hamcrest.text.IsEmptyString.isEmptyOrNullString; 52 | 53 | 54 | public class HamcrestShowcaseOnNumbers { 55 | 56 | @Test public void testNumbers() { 57 | 58 | assertThat( Math.PI, closeTo(3.14, .01) ); 59 | 60 | } 61 | 62 | @Test public void testComparables() { 63 | 64 | class Mountain implements Comparable { 65 | private String name; 66 | private Integer height; 67 | public Mountain(String name, int height) { 68 | super(); 69 | this.name = name; 70 | this.height = height; 71 | } 72 | 73 | @Override 74 | public int compareTo(Mountain otherMountain) { 75 | return this.height.compareTo(otherMountain.height); 76 | } 77 | 78 | }; 79 | 80 | 81 | 82 | Mountain nowshak = new Mountain("Nowshak", 7492); 83 | Mountain pumarKish = new Mountain("Pumar Kish", 7492); 84 | Mountain monteBianco = new Mountain("Monte Bianco", 4810); 85 | assertThat( pumarKish, comparesEqualTo(nowshak) ); 86 | assertThat( nowshak, greaterThan(monteBianco)); 87 | 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/matchers/HamcrestShowcaseOnStrings.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.matchers; 2 | 3 | import static org.hamcrest.CoreMatchers.endsWith; 4 | import static org.hamcrest.CoreMatchers.startsWith; 5 | import static org.hamcrest.text.IsEmptyString.isEmptyOrNullString; 6 | import static org.hamcrest.text.IsEmptyString.isEmptyString; 7 | import static org.junit.Assert.assertThat; 8 | 9 | import org.hamcrest.core.StringContains; 10 | import org.junit.Test; 11 | 12 | 13 | public class HamcrestShowcaseOnStrings { 14 | 15 | @Test 16 | public void testEndsWith() { 17 | assertThat( "www.domain.com", endsWith(".com")); 18 | } 19 | 20 | @Test 21 | public void testContainsString() { 22 | assertThat( "People", StringContains.containsString("op")); 23 | } 24 | 25 | @Test 26 | public void testIsEmptyOrNullString() { 27 | assertThat( "", isEmptyOrNullString() ); 28 | assertThat( null, isEmptyOrNullString() ); 29 | } 30 | 31 | @Test 32 | public void testIsEmptyString() { 33 | assertThat( "", isEmptyString() ); 34 | } 35 | 36 | @Test 37 | public void testStartsWith() { 38 | assertThat( 39 | "+3902112233", 40 | startsWith("+39") 41 | ); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/parameterized/NotATriangleTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.parameterized; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | import org.junit.internal.runners.statements.ExpectException; 10 | import org.junit.rules.ExpectedException; 11 | import org.junit.runner.RunWith; 12 | import org.junit.runners.Parameterized; 13 | import org.junit.runners.Parameterized.Parameters; 14 | 15 | @RunWith(Parameterized.class) 16 | public class NotATriangleTest { 17 | 18 | public @Rule ExpectedException asError = ExpectedException.none(); 19 | 20 | @Parameters(name="Case: {index}, not expected to be a triangle with sides {0}, {1}, {2}") 21 | public static Iterable parameters() { 22 | return Arrays.asList( 23 | new Object[]{10, 2, 2}, // not a triangle 24 | new Object[]{2, 10, 2}, // not a triangle 25 | new Object[]{2, 2, 10}, // not a triangle 26 | new Object[]{-10, 10, 10}, 27 | new Object[]{10, -10, 10}, 28 | new Object[]{10, 10, -10}, 29 | new Object[]{10, -10, -10}, 30 | new Object[]{-10, 10, -10}, 31 | new Object[]{-10, -10, 10}, 32 | new Object[]{0, 0, 0} 33 | ); 34 | } 35 | 36 | private long side1; 37 | private long side2; 38 | private long side3; 39 | 40 | public NotATriangleTest( long side1, long side2, long side3 ) { 41 | this.side1 = side1; 42 | this.side2 = side2; 43 | this.side3 = side3; 44 | } 45 | 46 | @Test 47 | public void notATriangle() { 48 | asError.expect(NotATriangleException.class); 49 | 50 | new Triangle(side1, side2, side3); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/parameterized/PlusOperatorParametrizedTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.parameterized; 2 | 3 | import static org.hamcrest.CoreMatchers.is; 4 | import static org.hamcrest.number.OrderingComparison.greaterThan; 5 | import static org.junit.Assert.*; 6 | 7 | import java.util.Arrays; 8 | import java.util.Collection; 9 | 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.junit.runners.Parameterized; 13 | import org.junit.runners.Parameterized.Parameters; 14 | 15 | @RunWith(Parameterized.class) 16 | public class PlusOperatorParametrizedTest { 17 | 18 | @Parameters(name="Case: {index}, a={0}, b={1}") 19 | public static Collection parameters() { 20 | return Arrays.asList( 21 | new Object[]{1, 1}, 22 | new Object[]{46, 10}, 23 | new Object[]{23, 99} 24 | ); 25 | } 26 | 27 | private Integer a; 28 | private Integer b; 29 | 30 | public PlusOperatorParametrizedTest(Integer a, Integer b) { 31 | this.a = a; 32 | this.b = b; 33 | } 34 | 35 | @Test 36 | public void a_plus_b_is_greater_than_a_and_greater_than_b() { 37 | assertThat(a+b, is(greaterThan(a))); 38 | assertThat(a+b, is(greaterThan(b))); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/parameterized/TriangleTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.parameterized; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | import org.junit.internal.runners.statements.Fail; 10 | import org.junit.rules.ExpectedException; 11 | import org.junit.runner.RunWith; 12 | import org.junit.runners.Parameterized; 13 | import org.junit.runners.Parameterized.Parameters; 14 | 15 | @RunWith(Parameterized.class) 16 | public class TriangleTest { 17 | 18 | public @Rule ExpectedException asError = ExpectedException.none(); 19 | 20 | @Parameters(name="Case: {index}, not expected to be a triangle with sides {0}, {1}, {2}") 21 | public static Iterable parameters() { 22 | return Arrays.asList( 23 | new Object[]{10, 7, 7}, 24 | new Object[]{7, 10, 7}, 25 | new Object[]{7, 7, 10} 26 | ); 27 | } 28 | 29 | private long side1; 30 | private long side2; 31 | private long side3; 32 | 33 | public TriangleTest( long side1, long side2, long side3 ) { 34 | this.side1 = side1; 35 | this.side2 = side2; 36 | this.side3 = side3; 37 | } 38 | 39 | @Test 40 | public void validTriangle() { 41 | 42 | try{ 43 | new Triangle(side1, side2, side3); 44 | }catch(NotATriangleException nate){ 45 | fail(); 46 | } 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/rules/ExceptionRuleTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.rules; 2 | 3 | import org.junit.Rule; 4 | import org.junit.Test; 5 | import org.junit.rules.ExpectedException; 6 | import static org.hamcrest.Matchers.instanceOf; 7 | public class ExceptionRuleTest { 8 | 9 | @Rule public ExpectedException asError = ExpectedException.none(); 10 | 11 | @Test 12 | public void expectAnNumberFormatException() { 13 | 14 | asError.expect(NumberFormatException.class); 15 | Integer.parseInt("xyz"); 16 | 17 | } 18 | 19 | @Test 20 | public void expectNothing() { 21 | 22 | Integer.parseInt("100"); 23 | 24 | } 25 | 26 | @Test 27 | public void expectARuntimeException() { 28 | 29 | asError.expect(instanceOf(RuntimeException.class)); 30 | 31 | Integer.parseInt("100a"); 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/rules/MyOwnRuleTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.rules; 2 | 3 | import org.junit.Assume; 4 | import org.junit.Rule; 5 | import org.junit.Test; 6 | import org.junit.internal.AssumptionViolatedException; 7 | import org.junit.runner.Description; 8 | public class MyOwnRuleTest { 9 | 10 | static class AllowMaxSkips extends org.junit.rules.TestWatcher { 11 | 12 | private static int countTests = 0; 13 | private int maxSkips = 0; 14 | 15 | public AllowMaxSkips(int maxSkips) { 16 | super(); 17 | this.maxSkips = maxSkips; 18 | } 19 | 20 | @Override 21 | protected void skipped(AssumptionViolatedException e, 22 | Description description) { 23 | 24 | countTests++; 25 | 26 | if(countTests > maxSkips){ 27 | throw new AssertionError("Too many skips"); 28 | } 29 | } 30 | } 31 | 32 | @Rule public AllowMaxSkips maxSkips = new AllowMaxSkips(4); 33 | 34 | @Test 35 | public void notSoLongProcess() { 36 | Assume.assumeTrue(false); 37 | } 38 | 39 | @Test 40 | public void notSoLongProcess2() { 41 | Assume.assumeTrue(false); 42 | } 43 | 44 | @Test 45 | public void notSoLongProcess3() { 46 | Assume.assumeTrue(false); 47 | } 48 | 49 | @Test 50 | public void notSoLongProcess4() { 51 | Assume.assumeTrue(false); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/rules/TemporaryFolderRuleTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.rules; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.PrintWriter; 6 | 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | import org.junit.rules.TemporaryFolder; 10 | import static org.hamcrest.Matchers.equalTo; 11 | import static org.junit.Assert.assertThat; 12 | 13 | public class TemporaryFolderRuleTest { 14 | 15 | @Rule public TemporaryFolder temp = new TemporaryFolder(); 16 | 17 | @Test 18 | public void shouldWriteFile() throws FileNotFoundException { 19 | 20 | PrintWriter printWriter = new PrintWriter( new File(temp.getRoot(), "hello.txt") ); 21 | printWriter.write("hello world"); 22 | 23 | } 24 | 25 | @Test 26 | public void shouldCountFiles() throws FileNotFoundException { 27 | 28 | assertThat( temp.getRoot().listFiles().length, equalTo(0) ); 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/rules/TestNameRuleTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.rules; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | import org.junit.rules.TestName; 8 | 9 | public class TestNameRuleTest { 10 | 11 | @Rule public TestName testId = new TestName(); 12 | 13 | @Test 14 | public void byteMatch() { 15 | assertEquals("an error occurred in '" + testId.getMethodName() + "'", "x", new String(new byte[]{120})); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/rules/TimeoutRuleTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.rules; 2 | 3 | import org.junit.Rule; 4 | import org.junit.Test; 5 | import org.junit.rules.Timeout; 6 | public class TimeoutRuleTest { 7 | 8 | @Rule public Timeout timeout = new Timeout(500); 9 | 10 | @Test 11 | public void notSoLongProcess() { 12 | 13 | long numberOfTerms = 4; 14 | //long numberOfTerms = Long.MAX_VALUE; 15 | long total = 0; 16 | 17 | for(long i = 0; i< numberOfTerms; i++){ 18 | total = total + (i%2 == 0 ? i : -i); 19 | Thread.yield(); 20 | } 21 | 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/theories/PlusOperatorTheoryTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.theories; 2 | 3 | import static org.hamcrest.CoreMatchers.is; 4 | import static org.hamcrest.number.OrderingComparison.greaterThan; 5 | import static org.junit.Assert.assertThat; 6 | import static org.junit.Assume.assumeTrue; 7 | 8 | import org.junit.experimental.theories.DataPoints; 9 | import org.junit.experimental.theories.Theories; 10 | import org.junit.experimental.theories.Theory; 11 | import org.junit.runner.RunWith; 12 | 13 | @RunWith(Theories.class) 14 | public class PlusOperatorTheoryTest { 15 | 16 | @DataPoints 17 | public static int[] positiveIntegers() { 18 | return new int[] { -1, 1, 10, 1234567, -5986 }; 19 | } 20 | 21 | @Theory 22 | public void a_plus_b_is_greater_than_a_and_greater_than_b(Integer a, Integer b) { 23 | assumeTrue(a >0 && b > 0); 24 | 25 | assertThat(a+b, is(greaterThan(a))); 26 | assertThat(a+b, is(greaterThan(b))); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /showcase-junit/src/test/java/com/danidemi/tutorial/tdd/showcase/junit/theories/TheoryTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.junit.theories; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import org.junit.experimental.theories.DataPoint; 7 | import org.junit.experimental.theories.DataPoints; 8 | import org.junit.experimental.theories.Theories; 9 | import org.junit.experimental.theories.Theory; 10 | import org.junit.runner.RunWith; 11 | 12 | @RunWith(Theories.class) 13 | public class TheoryTest { 14 | 15 | @DataPoints public static Device[] devices(){ 16 | return new Device[]{ 17 | new Radio(), 18 | new TV() 19 | }; 20 | } 21 | 22 | @Theory 23 | public void shouldBeOffAfterBeingTurnedOff(Device device){ 24 | 25 | device.turnOff(); 26 | 27 | assertThat( device.isOn(), is(false) ); 28 | 29 | } 30 | 31 | @Theory 32 | public void shouldBeOnAfterBeingTurnedOn(Device device){ 33 | 34 | device.turnOn(); 35 | 36 | assertThat( device.isOn(), is(true) ); 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /showcase-main/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.settings 3 | /.classpath 4 | /.project 5 | *.iml -------------------------------------------------------------------------------- /showcase-main/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | tdd-showcase-main 9 | 10 | -------------------------------------------------------------------------------- /showcase-main/src/main/java/com/danidemi/tutorial/tdd/showcase/accounting/Invoice.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.accounting; 2 | 3 | public class Invoice { 4 | 5 | private InvoiceStatus status; 6 | private Long number; 7 | private long amount; 8 | 9 | public Invoice(Long number, long amount) { 10 | super(); 11 | this.number = number; 12 | this.amount = amount; 13 | this.status = new WaitingPayementStatus(this); 14 | } 15 | 16 | public void pay(){ 17 | status.pay(); 18 | } 19 | 20 | public boolean hasBeenPayed() { 21 | return status.hasBeenPayed(); 22 | } 23 | 24 | void transitionTo(InvoiceStatus nextStatus) { 25 | this.status = nextStatus; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return "Invoice #" + number; 31 | } 32 | 33 | public int k(String param) { 34 | this.noArgPrivate(); 35 | return this.oneArgPrivate(param); 36 | } 37 | 38 | private int oneArgPrivate(String string) { 39 | return 0; 40 | } 41 | 42 | private void noArgPrivate() { 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /showcase-main/src/main/java/com/danidemi/tutorial/tdd/showcase/accounting/InvoiceStatus.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.accounting; 2 | 3 | public interface InvoiceStatus { 4 | 5 | void pay(); 6 | 7 | boolean hasBeenPayed(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /showcase-main/src/main/java/com/danidemi/tutorial/tdd/showcase/accounting/PayedStatus.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.accounting; 2 | 3 | public class PayedStatus implements InvoiceStatus { 4 | 5 | private Invoice master; 6 | 7 | public PayedStatus(Invoice master) { 8 | this.master = master; 9 | } 10 | 11 | @Override 12 | public void pay() { 13 | throw new IllegalStateException("Already payed"); 14 | } 15 | 16 | @Override 17 | public boolean hasBeenPayed() { 18 | return true; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /showcase-main/src/main/java/com/danidemi/tutorial/tdd/showcase/accounting/WaitingPayementStatus.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.accounting; 2 | 3 | public class WaitingPayementStatus implements InvoiceStatus { 4 | 5 | private Invoice master; 6 | 7 | public WaitingPayementStatus(Invoice invoice) { 8 | this.master = invoice; 9 | } 10 | 11 | @Override 12 | public void pay() { 13 | master.transitionTo( new PayedStatus(master) ); 14 | } 15 | 16 | @Override 17 | public boolean hasBeenPayed() { 18 | // TODO Auto-generated method stub 19 | return false; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /showcase-main/src/main/java/com/danidemi/tutorial/tdd/showcase/authentication/DatabaseException.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.authentication; 2 | 3 | import java.sql.SQLException; 4 | 5 | public class DatabaseException extends RuntimeException { 6 | 7 | public DatabaseException(SQLException e) { 8 | super(e); 9 | } 10 | 11 | /** */ 12 | private static final long serialVersionUID = 2402158290691206310L; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /showcase-main/src/main/java/com/danidemi/tutorial/tdd/showcase/authentication/LoginException.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.authentication; 2 | 3 | public class LoginException extends Exception { 4 | 5 | public LoginException(String message) { 6 | super(message); 7 | } 8 | 9 | /** */ 10 | private static final long serialVersionUID = -8134096018297522414L; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /showcase-main/src/main/java/com/danidemi/tutorial/tdd/showcase/authentication/Repository.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.authentication; 2 | 3 | import java.sql.Connection; 4 | import java.sql.PreparedStatement; 5 | import java.sql.ResultSet; 6 | import java.sql.SQLException; 7 | import java.sql.Timestamp; 8 | 9 | public class Repository { 10 | 11 | public User findUserByUsername(String username) throws SQLException { 12 | User user = null; 13 | Connection connection = getConnection(); 14 | String sql = "SELECT * FROM USERS WHERE USERNAME = ?"; 15 | PreparedStatement s = connection.prepareStatement(sql); 16 | s.setString(1, username); 17 | ResultSet rs = s.executeQuery(); 18 | if (rs.next()) { 19 | /* 20 | * create a new user with values from DB 21 | */ 22 | } 23 | return user; 24 | } 25 | 26 | private Connection getConnection() { 27 | /* 28 | * gets and returns a collection 29 | */ 30 | return null; 31 | } 32 | 33 | public void logAccess(User user) throws SQLException { 34 | Connection connection = getConnection(); 35 | String sql = "INSERT INTO LOG (TIMESTAMP, USER) VALUES (?,?)"; 36 | PreparedStatement s = connection.prepareStatement(sql); 37 | s.setTimestamp(1, new Timestamp(System.currentTimeMillis())); 38 | s.setString(2, user.getUsername()); 39 | s.executeUpdate(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /showcase-main/src/main/java/com/danidemi/tutorial/tdd/showcase/authentication/Service.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.authentication; 2 | 3 | import java.sql.SQLException; 4 | import java.util.Date; 5 | 6 | public class Service { 7 | 8 | Repository repository; 9 | 10 | public User login(String username, String password) throws LoginException { 11 | if (username == null || password == null) { 12 | throw new IllegalArgumentException(); 13 | } 14 | User user = null; 15 | try { 16 | user = repository.findUserByUsername(username); 17 | } catch (SQLException e) { 18 | throw new DatabaseException(e); 19 | } 20 | if (user == null) { 21 | throw new LoginException("wrong username"); 22 | } 23 | if (!user.getPassword().equals(password)) { 24 | throw new LoginException("wrong password"); 25 | } 26 | try { 27 | repository.logAccess(user); 28 | } catch (SQLException e) { 29 | throw new DatabaseException(e); 30 | } 31 | return user; 32 | } 33 | 34 | public String generatePassword(long seed) { 35 | if (Math.abs(seed) == 1L) { 36 | throw new IllegalArgumentException("abs(seed) must be <> 1"); 37 | } 38 | return "_"+Long.toHexString((Math.round(Math.pow(seed, 2))))+"!"; 39 | } 40 | 41 | public String generatePassword() { 42 | Date date = new Date(); 43 | return "!"+date.getTime()+"_"; 44 | } 45 | 46 | public void setRepository(Repository repository) { 47 | this.repository = repository; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /showcase-main/src/main/java/com/danidemi/tutorial/tdd/showcase/authentication/User.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.authentication; 2 | 3 | public class User { 4 | 5 | private String username; 6 | private String password; 7 | 8 | public String getUsername() { 9 | return username; 10 | } 11 | public void setUsername(String username) { 12 | this.username = username; 13 | } 14 | public String getPassword() { 15 | return password; 16 | } 17 | public void setPassword(String password) { 18 | this.password = password; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /showcase-mockito/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.settings 3 | /.classpath 4 | /.project 5 | *.iml 6 | -------------------------------------------------------------------------------- /showcase-mockito/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | tdd-mockito-showcase 9 | 10 | 11 | 12 | ${project.groupId} 13 | tdd-showcase-main 14 | ${project.version} 15 | 16 | 17 | org.mockito 18 | mockito-core 19 | 20 | 21 | junit 22 | junit 23 | 24 | 25 | org.hamcrest 26 | hamcrest-library 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /showcase-mockito/src/test/java/com/danidemi/tutorial/tdd/showcase/mockito/ServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.mockito; 2 | 3 | import com.danidemi.tutorial.tdd.showcase.authentication.LoginException; 4 | import com.danidemi.tutorial.tdd.showcase.authentication.Repository; 5 | import com.danidemi.tutorial.tdd.showcase.authentication.Service; 6 | import com.danidemi.tutorial.tdd.showcase.authentication.User; 7 | import org.junit.Test; 8 | import org.mockito.InOrder; 9 | 10 | import java.sql.SQLException; 11 | 12 | import static org.hamcrest.CoreMatchers.equalTo; 13 | import static org.hamcrest.CoreMatchers.is; 14 | import static org.junit.Assert.*; 15 | import static org.mockito.Mockito.*; 16 | 17 | public class ServiceTest { 18 | 19 | @Test 20 | public void testGeneratePasswordWithSeedHappyPath() { 21 | //given 22 | long seed = 875115L; 23 | Service service = new Service(); 24 | 25 | //when 26 | String result = service.generatePassword(seed); 27 | 28 | //then 29 | assertThat(result, is(equalTo("_b24ecd68b9!"))); 30 | } 31 | 32 | @Test(expected=IllegalArgumentException.class) 33 | public void testGeneratePasswordWithInvalidSeed() { 34 | //given 35 | long seed = -1L; 36 | Service service = new Service(); 37 | 38 | //when 39 | String result = service.generatePassword(seed); 40 | 41 | //then 42 | //expect exception 43 | } 44 | 45 | @Test 46 | public void testLoginHappyPath() { 47 | Repository repo = mock(Repository.class); 48 | String username = "username"; 49 | String password = "password"; 50 | User user = new User(); 51 | user.setUsername(username); 52 | user.setPassword(password); 53 | Service service = new Service(); 54 | service.setRepository(repo); 55 | 56 | try { 57 | when(repo.findUserByUsername(username)).thenReturn(user); 58 | doNothing().when(repo).logAccess(user); 59 | } catch (SQLException e) { 60 | e.printStackTrace(); 61 | fail(e.getMessage()); 62 | } 63 | 64 | try { 65 | User result = service.login(username, password); 66 | assertNotNull(result); 67 | assertSame(user, result); 68 | } catch (LoginException e) { 69 | e.printStackTrace(); 70 | fail(e.getMessage()); 71 | } 72 | try { 73 | verify(repo, times(1)).findUserByUsername(username); 74 | InOrder inOrder = inOrder(repo); 75 | inOrder.verify(repo).findUserByUsername(username); 76 | inOrder.verify(repo).logAccess(user); 77 | } catch (SQLException e) { 78 | e.printStackTrace(); 79 | fail(e.getMessage()); 80 | } 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /showcase-mockito/src/test/java/com/danidemi/tutorial/tdd/showcase/mockito/StubbingShowcase.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.mockito; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.junit.Assert.assertThat; 5 | import static org.mockito.Matchers.any; 6 | import static org.mockito.Mockito.doThrow; 7 | import static org.mockito.Mockito.mock; 8 | import static org.mockito.Mockito.when; 9 | 10 | import java.text.SimpleDateFormat; 11 | import java.util.Comparator; 12 | import java.util.Map; 13 | 14 | import org.junit.Rule; 15 | import org.junit.Test; 16 | import org.junit.rules.ExpectedException; 17 | import org.mockito.invocation.InvocationOnMock; 18 | import org.mockito.stubbing.Answer; 19 | 20 | public class StubbingShowcase { 21 | 22 | public @Rule ExpectedException asError = ExpectedException.none(); 23 | 24 | @Test 25 | public void stubAnInterface() { 26 | 27 | Comparator comparator = mock(Comparator.class); 28 | 29 | int compare = comparator.compare("s1", "s2"); 30 | 31 | assertThat( compare, equalTo(0) ); 32 | 33 | } 34 | 35 | @Test 36 | public void stubANotVoidMethod() { 37 | 38 | Comparator comparator = mock(Comparator.class); 39 | 40 | when(comparator.compare("s1", "s2")).thenReturn(1); 41 | assertThat( comparator.compare("s1", "s2"), equalTo(1) ); 42 | 43 | when(comparator.compare("s2", "s1")).thenReturn(-1); 44 | assertThat( comparator.compare("s2", "s1"), equalTo(-1) ); 45 | 46 | assertThat( comparator.compare("k", "K"), equalTo(0) ); 47 | 48 | } 49 | 50 | @Test 51 | public void stubThrowingAnException() { 52 | 53 | asError.expect(IllegalArgumentException.class); 54 | 55 | Comparator comparator = mock(Comparator.class); 56 | when(comparator.compare(any(String.class), any(String.class))).thenThrow( new IllegalArgumentException() ); 57 | 58 | comparator.compare("a", "b"); 59 | 60 | } 61 | 62 | @Test 63 | public void stubbingProvidingAComplexBehaviour() { 64 | 65 | Comparator comparator = mock(Comparator.class); 66 | when(comparator.compare(any(String.class), any(String.class))).then(new Answer() { 67 | 68 | @Override 69 | public Integer answer(InvocationOnMock invocation) throws Throwable { 70 | Object[] arguments = invocation.getArguments(); 71 | return ((String)arguments[0]).length() + ((String)arguments[1]).length(); 72 | } 73 | }); 74 | 75 | assertThat( comparator.compare("Hello", "World"), equalTo(10) ); 76 | 77 | } 78 | 79 | @Test 80 | public void stubVoidMethod() { 81 | 82 | SimpleDateFormat mock = mock( SimpleDateFormat.class ); 83 | 84 | doThrow( new IllegalArgumentException() ) 85 | .when( mock ) 86 | .setLenient(any(Boolean.class)); 87 | 88 | asError.expect(IllegalArgumentException.class); 89 | 90 | mock.setLenient(false); 91 | 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /showcase-mockito/src/test/java/com/danidemi/tutorial/tdd/showcase/mockito/VerifyShowcase.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.mockito; 2 | 3 | import java.util.List; 4 | 5 | import org.junit.Test; 6 | import org.mockito.ArgumentCaptor; 7 | import org.mockito.InOrder; 8 | import org.mockito.verification.VerificationMode; 9 | 10 | import static org.mockito.Mockito.*; 11 | import static org.mockito.Matchers.argThat; 12 | import static org.hamcrest.text.IsEqualIgnoringCase.equalToIgnoringCase; 13 | import static org.hamcrest.Matchers.*; 14 | 15 | import static org.junit.Assert.assertThat; 16 | 17 | public class VerifyShowcase { 18 | 19 | @Test 20 | public void verifyExactInvocation() { 21 | 22 | List myList = mock(List.class); 23 | 24 | myList.add("Hello"); 25 | myList.add("World"); 26 | 27 | verify(myList).add("World"); 28 | verify(myList).add("Hello"); 29 | 30 | } 31 | 32 | @Test 33 | public void verifyInvocationWihtMatchers() { 34 | 35 | List myList = mock(List.class); 36 | 37 | myList.add("Hello"); 38 | 39 | verify(myList).add(argThat(equalToIgnoringCase("HELLO"))); 40 | 41 | } 42 | 43 | @Test public void verifyMultipleInvocations() { 44 | 45 | List myList = mock(List.class); 46 | 47 | myList.add("Hello"); 48 | myList.add("Hello"); 49 | myList.add("Hello"); 50 | 51 | verify(myList, times(3)).add("Hello"); 52 | 53 | List myList2 = mock(List.class); 54 | 55 | myList2.add("Hello"); 56 | myList2.add("Hello"); 57 | myList2.add("Hello"); 58 | 59 | verify(myList, atLeastOnce()).add("Hello"); 60 | 61 | List myList3 = mock(List.class); 62 | 63 | myList3.add("Hello"); 64 | myList3.add("Hello"); 65 | myList3.add("Hello"); 66 | 67 | verify(myList, atMost(10)).add("Hello"); 68 | 69 | 70 | } 71 | 72 | @Test public void verifyTimeout() { 73 | 74 | final List myList = mock(List.class); 75 | 76 | Thread thread = new Thread( new Runnable() { 77 | 78 | @Override 79 | public void run() { 80 | try { 81 | Thread.sleep(1000); 82 | } catch (InterruptedException e) { 83 | } 84 | myList.add("Hello"); 85 | } 86 | } ); 87 | thread.start(); 88 | 89 | verify(myList, timeout(3000)).add("Hello"); 90 | 91 | } 92 | 93 | @Test public void verifyInOrder() { 94 | 95 | final List myList = mock(List.class); 96 | 97 | myList.add("Hello"); 98 | myList.add("World"); 99 | 100 | InOrder inOrder = inOrder(myList); 101 | inOrder.verify(myList).add("Hello"); 102 | inOrder.verify(myList).add("World"); 103 | 104 | } 105 | 106 | @Test public void verifyComplexArgument() { 107 | 108 | final List myList = mock(List.class); 109 | ArgumentCaptor stringArg = ArgumentCaptor.forClass(String.class); 110 | 111 | myList.add("Hello"); 112 | myList.add("World"); 113 | 114 | verify(myList, times(2)).add( stringArg.capture() ); 115 | 116 | List allValues = stringArg.getAllValues(); 117 | 118 | assertThat( allValues.get(0), equalTo("Hello") ); 119 | assertThat( allValues.get(1), equalTo("World") ); 120 | 121 | } 122 | 123 | 124 | 125 | } 126 | -------------------------------------------------------------------------------- /showcase-powermock/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.settings 3 | /.classpath 4 | /.project 5 | *.iml 6 | -------------------------------------------------------------------------------- /showcase-powermock/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | tdd-showcase-powermock 9 | 10 | 11 | 12 | com.danidemi.tutorial.tdd 13 | tdd-showcase-main 14 | ${project.version} 15 | 16 | 17 | org.powermock 18 | powermock-module-junit4 19 | 20 | 21 | org.powermock 22 | powermock-api-mockito 23 | 24 | 25 | junit 26 | junit 27 | 28 | 29 | org.hamcrest 30 | hamcrest-library 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /showcase-powermock/src/test/java/com/danidemi/tutorial/tdd/showcase/powermock/Constructors.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.powermock; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.junit.Assert.assertThat; 5 | import static org.powermock.api.mockito.PowerMockito.mock; 6 | import static org.powermock.api.mockito.PowerMockito.when; 7 | import static org.powermock.api.mockito.PowerMockito.whenNew; 8 | 9 | import java.util.Date; 10 | 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.powermock.core.classloader.annotations.PrepareForTest; 14 | import org.powermock.modules.junit4.PowerMockRunner; 15 | 16 | @RunWith(PowerMockRunner.class) 17 | @PrepareForTest(Date.class) 18 | public class Constructors { 19 | 20 | @Test 21 | public void mockConstructor() throws Exception { 22 | 23 | // Mock a constructor to make it return what you want. 24 | // Extremely useful with code using Date. 25 | 26 | // given 27 | // ...a date "0" 28 | Date myDate = new Date(0); 29 | // ...when code instantiate a new Date, return the date "0" 30 | whenNew(Date.class).withNoArguments().thenReturn( myDate ); 31 | 32 | // when 33 | long tested = new Date().getTime(); 34 | 35 | // then 36 | assertThat( tested, equalTo(0L) ); 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /showcase-powermock/src/test/java/com/danidemi/tutorial/tdd/showcase/powermock/PrivateMethods.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.powermock; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.Arrays; 5 | 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.powermock.api.mockito.PowerMockito; 9 | import org.powermock.core.classloader.annotations.PrepareForTest; 10 | import org.powermock.modules.junit4.PowerMockRunner; 11 | 12 | import com.danidemi.tutorial.tdd.showcase.accounting.Invoice; 13 | 14 | import static org.mockito.Matchers.anyString; 15 | import static org.mockito.Matchers.argThat; 16 | import static org.powermock.api.support.membermodification.MemberMatcher.method; 17 | import static org.hamcrest.Matchers.any; 18 | import static org.hamcrest.Matchers.either; 19 | import static org.hamcrest.Matchers.is; 20 | import static org.junit.Assert.assertThat; 21 | import static org.powermock.api.mockito.PowerMockito.*; 22 | 23 | @RunWith(PowerMockRunner.class) 24 | @PrepareForTest(Invoice.class) 25 | public class PrivateMethods { 26 | 27 | @Test(expected=IllegalArgumentException.class) 28 | public void mockPrivateMethodWithArgs() throws Exception { 29 | 30 | // Given 31 | // ...a spy on the Invoice 32 | Invoice invoice = PowerMockito.spy( new Invoice(10L, 30) ); 33 | 34 | // ...it is possible to "partially" mock just the private method "oneArgPrivate" to make it 35 | // throw an exception when it is invoked with "ole" parameter. 36 | when(invoice, "oneArgPrivate", "ole").thenThrow(new IllegalArgumentException()); 37 | 38 | 39 | 40 | // When 41 | invoice.k("ole"); 42 | 43 | 44 | 45 | // Then 46 | PowerMockito.verifyPrivate(invoice).invoke( Invoice.class.getMethod("z", (Class[])null ) ).withNoArguments(); 47 | 48 | } 49 | 50 | @Test(expected=IllegalArgumentException.class) 51 | public void mockPrivateMethodWithoutArgs() throws Exception { 52 | 53 | // Given 54 | // ...a spy on the Invoice 55 | Invoice invoice = PowerMockito.spy( new Invoice(10L, 30) ); 56 | 57 | // ...it is possible to "partially" mock just the private method "noArgPrivate" to make it 58 | // throw an exception when it is invoked. Its invocation is somewhat cumbersome becasue we need to specify 59 | // 'no args' with an empty array. 60 | when(invoice, "noArgPrivate", new Object[]{}).thenThrow(new IllegalArgumentException()); 61 | 62 | // When 63 | invoice.k("test"); 64 | 65 | 66 | PowerMockito.verifyPrivate(invoice).invoke( Invoice.class.getMethod("z", (Class[])null ) ).withNoArguments(); 67 | 68 | } 69 | 70 | @Test 71 | public void stubbingPrivateMethods() throws Exception { 72 | 73 | // Given 74 | // ...a spy on the Invoice 75 | Invoice invoice = PowerMockito.spy( new Invoice(10L, 30) ); 76 | 77 | 78 | when(invoice, method(Invoice.class, "oneArgPrivate", String.class )) 79 | .withArguments("test1") 80 | .thenReturn(1234567); 81 | 82 | 83 | when(invoice, method(Invoice.class, "oneArgPrivate", String.class )) 84 | .withArguments("test2") 85 | .thenReturn(123); 86 | 87 | // When 88 | int k1 = invoice.k("test1"); 89 | int k2 = invoice.k("test2"); 90 | 91 | // Then 92 | assertThat( k1, is(1234567) ); 93 | assertThat( k2, is(123) ); 94 | 95 | } 96 | 97 | 98 | @Test 99 | public void stubbingPrivateMethodsWithMatchers() throws Exception { 100 | 101 | // Given 102 | // ...a spy on the Invoice 103 | Invoice invoice = PowerMockito.spy( new Invoice(10L, 30) ); 104 | 105 | 106 | when(invoice, method(Invoice.class, "oneArgPrivate", String.class )) 107 | .withArguments( argThat( either(is("ok")).or(is("ko")) ) ) 108 | .thenReturn(34); 109 | 110 | // When 111 | int k1 = invoice.k("ko"); 112 | int k2 = invoice.k("ok"); 113 | int k3 = invoice.k("kk"); 114 | 115 | // Then 116 | assertThat( k1, is(34) ); 117 | assertThat( k2, is(34) ); 118 | assertThat( k3, is(0) ); 119 | 120 | } 121 | 122 | 123 | } 124 | -------------------------------------------------------------------------------- /showcase-powermock/src/test/java/com/danidemi/tutorial/tdd/showcase/powermock/StaticMethods.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.powermock; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.powermock.api.mockito.PowerMockito; 8 | import org.powermock.core.classloader.annotations.PrepareForTest; 9 | import org.powermock.modules.junit4.PowerMockRunner; 10 | 11 | import static org.mockito.Matchers.any; 12 | import static org.mockito.Matchers.isNull; 13 | import static org.hamcrest.Matchers.equalTo; 14 | import static org.hamcrest.Matchers.is; 15 | import static org.hamcrest.Matchers.nullValue; 16 | import static org.powermock.api.mockito.PowerMockito.*; 17 | 18 | @RunWith(PowerMockRunner.class) 19 | @PrepareForTest(String.class) 20 | public class StaticMethods { 21 | 22 | @Test 23 | public void doNotMockAtAll() { 24 | 25 | // when 26 | String resultFromStatic = String.valueOf( new Double(123.43) ); 27 | 28 | // then 29 | // ...the class with static methods is not mocked at all, 30 | // invocations are obviously taken in charge by the class itself. 31 | assertThat( resultFromStatic, equalTo("123.43") ); 32 | assertThat( String.format("'%s'", "Hello World"), is("'Hello World'")); 33 | } 34 | 35 | @Test 36 | public void staticMockingAClassMeansMockingAllOfItsStaticMethods() { 37 | 38 | // given 39 | PowerMockito.mockStatic(String.class); 40 | when(String.valueOf(any(Double.class))).thenReturn("oh"); 41 | 42 | // when 43 | String resultFromMockedStatic = String.valueOf( new Double(123.43) ); 44 | 45 | // then 46 | // ...all static methods are mocked. This means that as it happens with Mockito, 47 | // other static methods will return the default value depending on the type, e.g. 48 | // false for boolean, null for Object, 0 for numbers, etc... 49 | assertThat( resultFromMockedStatic, equalTo("oh") ); 50 | assertThat( String.format("'%s'", "Hello World"), nullValue()); 51 | 52 | } 53 | 54 | @Test public void spyingAllowsToStubJustOneMethod() { 55 | 56 | // given 57 | PowerMockito.spy(String.class); 58 | when(String.valueOf(any(Double.class))).thenReturn("oh"); 59 | 60 | // when 61 | String resultFromMockedStatic = String.valueOf( new Double(123.43) ); 62 | 63 | // then 64 | // ...just the stubbed static method changes 65 | assertThat( resultFromMockedStatic, equalTo("oh") ); 66 | assertThat( String.format("'%s'", "Hello World"), is("'Hello World'")); 67 | 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /showcase-powermock/src/test/java/com/danidemi/tutorial/tdd/showcase/powermock/StatusTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.showcase.powermock; 2 | import static org.hamcrest.Matchers.isA; 3 | import static org.mockito.Matchers.argThat; 4 | import static org.mockito.Mockito.times; 5 | import static org.powermock.api.mockito.PowerMockito.*; 6 | 7 | import org.junit.Ignore; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.powermock.api.mockito.verification.PrivateMethodVerification; 11 | import org.powermock.core.classloader.annotations.PrepareForTest; 12 | import org.powermock.modules.junit4.PowerMockRunner; 13 | 14 | import com.danidemi.tutorial.tdd.showcase.accounting.Invoice; 15 | import com.danidemi.tutorial.tdd.showcase.accounting.PayedStatus; 16 | 17 | @RunWith(PowerMockRunner.class) 18 | @PrepareForTest(Invoice.class) 19 | public class StatusTest { 20 | 21 | @Ignore 22 | @Test 23 | public void testInvoice() throws Exception { 24 | 25 | 26 | //Invoice.class.getMethod("transitionTo", parameterTypes) 27 | 28 | Invoice invoice = mock(Invoice.class); 29 | //Invoice invoice = spy( new Invoice(100L, 7800)); 30 | 31 | invoice.pay(); 32 | 33 | PrivateMethodVerification verifyPrivate = verifyPrivate(invoice, 34 | times(2)); 35 | verifyPrivate.invoke("transitionTo", argThat(isA(PayedStatus.class))); 36 | //verifyPrivate.invoke("transitionTo", InvoiceStatus.class); 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tdd-darts/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /.project 3 | /.classpath 4 | /target 5 | *.iml 6 | -------------------------------------------------------------------------------- /tdd-darts/README.md: -------------------------------------------------------------------------------- 1 | # Darts # 2 | 3 | How to create a class that counts point for a dart game using a pure TDD approach. -------------------------------------------------------------------------------- /tdd-darts/pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 4.0.0 6 | tdd-darts 7 | jar 8 | 9 | com.danidemi.tutorial.tdd 10 | tdd-parent 11 | 0.0.2-SNAPSHOT 12 | 13 | 14 | 15 | 16 | junit 17 | junit 18 | 19 | 20 | org.hamcrest 21 | hamcrest-library 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /tdd-darts/src/main/java/com/danidemi/tutorial/tdd/darts/Darts.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.darts; 2 | 3 | 4 | public class Darts { 5 | 6 | enum Multiplier{ 7 | DOUBLE, TRIPLE 8 | } 9 | 10 | private int dartsLeft = 3; 11 | private int turn = 1; 12 | 13 | private int score = 301; 14 | private int lastTurnScore = score; 15 | private boolean isFinished = false; 16 | 17 | public int score() { 18 | return score; 19 | } 20 | 21 | public void dart(int i) { 22 | computeScore(i, null); 23 | } 24 | 25 | public void dart(int score, Multiplier multiplier) { 26 | int mult = 0; 27 | switch (multiplier) { 28 | case DOUBLE: 29 | mult = 2; 30 | break; 31 | 32 | case TRIPLE: 33 | mult = 3; 34 | break; 35 | } 36 | int dartScore = mult * score; 37 | 38 | computeScore(dartScore, multiplier); 39 | } 40 | 41 | private void computeScore(int dartScore, Multiplier m) { 42 | 43 | int newScore = score - dartScore; 44 | 45 | if(newScore == 0 && m.equals(Multiplier.DOUBLE)){ 46 | isFinished = true; 47 | return; 48 | } 49 | 50 | if( (newScore == 1 && dartsLeft != 1) || (newScore < 0)){ // bust 51 | newScore = lastTurnScore; 52 | dartsLeft = 3; 53 | turn++; 54 | }else if(dartsLeft == 1){ 55 | lastTurnScore = newScore; 56 | dartsLeft = 3; 57 | turn++; 58 | }else{ 59 | dartsLeft--; 60 | } 61 | 62 | score = newScore; 63 | 64 | } 65 | 66 | public int getTurn() { 67 | return turn; 68 | } 69 | 70 | public int dartsLeft() { 71 | return dartsLeft; 72 | } 73 | 74 | public boolean isFinished() { 75 | return isFinished; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /tdd-darts/src/test/java/com/danidemi/tutorial/tdd/darts/DartsTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.darts; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.hamcrest.Matchers.is; 5 | import static org.junit.Assert.*; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.danidemi.tutorial.tdd.darts.Darts; 11 | import com.danidemi.tutorial.tdd.darts.Darts.Multiplier; 12 | 13 | 14 | public class DartsTest { 15 | 16 | private Darts d; 17 | 18 | @Before public void setUpGame(){ 19 | d = new Darts(); 20 | } 21 | 22 | @Test public void aGameShouldStartAt301(){ 23 | assertThat(d.score(), equalTo(301) ); 24 | assertThat(d.isFinished(), is(false)); 25 | } 26 | 27 | @Test public void shouldCorrectlyScoreANormalThrow(){ 28 | d.dart(20); 29 | assertThat(d.score(), is(281)); 30 | } 31 | 32 | @Test public void shouldCountADoubleThrow(){ 33 | d.dart(20, Multiplier.DOUBLE); 34 | assertThat(d.score(), is(301 - 20*2)); 35 | } 36 | 37 | @Test public void shouldCountATripleThrow(){ 38 | d.dart(20, Multiplier.TRIPLE); 39 | assertThat(d.score(), is(301 - 20*3)); 40 | } 41 | 42 | @Test public void shouldCountTheTurnInitially(){ 43 | assertThat(d.getTurn(), is(1)); 44 | assertThat(d.dartsLeft(), is(3)); 45 | } 46 | 47 | @Test public void shouldCountTheTurn(){ 48 | 49 | d.dart(1); 50 | assertThat(d.getTurn(), is(1)); 51 | assertThat(d.dartsLeft(), is(2)); 52 | 53 | d.dart(1); 54 | assertThat(d.getTurn(), is(1)); 55 | assertThat(d.dartsLeft(), is(1)); 56 | 57 | d.dart(1); 58 | assertThat(d.getTurn(), is(2)); 59 | assertThat(d.dartsLeft(), is(3)); 60 | } 61 | 62 | @Test public void shouldGoBustReaching1(){ 63 | 64 | for(int i=0; i<3; i++){ 65 | d.dart(20, Multiplier.TRIPLE); 66 | } 67 | // 121 68 | 69 | d.dart(20, Multiplier.TRIPLE); 70 | d.dart(20, Multiplier.TRIPLE); 71 | // 1 --> Bust! 72 | 73 | assertEquals(121, d.score()); 74 | assertEquals(3, d.getTurn()); 75 | assertEquals(3, d.dartsLeft()); 76 | 77 | } 78 | 79 | @Test public void shouldGoBustAboveZero(){ 80 | 81 | for(int i=0; i<3; i++){ 82 | d.dart(20, Multiplier.TRIPLE); 83 | } 84 | // 121 85 | 86 | d.dart(15, Multiplier.TRIPLE); // 76 87 | d.dart(15, Multiplier.TRIPLE); // 31 88 | d.dart(20, Multiplier.TRIPLE); // -29 Bust! 89 | 90 | assertEquals(121, d.score()); 91 | assertEquals(3, d.getTurn()); 92 | assertEquals(3, d.dartsLeft()); 93 | 94 | 95 | } 96 | 97 | @Test public void shouldCompleteAGameWithADouble(){ 98 | 99 | for(int i=0; i<3; i++){ 100 | d.dart(20, Multiplier.TRIPLE); 101 | } 102 | // 121 103 | 104 | d.dart(17, Multiplier.TRIPLE); // 70 105 | d.dart(20, Multiplier.TRIPLE); // 10 106 | d.dart(5, Multiplier.DOUBLE); // 0 107 | 108 | assertTrue(d.isFinished()); 109 | 110 | } 111 | 112 | 113 | } 114 | -------------------------------------------------------------------------------- /tdd-fill-the-code-starting-from-test/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /.project 3 | /.classpath 4 | /target 5 | *.iml 6 | -------------------------------------------------------------------------------- /tdd-fill-the-code-starting-from-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.danidemi.tutorial.tdd 9 | tdd-parent 10 | 0.0.2-SNAPSHOT 11 | 12 | 13 | tdd-fill-the-code-starting-from-test 14 | jar 15 | 16 | 17 | 18 | junit 19 | junit 20 | 21 | 22 | 23 | 24 | 25 | 26 | org.apache.maven.plugins 27 | maven-compiler-plugin 28 | 29 | 1.8 30 | 1.8 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /tdd-fill-the-code-starting-from-test/src/main/java/com/danidemi/tutorial/tdd/calc/Calc.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.calc; 2 | 3 | public class Calc { 4 | 5 | String display; 6 | 7 | Calc(){ 8 | display = "0"; 9 | } 10 | 11 | public void press(char c) { 12 | 13 | if(display.length()==9) return; 14 | 15 | if(display.equals("0")){ 16 | display = "" + c; 17 | }else{ 18 | display = display + c; 19 | } 20 | } 21 | 22 | public String display() { 23 | return display; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tdd-helloworld/.gitignore: -------------------------------------------------------------------------------- 1 | /.classpath 2 | /.settings 3 | /.project 4 | /target 5 | *.iml -------------------------------------------------------------------------------- /tdd-helloworld/README.md: -------------------------------------------------------------------------------- 1 | # Hello World # 2 | 3 | Just show how to use JUnit to check that a String returned by an object is the expected one. -------------------------------------------------------------------------------- /tdd-helloworld/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | tdd-helloworld 6 | jar 7 | 8 | 9 | com.danidemi.tutorial.tdd 10 | tdd-parent 11 | 0.0.2-SNAPSHOT 12 | 13 | 14 | 15 | 16 | junit 17 | junit 18 | 19 | 20 | org.hamcrest 21 | hamcrest-library 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tdd-helloworld/src/main/java/com/danidemi/tutorial/tdd/helloworld/Greeter.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.helloworld; 2 | 3 | public class Greeter { 4 | 5 | public String sayHello(){ 6 | return "Hello World!"; 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tdd-helloworld/src/test/java/com/danidemi/tutorial/tdd/helloworld/GreeterTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.helloworld; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.hamcrest.Matchers.is; 5 | import static org.junit.Assert.*; 6 | 7 | import org.junit.Test; 8 | 9 | public class GreeterTest { 10 | 11 | @Test 12 | public void shouldSayHelloToTheWorld() { 13 | 14 | Greeter greeter = new Greeter(); 15 | 16 | String helloMsg = greeter.sayHello(); 17 | 18 | assertThat(helloMsg, equalTo("Hello World!")); 19 | 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /tdd-mocks/.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /lib/*.jar 3 | /.classpath 4 | /.project 5 | /.metadata 6 | /target 7 | /bin 8 | *.iml -------------------------------------------------------------------------------- /tdd-mocks/README.md: -------------------------------------------------------------------------------- 1 | # TDD Mocks Tutorial # 2 | 3 | This is a tutorial about using dinamically generated mock objects. -------------------------------------------------------------------------------- /tdd-mocks/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 35 | 36 | -------------------------------------------------------------------------------- /tdd-mocks/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | tdd-mocks 6 | jar 7 | 8 | 9 | com.danidemi.tutorial.tdd 10 | tdd-parent 11 | 0.0.2-SNAPSHOT 12 | 13 | 14 | 15 | 16 | 17 | org.dbunit 18 | dbunit 19 | 20 | 21 | junit 22 | junit 23 | 24 | 25 | org.hsqldb 26 | hsqldb 27 | 28 | 29 | org.slf4j 30 | slf4j-jdk14 31 | 32 | 33 | commons-io 34 | commons-io 35 | 36 | 37 | org.mockito 38 | mockito-core 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /tdd-mocks/src/main/java/com/danidemi/tutorial/tdd/mocks/AbstractDao.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | public class AbstractDao { 4 | 5 | protected JdbcConf jdbcConf; 6 | 7 | public AbstractDao(JdbcConf jdbcConf) { 8 | this.jdbcConf = jdbcConf; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /tdd-mocks/src/main/java/com/danidemi/tutorial/tdd/mocks/InvalidCredentialException.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | public class InvalidCredentialException extends Exception { 4 | 5 | public InvalidCredentialException(String string) { 6 | super(string); 7 | } 8 | 9 | public InvalidCredentialException() { 10 | super(); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /tdd-mocks/src/main/java/com/danidemi/tutorial/tdd/mocks/JdbcConf.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | public class JdbcConf { 4 | private String driverName; 5 | private String jdbcUrl; 6 | private String username; 7 | private String password; 8 | 9 | public JdbcConf(String driverName, String jdbcUrl, String username, 10 | String password) { 11 | this.driverName = driverName; 12 | this.jdbcUrl = jdbcUrl; 13 | this.username = username; 14 | this.password = password; 15 | } 16 | 17 | public String getDriverName() { 18 | return driverName; 19 | } 20 | 21 | public String getJdbcUrl() { 22 | return jdbcUrl; 23 | } 24 | 25 | public String getUsername() { 26 | return username; 27 | } 28 | 29 | public String getPassword() { 30 | return password; 31 | } 32 | } -------------------------------------------------------------------------------- /tdd-mocks/src/main/java/com/danidemi/tutorial/tdd/mocks/Login.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | public class Login { 4 | 5 | private TokenService tokenService; 6 | private UserDao userDao; 7 | 8 | public void setUserDao(UserDao userDao) { 9 | 10 | this.userDao = userDao; 11 | 12 | } 13 | 14 | public void setTokenService(TokenService tokenService) { 15 | 16 | this.tokenService = tokenService; 17 | 18 | } 19 | 20 | public User login(String username, String password, String token) throws 21 | InvalidCredentialException, 22 | UserLockedException { 23 | 24 | User user = userDao.findUserByUsername(username); 25 | if(user == null){ 26 | throw new InvalidCredentialException("User not specified"); 27 | } 28 | if(!user.passwordMatches(password)){ 29 | throw new InvalidCredentialException("Password does not match"); 30 | } 31 | if(user.isLocked()){ 32 | throw new UserLockedException(); 33 | } 34 | if(! tokenService.check(user.getTokenId(), token) ){ 35 | user.increaseFailuers(); 36 | if(user.getFailures()>=3){ 37 | user.lock(); 38 | userDao.saveOrUpdate(user); 39 | throw new UserLockedException(); 40 | }else{ 41 | userDao.saveOrUpdate(user); 42 | throw new InvalidCredentialException("Token does not match"); 43 | } 44 | } 45 | return user; 46 | 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /tdd-mocks/src/main/java/com/danidemi/tutorial/tdd/mocks/SqlSandwich.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.SQLException; 6 | 7 | abstract class SqlSandwich { 8 | 9 | public T execute(JdbcConf jdbcConf){ 10 | Connection connection = null; 11 | try { 12 | Class.forName(jdbcConf.getDriverName()); 13 | connection = DriverManager.getConnection(jdbcConf.getJdbcUrl(), jdbcConf.getUsername(), jdbcConf.getPassword()); 14 | return withConnection(connection); 15 | } catch (Exception e) { 16 | e.printStackTrace(); 17 | } finally { 18 | if(connection!=null){ 19 | try { 20 | connection.close(); 21 | } catch (SQLException e1) { 22 | e1.printStackTrace(); 23 | } 24 | } 25 | } 26 | return null; 27 | } 28 | 29 | abstract T withConnection(Connection conn) throws Exception; 30 | } -------------------------------------------------------------------------------- /tdd-mocks/src/main/java/com/danidemi/tutorial/tdd/mocks/TokenService.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | public class TokenService { 4 | 5 | public boolean check(long tokeId, String userCode) { 6 | return false; 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tdd-mocks/src/main/java/com/danidemi/tutorial/tdd/mocks/User.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | public abstract class User { 4 | 5 | protected Long id; 6 | protected String password; 7 | protected boolean isLocked; 8 | protected int failures; 9 | protected String username; 10 | 11 | public void setLocked(boolean locked) { 12 | this.isLocked = locked; 13 | } 14 | 15 | void setUsername(String string) { 16 | this.username = string; 17 | } 18 | 19 | void setPassword(String string) { 20 | this.password = string; 21 | } 22 | 23 | public void lock() { 24 | setLocked(true); 25 | } 26 | 27 | public long getTokenId() { 28 | return 0L; 29 | } 30 | 31 | public boolean passwordMatches(String password) { 32 | return this.password.equals(password); 33 | } 34 | 35 | public boolean isLocked() { 36 | return this.isLocked; 37 | } 38 | 39 | public void increaseFailuers() { 40 | failures++; 41 | } 42 | 43 | public int getFailures() { 44 | return failures; 45 | } 46 | 47 | public Long getId() { 48 | return id; 49 | } 50 | 51 | public String getUsername() { 52 | return username; 53 | } 54 | 55 | public String getPassword() { 56 | return password; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /tdd-mocks/src/main/java/com/danidemi/tutorial/tdd/mocks/UserDao.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | public interface UserDao { 4 | 5 | User findUserByUsername(String username); 6 | 7 | void saveOrUpdate(User user); 8 | 9 | User newUser(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /tdd-mocks/src/main/java/com/danidemi/tutorial/tdd/mocks/UserDaoImpl.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | import java.sql.Connection; 4 | import java.sql.PreparedStatement; 5 | import java.sql.ResultSet; 6 | 7 | public class UserDaoImpl extends AbstractDao implements UserDao { 8 | 9 | public UserDaoImpl(JdbcConf jdbcConf) { 10 | super(jdbcConf); 11 | } 12 | 13 | public User findUserByUsername(final String username) { 14 | 15 | return new SqlSandwich() { 16 | 17 | @Override 18 | User withConnection(Connection conn) throws Exception { 19 | PreparedStatement ps = conn.prepareStatement("SELECT username, password, is_locked FROM TDD_USER WHERE username=?"); 20 | ps.setString(1, username); 21 | ResultSet rs = ps.executeQuery(); 22 | if(rs.next()){ 23 | UserImpl u = new UserImpl(); 24 | u.setUsername( rs.getString("username") ); 25 | u.setLocked( rs.getBoolean("is_locked") ); 26 | u.setPassword( rs.getString("password") ); 27 | return u; 28 | } 29 | return null; 30 | } 31 | }.execute(jdbcConf); 32 | 33 | } 34 | 35 | 36 | public void saveOrUpdate(final User u) { 37 | 38 | final UserImpl user = (UserImpl) u; 39 | 40 | new SqlSandwich() { 41 | 42 | @Override 43 | Void withConnection(Connection conn) throws Exception { 44 | PreparedStatement ps; 45 | if(user.getId()!=null){ 46 | ps = conn.prepareStatement("UPDATE TDD_USER SET username=?, password=?,is_locked=? WHERE id=?"); 47 | ps.setString(1, user.getUsername()); 48 | ps.setString(2, user.getPassword()); 49 | ps.setBoolean(3, user.isLocked()); 50 | ps.setLong(4, user.getId()); 51 | ps.executeUpdate(); 52 | }else{ 53 | ps = conn.prepareStatement("INSERT INTO TDD_USER (username,password,is_locked) VALUES(?, ?, ?)", java.sql.Statement.RETURN_GENERATED_KEYS); 54 | ps.setString(1, user.getUsername()); 55 | ps.setString(2, user.getPassword()); 56 | ps.setBoolean(3, user.isLocked()); 57 | ps.executeUpdate(); 58 | ResultSet generatedKeys = ps.getGeneratedKeys(); 59 | generatedKeys.next(); 60 | user.setId( generatedKeys.getLong(1) ); 61 | } 62 | return null; 63 | } 64 | }.execute(jdbcConf); 65 | 66 | } 67 | 68 | 69 | public User newUser() { 70 | UserImpl userImpl = new UserImpl(); 71 | return userImpl; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /tdd-mocks/src/main/java/com/danidemi/tutorial/tdd/mocks/UserImpl.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | public class UserImpl extends User { 4 | 5 | public void setId(long long1) { 6 | this.id = long1; 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tdd-mocks/src/main/java/com/danidemi/tutorial/tdd/mocks/UserLockedException.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | public class UserLockedException extends Exception { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /tdd-mocks/src/test/java/com/danidemi/tutorial/tdd/mocks/LoginTest_1.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | import static org.junit.Assert.*; 4 | import static org.mockito.Mockito.*; 5 | 6 | import org.junit.Test; 7 | 8 | public class LoginTest_1 { 9 | 10 | /** 11 | * [1] This test check that all components are invoked as supposed in a 12 | * successfull scenario. 13 | * 14 | * @throws UserLockedException 15 | * @throws InvalidCredentialException 16 | */ 17 | @Test 18 | public void shouldSearchTheUserThroughDaoAndCheckItsTokenId_1() 19 | throws InvalidCredentialException, UserLockedException { 20 | 21 | // given 22 | TokenService tokenService = mock(TokenService.class); 23 | when(tokenService.check(1283L, "112358")).thenReturn(true); 24 | 25 | User user = mock(User.class); 26 | when(user.getTokenId()).thenReturn(1283L); 27 | when(user.passwordMatches(any(String.class))).thenReturn(true); 28 | 29 | UserDao userDao = mock(UserDao.class); 30 | when(userDao.findUserByUsername(anyString())).thenReturn(user); 31 | 32 | Login login = new Login(); 33 | login.setUserDao(userDao); 34 | login.setTokenService(tokenService); 35 | 36 | // when 37 | User u = login.login("username", "password", "112358"); 38 | 39 | // then 40 | verify(userDao).findUserByUsername("username"); 41 | verify(tokenService).check(1283L, "112358"); 42 | assertEquals(user, u); 43 | 44 | } 45 | 46 | /** 47 | * [2] throw exception if password check fail 48 | */ 49 | @Test 50 | public void shouldFailIfPasswordFail_2() { 51 | 52 | // given 53 | User user = mock(User.class); 54 | 55 | UserDao userDao = mock(UserDao.class); 56 | when(user.passwordMatches(anyString())).thenReturn(false); 57 | 58 | TokenService tokenService = mock(TokenService.class); 59 | when(tokenService.check(anyLong(), anyString())).thenReturn(false); 60 | 61 | when(userDao.findUserByUsername(anyString())).thenReturn(user); 62 | 63 | // when 64 | Login login = new Login(); 65 | login.setUserDao(userDao); 66 | login.setTokenService(tokenService); 67 | 68 | try { 69 | User u = login.login("username", "password", "112358"); 70 | fail("Exception expected"); 71 | } catch (InvalidCredentialException ice) { 72 | 73 | } catch (Exception e) { 74 | fail("Wrong exception"); 75 | } 76 | 77 | } 78 | 79 | /** 80 | * [3]<-[1] This test check that all components are invoked as supposed in a 81 | * successfull scenario. 82 | */ 83 | @Test 84 | public void shouldSearchTheUserThroughDaoAndCheckItsTokenId_3() 85 | throws Exception { 86 | 87 | // given 88 | TokenService tokenService = mock(TokenService.class); 89 | when(tokenService.check(1283L, "112358")).thenReturn(true); 90 | 91 | User user = mock(User.class); 92 | when(user.passwordMatches(anyString())).thenReturn(true); 93 | when(user.getTokenId()).thenReturn(1283L); 94 | 95 | UserDao userDao = mock(UserDao.class); 96 | when(userDao.findUserByUsername(anyString())).thenReturn(user); 97 | 98 | // when 99 | Login login = new Login(); 100 | login.setUserDao(userDao); 101 | login.setTokenService(tokenService); 102 | User u = login.login("username", "password", "112358"); 103 | 104 | // then 105 | verify(userDao).findUserByUsername("username"); 106 | verify(tokenService).check(1283L, "112358"); 107 | assertEquals(user, u); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /tdd-mocks/src/test/java/com/danidemi/tutorial/tdd/mocks/UserDaoDbTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tutorial.tdd.mocks; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | import static org.junit.Assert.assertNull; 5 | 6 | import java.io.InputStream; 7 | import java.sql.Connection; 8 | import java.sql.DriverManager; 9 | import java.sql.Statement; 10 | 11 | import org.apache.commons.io.IOUtils; 12 | import org.dbunit.database.DatabaseConnection; 13 | import org.dbunit.database.IDatabaseConnection; 14 | import org.dbunit.dataset.IDataSet; 15 | import org.dbunit.dataset.xml.XmlDataSet; 16 | import org.dbunit.operation.DatabaseOperation; 17 | import org.junit.BeforeClass; 18 | import org.junit.Test; 19 | 20 | public class UserDaoDbTest { 21 | 22 | private static JdbcConf jdbcConf; 23 | 24 | @BeforeClass 25 | public static void handleSetUpOperation() throws Exception { 26 | 27 | String createDatabaseSql = IOUtils.toString(UserDaoDbTest.class.getResource("/database.sql")); 28 | 29 | jdbcConf = new JdbcConf("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:aname", "sa", ""); 30 | 31 | Class.forName(jdbcConf.getDriverName()); 32 | Connection connection = DriverManager.getConnection(jdbcConf.getJdbcUrl(), jdbcConf.getUsername(), jdbcConf.getPassword()); 33 | Statement createStatement = connection.createStatement(); 34 | createStatement.addBatch(createDatabaseSql); 35 | createStatement.executeBatch(); 36 | 37 | final IDatabaseConnection conn = new DatabaseConnection(connection); 38 | 39 | String resourcePath = "/" + UserDaoDbTest.class.getSimpleName() + ".xml"; 40 | InputStream resourceAsStream = UserDaoDbTest.class 41 | .getResourceAsStream(resourcePath); 42 | if (resourceAsStream == null) { 43 | throw new NullPointerException(resourcePath + " not found"); 44 | } 45 | final IDataSet data = new XmlDataSet(resourceAsStream); 46 | 47 | try { 48 | DatabaseOperation.CLEAN_INSERT.execute(conn, data); 49 | } finally { 50 | conn.close(); 51 | } 52 | } 53 | 54 | @Test 55 | public void shouldFindAUserByUsername() { 56 | 57 | UserDao userDao = new UserDaoImpl(jdbcConf); 58 | User john = userDao.findUserByUsername("John"); 59 | assertNotNull(john); 60 | 61 | } 62 | 63 | @Test 64 | public void shouldReturnNullIfTheUserIsNotFound() { 65 | 66 | UserDao userDao = new UserDaoImpl(jdbcConf); 67 | User user = userDao.findUserByUsername("NOT_THERE"); 68 | assertNull(user); 69 | 70 | } 71 | 72 | @Test 73 | public void shouldCreateANewUser() { 74 | 75 | UserDao userDao = new UserDaoImpl(jdbcConf); 76 | User u = userDao.newUser(); 77 | u.setUsername("newuser"); 78 | u.setPassword("pwd"); 79 | userDao.saveOrUpdate(u); 80 | 81 | assertNotNull(u.getId()); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /tdd-mocks/src/test/resources/UserDaoDbTest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | username 5 | password 6 | is_locked 7 | 8 | John 9 | Doe 10 | false 11 | 12 | 13 | Lucas 14 | Manon 15 | true 16 | 17 | 18 | -------------------------------------------------------------------------------- /tdd-mocks/src/test/resources/data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | username 5 | password 6 | is_locked 7 | 8 | john 9 | doe 10 | true 11 | 12 | 13 | jean 14 | doe 15 | false 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tdd-mocks/src/test/resources/database.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE TDD_USER( 2 | id BIGINT IDENTITY, 3 | username VARCHAR(32) UNIQUE, 4 | password VARCHAR(32), 5 | is_locked BOOLEAN 6 | ); -------------------------------------------------------------------------------- /tdd-mocks/src/test/resources/tdddb.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE TDD_USER( 2 | id BIGINT IDENTITY, 3 | username VARCHAR(32) UNIQUE, 4 | password VARCHAR(32), 5 | is_locked BOOLEAN 6 | ); 7 | -------------------------------------------------------------------------------- /tdd-refactoring-done/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.classpath 3 | /.project 4 | /.settings 5 | *.iml -------------------------------------------------------------------------------- /tdd-refactoring-done/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | tdd-refactoring-done 9 | 10 | 11 | junit 12 | junit 13 | 14 | 15 | org.hamcrest 16 | hamcrest-library 17 | 18 | 19 | org.mockito 20 | mockito-core 21 | 22 | 23 | -------------------------------------------------------------------------------- /tdd-refactoring-done/src/main/java/com/danidemi/tdd/refactoringdone/ChildrenStatus.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | 3 | public class ChildrenStatus implements MovieStatus { 4 | 5 | @Override 6 | public double amount(int daysRented) { 7 | double thisAmount = 1.5; 8 | if (daysRented > 3) 9 | thisAmount += (daysRented - 3) * 1.5; 10 | return thisAmount; 11 | } 12 | 13 | @Override 14 | public int rentalPoint(int daysRented) { 15 | return 1; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /tdd-refactoring-done/src/main/java/com/danidemi/tdd/refactoringdone/Customer.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Enumeration; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | import java.util.Vector; 8 | 9 | class Customer extends DomainObject { 10 | 11 | private List rentals = new ArrayList<>(); 12 | 13 | public Customer(String name) { 14 | super(name); 15 | } 16 | 17 | public String statement() { 18 | 19 | double totalAmount = 0; 20 | int frequentRenterPoints = 0; 21 | String result = "Rental Record for " + name() + "\n"; 22 | 23 | for (Rental rental : rentals) { 24 | totalAmount += rental.amount(); 25 | frequentRenterPoints += rental.rentalPoints(); 26 | 27 | // show figures for this rental 28 | result += "\t" + rental.movieName() + "\t" 29 | + String.valueOf(rental.amount()) + "\n"; 30 | 31 | } 32 | 33 | // add footer lines 34 | result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; 35 | result += "You earned " + String.valueOf(frequentRenterPoints) 36 | + " frequent renter points"; 37 | return result; 38 | 39 | } 40 | 41 | public String htmlStatement() { 42 | 43 | double totalAmount = 0; 44 | int frequentRenterPoints = 0; 45 | String result = "Rental Record for " + name() + "\n"; 46 | 47 | for (Rental rental : rentals) { 48 | totalAmount += rental.amount(); 49 | frequentRenterPoints += rental.rentalPoints(); 50 | 51 | // show figures for this rental 52 | result += "\t" + rental.movieName() + "\t" 53 | + String.valueOf(rental.amount()) + "\n"; 54 | 55 | } 56 | 57 | // add footer lines 58 | result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; 59 | result += "You earned " + String.valueOf(frequentRenterPoints) 60 | + " frequent renter points"; 61 | return result; 62 | 63 | } 64 | 65 | public void addRental(Rental arg) { 66 | rentals.add(arg); 67 | } 68 | 69 | public static Customer get(String name) { 70 | return (Customer) Registrar.get("Customers", name); 71 | } 72 | 73 | public void persist() { 74 | Registrar.add("Customers", this); 75 | } 76 | 77 | 78 | } -------------------------------------------------------------------------------- /tdd-refactoring-done/src/main/java/com/danidemi/tdd/refactoringdone/DomainObject.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | 3 | /** 4 | * DomainObject is a general class that does a few standard things, such as hold a name. 5 | */ 6 | public class DomainObject { 7 | 8 | protected String name = "no name"; 9 | 10 | public DomainObject (String name) { 11 | this.name = name; 12 | }; 13 | 14 | public DomainObject () {}; 15 | 16 | public String name () { 17 | return name; 18 | }; 19 | 20 | public String toString() { 21 | return name; 22 | }; 23 | 24 | } -------------------------------------------------------------------------------- /tdd-refactoring-done/src/main/java/com/danidemi/tdd/refactoringdone/JustReleasedStatus.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | 3 | public class JustReleasedStatus implements MovieStatus { 4 | 5 | @Override 6 | public double amount(int daysRented) { 7 | return daysRented * 3; 8 | } 9 | 10 | @Override 11 | public int rentalPoint(int daysRented) { 12 | return daysRented > 1 ? 2 : 1; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tdd-refactoring-done/src/main/java/com/danidemi/tdd/refactoringdone/Movie.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | 3 | /** 4 | * Movie represents the notion of a film. A video store might have several tapes 5 | * in stock of the same movie. The movie uses a class called a registrar (not 6 | * shown) as a class to hold instances of movie. I also do this with other 7 | * classes. I use the message persist to tell an object to save itself to the 8 | * registrar. I can then retrieve the object, based on its name, with a 9 | * get(String) method. 10 | */ 11 | public class Movie extends DomainObject { 12 | 13 | private static final int CHILDRENS = 2; 14 | private static final int REGULAR = 0; 15 | private static final int NEW_RELEASE = 1; 16 | 17 | private MovieStatus status; 18 | 19 | 20 | private Movie(String name, MovieStatus initialStatus) { 21 | super(name); 22 | this.status = initialStatus; 23 | } 24 | 25 | public static Movie newChildrenMovie(String name) { 26 | return new Movie(name, new ChildrenStatus()); 27 | } 28 | 29 | public static Movie newRegularMovie(String name) { 30 | return new Movie(name, new RegularStatus()); 31 | } 32 | 33 | public static Movie newJustReleasedMovie(String name) { 34 | return new Movie(name, new JustReleasedStatus()); 35 | } 36 | 37 | public void beForChildren() { 38 | this.status = new ChildrenStatus(); 39 | } 40 | 41 | public void beRegular() { 42 | this.status = new RegularStatus(); 43 | } 44 | 45 | public void persist() { 46 | Registrar.add("Movies", this); 47 | } 48 | 49 | double amount(int daysRented) { 50 | return status.amount(daysRented); 51 | } 52 | 53 | // double amount(int daysRented) { 54 | // double thisAmount = 0; 55 | // switch (priceCode()) { 56 | // case Movie.REGULAR: 57 | // thisAmount += 2; 58 | // if (daysRented > 2) 59 | // thisAmount += (daysRented - 2) * 1.5; 60 | // break; 61 | // case Movie.NEW_RELEASE: 62 | // thisAmount += daysRented * 3; 63 | // break; 64 | // case Movie.CHILDRENS: 65 | // thisAmount += 1.5; 66 | // if (daysRented > 3) 67 | // thisAmount += (daysRented - 3) * 1.5; 68 | // break; 69 | // } 70 | // return thisAmount; 71 | // } 72 | 73 | int rentalPoint(int daysRented) { 74 | return this.status.rentalPoint(daysRented); 75 | } 76 | 77 | // int rentalPoint(int daysRented) { 78 | // int pointToAdd = 0; 79 | // 80 | // // add frequent renter points 81 | // pointToAdd++; 82 | // // add bonus for a two day new release rental 83 | // if ((priceCode() == Movie.NEW_RELEASE) 84 | // && daysRented > 1) 85 | // pointToAdd++; 86 | // return pointToAdd; 87 | // } 88 | 89 | public static Movie get(String name) { 90 | return (Movie) Registrar.get("Movies", name); 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /tdd-refactoring-done/src/main/java/com/danidemi/tdd/refactoringdone/MovieStatus.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | 3 | public interface MovieStatus { 4 | 5 | double amount(int daysRented); 6 | 7 | int rentalPoint(int daysRented); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tdd-refactoring-done/src/main/java/com/danidemi/tdd/refactoringdone/Registrar.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | 3 | public class Registrar { 4 | 5 | public static void add(String string, Movie movie) { 6 | // TODO Auto-generated method stub 7 | 8 | } 9 | 10 | public static DomainObject get(String string, String name) { 11 | // TODO Auto-generated method stub 12 | return null; 13 | } 14 | 15 | public static void add(String string, Customer customer) { 16 | // TODO Auto-generated method stub 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /tdd-refactoring-done/src/main/java/com/danidemi/tdd/refactoringdone/RegularStatus.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | 3 | public class RegularStatus implements MovieStatus { 4 | 5 | @Override 6 | public double amount(int daysRented) { 7 | double thisAmount = 2; 8 | if (daysRented > 2) 9 | thisAmount += (daysRented - 2) * 1.5; 10 | return thisAmount; 11 | } 12 | 13 | @Override 14 | public int rentalPoint(int daysRented) { 15 | return 1; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /tdd-refactoring-done/src/main/java/com/danidemi/tdd/refactoringdone/Render.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | 3 | public class Render { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /tdd-refactoring-done/src/main/java/com/danidemi/tdd/refactoringdone/Rental.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | 3 | /** 4 | * The rental class represents a customer renting a movie. 5 | */ 6 | class Rental extends DomainObject { 7 | 8 | private Tape tape; 9 | private int daysRented; 10 | 11 | public Rental(Tape tape, int daysRented) { 12 | this.tape = tape; 13 | this.daysRented = daysRented; 14 | } 15 | 16 | public int daysRented() { 17 | return daysRented; 18 | } 19 | 20 | public Tape tape() { 21 | return tape; 22 | } 23 | 24 | double amount() { 25 | return this.tape().movie().amount(this.daysRented()); 26 | } 27 | 28 | int rentalPoints() { 29 | return tape().movie().rentalPoint(this.daysRented()); 30 | } 31 | 32 | public String movieName() { 33 | return tape().movie().name(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /tdd-refactoring-done/src/main/java/com/danidemi/tdd/refactoringdone/Tape.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | /** 3 | * The tape class represents a physical tape. 4 | */ 5 | public class Tape extends DomainObject { 6 | 7 | private String serialNumber; 8 | private Movie movie; 9 | 10 | public Tape(String serialNumber, Movie movie) { 11 | this.serialNumber = serialNumber; 12 | this.movie = movie; 13 | } 14 | 15 | public Movie movie() { 16 | return movie; 17 | } 18 | 19 | 20 | } -------------------------------------------------------------------------------- /tdd-refactoring-done/src/test/java/com/danidemi/tdd/refactoringdone/CustomerTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | import static org.hamcrest.Matchers.*; 3 | import static org.junit.Assert.*; 4 | import static org.mockito.Mockito.RETURNS_DEEP_STUBS; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.when; 7 | 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | import org.junit.rules.ExpectedException; 11 | 12 | public class CustomerTest { 13 | 14 | @Rule public ExpectedException asException = ExpectedException.none(); 15 | 16 | @Test 17 | public void testRegularFor3Days() { 18 | 19 | // given 20 | Rental rental = mockARegularRental("The Empire Strikes Back", 3); 21 | 22 | // when 23 | Customer customer = new Customer("John"); 24 | customer.addRental( rental ); 25 | String statement = customer.statement(); 26 | 27 | // then 28 | assertThat( statement, equalTo("Rental Record for John\n\tThe Empire Strikes Back\t3.5\nAmount owed is 3.5\nYou earned 1 frequent renter points")); 29 | 30 | } 31 | 32 | @Test 33 | public void testRegularFor2Days() { 34 | 35 | // given 36 | Rental rental = mockARegularRental("The Empire Strikes Back", 2); 37 | 38 | // when 39 | Customer customer = new Customer("John"); 40 | customer.addRental( rental ); 41 | String statement = customer.statement(); 42 | 43 | // then 44 | assertThat( statement, equalTo("Rental Record for John\n\tThe Empire Strikes Back\t2.0\nAmount owed is 2.0\nYou earned 1 frequent renter points")); 45 | 46 | } 47 | 48 | @Test 49 | public void testRegularFor1Days() { 50 | 51 | // given 52 | Rental rental = mockARegularRental("The Empire Strikes Back", 1); 53 | 54 | // when 55 | Customer customer = new Customer("John"); 56 | customer.addRental( rental ); 57 | String statement = customer.statement(); 58 | 59 | // then 60 | assertThat( statement, equalTo("Rental Record for John\n\tThe Empire Strikes Back\t2.0\nAmount owed is 2.0\nYou earned 1 frequent renter points")); 61 | 62 | } 63 | 64 | @Test 65 | public void testLongNewRelease() { 66 | 67 | // given 68 | Rental rental = mockANewReleaseRental("The Empire Strikes Back", 2); 69 | 70 | // when 71 | Customer customer = new Customer("Mark"); 72 | customer.addRental( rental ); 73 | String statement = customer.statement(); 74 | 75 | // then 76 | assertThat( statement, equalTo("Rental Record for Mark\n\tThe Empire Strikes Back\t6.0\nAmount owed is 6.0\nYou earned 2 frequent renter points")); 77 | 78 | } 79 | 80 | @Test 81 | public void testShortNewRelease() { 82 | 83 | // given 84 | Rental rental = mockANewReleaseRental("The Empire Strikes Back", 1); 85 | 86 | // when 87 | Customer customer = new Customer("Mark"); 88 | customer.addRental( rental ); 89 | String statement = customer.statement(); 90 | 91 | // then 92 | assertThat( statement, equalTo("Rental Record for Mark\n\tThe Empire Strikes Back\t3.0\nAmount owed is 3.0\nYou earned 1 frequent renter points")); 93 | 94 | } 95 | 96 | @Test 97 | public void testShortChildren() { 98 | 99 | // given 100 | Rental rental = mockAChildrenRental("Whitesnow", 2); 101 | 102 | // when 103 | Customer customer = new Customer("Mark"); 104 | customer.addRental( rental ); 105 | String statement = customer.statement(); 106 | 107 | // then 108 | assertThat( statement, equalTo("Rental Record for Mark\n\tWhitesnow\t1.5\nAmount owed is 1.5\nYou earned 1 frequent renter points")); 109 | 110 | } 111 | 112 | @Test 113 | public void testLongChildren() { 114 | 115 | // given 116 | Rental rental = mockAChildrenRental( "Madagascar", 4); 117 | 118 | // when 119 | Customer customer = new Customer("Mark"); 120 | customer.addRental( rental ); 121 | String statement = customer.statement(); 122 | 123 | // then 124 | assertThat( statement, equalTo("Rental Record for Mark\n\tMadagascar\t3.0\nAmount owed is 3.0\nYou earned 1 frequent renter points")); 125 | 126 | } 127 | 128 | private Rental mockARegularRental(String title, int daysRented) { 129 | return mockARental(daysRented, Movie.newRegularMovie(title)); 130 | } 131 | 132 | private Rental mockAChildrenRental(String title, int daysRented) { 133 | return mockARental(daysRented, Movie.newChildrenMovie(title)); 134 | } 135 | 136 | private Rental mockANewReleaseRental(String title, int daysRented) { 137 | return mockARental(daysRented, Movie.newJustReleasedMovie(title)); 138 | } 139 | 140 | 141 | private Rental mockARental(int daysRented, Movie movie) { 142 | Tape tape = new Tape("00000", movie); 143 | Rental rental = new Rental(tape, daysRented); 144 | 145 | return rental; 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /tdd-refactoring-done/src/test/java/com/danidemi/tdd/refactoringdone/MovieTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoringdone; 2 | 3 | import org.junit.Test; 4 | import static org.hamcrest.Matchers.*; 5 | import static org.junit.Assert.*; 6 | public class MovieTest { 7 | 8 | @Test 9 | public void shouldChangeFromNewReleaseToChildrenLongRental() { 10 | 11 | // given 12 | Movie tested = Movie.newJustReleasedMovie("Cinderella"); 13 | 14 | // when 15 | double amountAsNewRelease = tested.amount(10); 16 | tested.beForChildren(); 17 | double amountAsForChildren = tested.amount(10); 18 | 19 | // then 20 | assertThat( amountAsNewRelease, equalTo( 3 * 10.0 ) ); 21 | assertThat( amountAsForChildren, equalTo( 1.5 + 1.5 * 7 ) ); 22 | 23 | } 24 | 25 | @Test 26 | public void shouldChangeFromNewReleaseToChildrenShortRental() { 27 | 28 | // given 29 | Movie tested = Movie.newJustReleasedMovie("Cinderella"); 30 | 31 | // when 32 | double amountAsNewRelease = tested.amount(2); 33 | tested.beForChildren(); 34 | double amountAsForChildren = tested.amount(2); 35 | 36 | // then 37 | assertThat( amountAsNewRelease, equalTo( 3.0 * 2.0 ) ); 38 | assertThat( amountAsForChildren, equalTo( 1.5 ) ); 39 | 40 | } 41 | 42 | @Test 43 | public void shouldChangeFromNewReleaseToRegularShortRental() { 44 | 45 | // given 46 | Movie tested = Movie.newJustReleasedMovie("Star Wars Episode VII"); 47 | 48 | // when 49 | double amountAsNewRelease = tested.amount(2); 50 | tested.beRegular(); 51 | double amountAsRegular = tested.amount(2); 52 | 53 | // then 54 | assertThat( amountAsNewRelease, equalTo( 3.0 * 2.0 ) ); 55 | assertThat( amountAsRegular, equalTo( 2.0 ) ); 56 | 57 | } 58 | 59 | @Test 60 | public void shouldChangeFromNewReleaseToRegularLongRental() { 61 | 62 | // given 63 | Movie tested = Movie.newJustReleasedMovie("Star Wars Episode VII"); 64 | 65 | // when 66 | double amountAsNewRelease = tested.amount(3); 67 | tested.beRegular(); 68 | double amountAsRegular = tested.amount(3); 69 | 70 | // then 71 | assertThat( amountAsNewRelease, equalTo( 3.0 * 3 ) ); 72 | assertThat( amountAsRegular, equalTo( 2.0 + 1.5 * 1 ) ); 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /tdd-refactoring/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.classpath 3 | /.project 4 | /.settings 5 | *.iml -------------------------------------------------------------------------------- /tdd-refactoring/README: -------------------------------------------------------------------------------- 1 | Inspired by http://www.cs.unc.edu/~stotts/723/refactor/chap1.html 2 | 3 | - Problem: we want an output in HTML and the charging rule could change. 4 | 5 | - That long statement routine in the Customer does far too much. Many of the things that it does should really be done by the other classes. 6 | - Extracting the Amount Calculation (extract method) 7 | - Changing variables name 8 | - Moving the amount calculation from Customer to Rental 9 | - Removing "temp variable" with "query", clearer meaning. 10 | - Extracting Frequent Rental Points 11 | - Move method to Rental 12 | - Removing "temp variable" with "query", clearer meaning. 13 | - htmlStatement() 14 | - Moving the Rental Calculations to Movie 15 | - frequent renter points moved to Movie 16 | - static constructors to hide constants 17 | - inheritance & state pattern -------------------------------------------------------------------------------- /tdd-refactoring/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.danidemi.tutorial.tdd 5 | tdd-parent 6 | 0.0.2-SNAPSHOT 7 | 8 | tdd-refactoring 9 | 10 | 11 | junit 12 | junit 13 | 14 | 15 | org.hamcrest 16 | hamcrest-library 17 | 18 | 19 | -------------------------------------------------------------------------------- /tdd-refactoring/src/main/java/com/danidemi/tdd/refactoring/Customer.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoring; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Enumeration; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | import java.util.Vector; 8 | 9 | class Customer extends DomainObject { 10 | 11 | private List rentals = new ArrayList<>(); 12 | 13 | public Customer(String name) { 14 | super(name); 15 | } 16 | 17 | public String statement() { 18 | double totalAmount = 0; 19 | int frequentRenterPoints = 0; 20 | Iterator rentalsIt = rentals.iterator(); 21 | String result = "Rental Record for " + name() + "\n"; 22 | while (rentalsIt.hasNext()) { 23 | double thisAmount = 0; 24 | Rental each = rentalsIt.next(); 25 | 26 | // determine amounts for each line 27 | switch (each.tape().movie().priceCode()) { 28 | case Movie.REGULAR: 29 | thisAmount += 2; 30 | if (each.daysRented() > 2) 31 | thisAmount += (each.daysRented() - 2) * 1.5; 32 | break; 33 | case Movie.NEW_RELEASE: 34 | thisAmount += each.daysRented() * 3; 35 | break; 36 | case Movie.CHILDRENS: 37 | thisAmount += 1.5; 38 | if (each.daysRented() > 3) 39 | thisAmount += (each.daysRented() - 3) * 1.5; 40 | break; 41 | 42 | } 43 | totalAmount += thisAmount; 44 | 45 | // add frequent renter points 46 | frequentRenterPoints++; 47 | // add bonus for a two day new release rental 48 | if ((each.tape().movie().priceCode() == Movie.NEW_RELEASE) 49 | && each.daysRented() > 1) 50 | frequentRenterPoints++; 51 | 52 | // show figures for this rental 53 | result += "\t" + each.tape().movie().name() + "\t" 54 | + String.valueOf(thisAmount) + "\n"; 55 | 56 | } 57 | // add footer lines 58 | result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; 59 | result += "You earned " + String.valueOf(frequentRenterPoints) 60 | + " frequent renter points"; 61 | return result; 62 | 63 | } 64 | 65 | public void addRental(Rental arg) { 66 | if(arg.tape().movie().priceCode() < 0 || arg.tape().movie().priceCode() > 3){ 67 | throw new IllegalArgumentException("Wrong movie price code"); 68 | } 69 | rentals.add(arg); 70 | } 71 | 72 | public static Customer get(String name) { 73 | return (Customer) Registrar.get("Customers", name); 74 | } 75 | 76 | public void persist() { 77 | Registrar.add("Customers", this); 78 | } 79 | 80 | 81 | } -------------------------------------------------------------------------------- /tdd-refactoring/src/main/java/com/danidemi/tdd/refactoring/DomainObject.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoring; 2 | 3 | /** 4 | * DomainObject is a general class that does a few standard things, such as hold a name. 5 | */ 6 | public class DomainObject { 7 | 8 | protected String name = "no name"; 9 | 10 | public DomainObject (String name) { 11 | this.name = name; 12 | }; 13 | 14 | public DomainObject () {}; 15 | 16 | public String name () { 17 | return name; 18 | }; 19 | 20 | public String toString() { 21 | return name; 22 | }; 23 | 24 | } -------------------------------------------------------------------------------- /tdd-refactoring/src/main/java/com/danidemi/tdd/refactoring/Movie.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoring; 2 | 3 | /** 4 | * Movie represents the notion of a film. A video store might have several tapes 5 | * in stock of the same movie. The movie uses a class called a registrar (not 6 | * shown) as a class to hold instances of movie. I also do this with other 7 | * classes. I use the message persist to tell an object to save itself to the 8 | * registrar. I can then retrieve the object, based on its name, with a 9 | * get(String) method. 10 | */ 11 | public class Movie extends DomainObject { 12 | 13 | public static final int CHILDRENS = 2; 14 | public static final int REGULAR = 0; 15 | public static final int NEW_RELEASE = 1; 16 | 17 | private int priceCode; 18 | 19 | public Movie(String name, int priceCode) { 20 | super(name); 21 | this.priceCode = priceCode; 22 | } 23 | 24 | public int priceCode() { 25 | return priceCode; 26 | } 27 | 28 | public void persist() { 29 | Registrar.add("Movies", this); 30 | } 31 | 32 | public static Movie get(String name) { 33 | return (Movie) Registrar.get("Movies", name); 34 | } 35 | } -------------------------------------------------------------------------------- /tdd-refactoring/src/main/java/com/danidemi/tdd/refactoring/Registrar.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoring; 2 | 3 | public class Registrar { 4 | 5 | public static void add(String string, Movie movie) { 6 | // TODO Auto-generated method stub 7 | 8 | } 9 | 10 | public static DomainObject get(String string, String name) { 11 | // TODO Auto-generated method stub 12 | return null; 13 | } 14 | 15 | public static void add(String string, Customer customer) { 16 | // TODO Auto-generated method stub 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /tdd-refactoring/src/main/java/com/danidemi/tdd/refactoring/Rental.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoring; 2 | 3 | /** 4 | * The rental class represents a customer renting a movie. 5 | */ 6 | class Rental extends DomainObject { 7 | 8 | private Tape tape; 9 | private int daysRented; 10 | 11 | public Rental(Tape tape, int daysRented) { 12 | this.tape = tape; 13 | this.daysRented = daysRented; 14 | } 15 | 16 | public int daysRented() { 17 | return daysRented; 18 | } 19 | 20 | public Tape tape() { 21 | return tape; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /tdd-refactoring/src/main/java/com/danidemi/tdd/refactoring/Tape.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoring; 2 | /** 3 | * The tape class represents a physical tape. 4 | */ 5 | public class Tape extends DomainObject { 6 | 7 | private String serialNumber; 8 | private Movie movie; 9 | 10 | public Tape(String serialNumber, Movie movie) { 11 | this.serialNumber = serialNumber; 12 | this.movie = movie; 13 | } 14 | 15 | public Movie movie() { 16 | return movie; 17 | } 18 | 19 | 20 | } -------------------------------------------------------------------------------- /tdd-refactoring/src/test/java/com/danidemi/tdd/refactoring/CustomerTest.java: -------------------------------------------------------------------------------- 1 | package com.danidemi.tdd.refactoring; 2 | import static org.hamcrest.Matchers.*; 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | import org.junit.rules.ExpectedException; 8 | 9 | 10 | 11 | public class CustomerTest { 12 | 13 | @Rule public ExpectedException asException = ExpectedException.none(); 14 | 15 | @Test 16 | public void testRegularFor3Days() { 17 | 18 | // given 19 | Rental rental = mockARental(Movie.REGULAR, "The Empire Strikes Back", 3); 20 | 21 | // when 22 | Customer customer = new Customer("John"); 23 | customer.addRental( rental ); 24 | String statement = customer.statement(); 25 | 26 | // then 27 | assertThat( statement, equalTo("Rental Record for John\n\tThe Empire Strikes Back\t3.5\nAmount owed is 3.5\nYou earned 1 frequent renter points")); 28 | 29 | } 30 | 31 | @Test 32 | public void testRegularFor2Days() { 33 | 34 | // given 35 | Rental rental = mockARental(Movie.REGULAR, "The Empire Strikes Back", 2); 36 | 37 | // when 38 | Customer customer = new Customer("John"); 39 | customer.addRental( rental ); 40 | String statement = customer.statement(); 41 | 42 | // then 43 | assertThat( statement, equalTo("Rental Record for John\n\tThe Empire Strikes Back\t2.0\nAmount owed is 2.0\nYou earned 1 frequent renter points")); 44 | 45 | } 46 | 47 | @Test 48 | public void testRegularFor1Days() { 49 | 50 | // given 51 | Rental rental = mockARental(Movie.REGULAR, "The Empire Strikes Back", 1); 52 | 53 | // when 54 | Customer customer = new Customer("John"); 55 | customer.addRental( rental ); 56 | String statement = customer.statement(); 57 | 58 | // then 59 | assertThat( statement, equalTo("Rental Record for John\n\tThe Empire Strikes Back\t2.0\nAmount owed is 2.0\nYou earned 1 frequent renter points")); 60 | 61 | } 62 | 63 | @Test 64 | public void testLongNewRelease() { 65 | 66 | // given 67 | Rental rental = mockARental(Movie.NEW_RELEASE, "The Empire Strikes Back", 2); 68 | 69 | // when 70 | Customer customer = new Customer("Mark"); 71 | customer.addRental( rental ); 72 | String statement = customer.statement(); 73 | 74 | // then 75 | assertThat( statement, equalTo("Rental Record for Mark\n\tThe Empire Strikes Back\t6.0\nAmount owed is 6.0\nYou earned 2 frequent renter points")); 76 | 77 | } 78 | 79 | @Test 80 | public void testShortNewRelease() { 81 | 82 | // given 83 | Rental rental = mockARental(Movie.NEW_RELEASE, "The Empire Strikes Back", 1); 84 | 85 | // when 86 | Customer customer = new Customer("Mark"); 87 | customer.addRental( rental ); 88 | String statement = customer.statement(); 89 | 90 | // then 91 | assertThat( statement, equalTo("Rental Record for Mark\n\tThe Empire Strikes Back\t3.0\nAmount owed is 3.0\nYou earned 1 frequent renter points")); 92 | 93 | } 94 | 95 | @Test 96 | public void testShortChildren() { 97 | 98 | // given 99 | Rental rental = mockARental(Movie.CHILDRENS, "Whitesnow", 2); 100 | 101 | // when 102 | Customer customer = new Customer("Mark"); 103 | customer.addRental( rental ); 104 | String statement = customer.statement(); 105 | 106 | // then 107 | assertThat( statement, equalTo("Rental Record for Mark\n\tWhitesnow\t1.5\nAmount owed is 1.5\nYou earned 1 frequent renter points")); 108 | 109 | } 110 | 111 | @Test 112 | public void testLongChildren() { 113 | 114 | // given 115 | Rental rental = mockARental(Movie.CHILDRENS, "Madagascar", 4); 116 | 117 | // when 118 | Customer customer = new Customer("Mark"); 119 | customer.addRental( rental ); 120 | String statement = customer.statement(); 121 | 122 | // then 123 | assertThat( statement, equalTo("Rental Record for Mark\n\tMadagascar\t3.0\nAmount owed is 3.0\nYou earned 1 frequent renter points")); 124 | 125 | } 126 | 127 | @Test 128 | public void shouldNotAcceptIllegalCategory() { 129 | 130 | // given 131 | Rental rental = mockARental(Integer.MAX_VALUE, "Madagascar", 4); 132 | Customer customer = new Customer("Mark"); 133 | 134 | // when 135 | asException.expect(IllegalArgumentException.class); 136 | customer.addRental( rental ); 137 | 138 | } 139 | 140 | private Rental mockARental(int priceCode, String title, int daysRented) { 141 | 142 | Movie movie = new Movie(title, priceCode); 143 | Tape tape = new Tape("00000", movie); 144 | Rental rental = new Rental(tape, daysRented); 145 | 146 | return rental; 147 | 148 | } 149 | 150 | } 151 | --------------------------------------------------------------------------------
Rental Record for " + name() + "\n"; 46 | 47 | for (Rental rental : rentals) { 48 | totalAmount += rental.amount(); 49 | frequentRenterPoints += rental.rentalPoints(); 50 | 51 | // show figures for this rental 52 | result += "\t" + rental.movieName() + "\t" 53 | + String.valueOf(rental.amount()) + "\n"; 54 | 55 | } 56 | 57 | // add footer lines 58 | result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; 59 | result += "You earned " + String.valueOf(frequentRenterPoints) 60 | + " frequent renter points