2. AGENDA
I. What is TDD
II. Benefits
III.Limitations
IV. TDD on Play Framework
3. WHAT IS TDD
Test-Driven Development (or test driven
design) is a methodology.
Common TDD misconception:
TDD is not about testing
TDD is about design and development
By testing first you design your code
Short development iterations
Based on requirement and pre-written test cases
Produces code necessary to pass that iteration's test
Refactor both code and tests
4. HOW DOES TDD HELP
I. TDD helps you produce clean working code
that fulfills requirements
II. Write Test Code
A. Code that fulfills requirements
III.Write Functional Code
A. Working code that fulfills requirements
IV. Refactor
A. Clean working code that fulfills
requirements
6. TDD BASICS - UNIT TESTING
Red, Green, Refactor
I. Make it Fail
A. No code without a failing test
II. Make it Work
A. As simply as possible
III.Make it Better
A. Refactor
7. BENEFITS
Confidence in change
Increase confidence in code
Fearlessly change your code
Document requirements
Discover usability issues early
Regression testing = Stable software =
Quality software
9. We need know about
I. Interfaces
II. Dependency Injection
III.Mock
IV. The builder and fluent interface patterns.
V. Refactoring
10. TOP FIVE EXCUSES FOR NOT UNIT
TESTI. Don’t have time to unit test.
II. The client pays me to develop code, not write
unit test.
III.I am supporting a legacy application without
unit tests.
IV. QA and User Acceptance Testing is far more
effective in finding bugs.
V. I don’t know how to unit test, or I don’t know
how to write good unit tests.
11. Play Test
Sbt test
Run all test classes
Sbt testOnly namespace.classTest
Run for only classTest
Change database when running test
Modify build.sbt
javaOptions in Test += "-
Dconfig.file=conf/application.test.conf"
12. We have
I. Unit Test -> Apply
II. Functional Test -> Apply
III.Selenium Test -> Optional
14. Application.conf
# Database configuration
# ~~~~~
# You can declare as many datasources as you want.
# By convention, the default datasource is named `default`
db.default.driver=com.mysql.jdbc.Driver
db.default.url="jdbc:mysql://xx.x.x.xx:xxxx/yyy"
db.default.username=xxx
db.default.password="xxx"
db.default.logSql=true
15. DAO Class
public class CompanyRepository {
public CompletionStage<Map<String, String>> options() {
return supplyAsync(() ->
ebeanServer.find(Company.class).orderBy("name").findList(),
executionContext).thenApply(list -> {
HashMap<String, String> options = new LinkedHashMap<String, String>();
for (Company c : list) {
options.put(c.id.toString(), c.getName());
}
return options;
});
}
}
16. Test DAO Class
sbt testOnly respository.CompanyRepositoryTest
public class CompanyRepositoryTest extends WithApplication {
@Test
public void options() throws Exception {
final CompanyRepository companyRepository =
app.injector().instanceOf(CompanyRepository.class);
final CompletionStage<Map<String, String>> stage =
companyRepository.options();
await().atMost(1, SECONDS).until(() ->
assertThat(stage.toCompletableFuture()).isCompletedWithValueMatching(companyMap -> {
return companyMap.size() == 42;
})
);
}
}
17. Controller Class
public class HomeController extends Controller {
public CompletionStage<Result> edit(Long id) {
CompletionStage<Map<String, String>> companiesFuture =
companyRepository.options();
return
computerRepository.lookup(id).thenCombineAsync(companiesFuture, (computerOptional,
companies) -> {
Computer c = computerOptional.get();
Form<Computer> computerForm =
formFactory.form(Computer.class).fill(c);
return ok(views.html.computer.editForm.render(id, computerForm,
companies));
}, httpExecutionContext.current());
}
18. Test Controller Class
public class HomeControllerTest extends WithApplication {
ComputerRepository computerRepository =
mock(ComputerRepository.class);
@Override
protected Application provideApplication() {
return new
GuiceApplicationBuilder().overrides(bind(ComputerRepository.class).toInstance(computerRe
pository)).build();
}
@Test
public void edit() {
final ComputerRepository computerRepository =
app.injector().instanceOf(ComputerRepository.class);
CompletionStage<Optional<Computer>> computer =
CompletableFuture.supplyAsync(()->{
return Optional.of(new Computer());
});
when(computerRepository.lookup(21L)).thenReturn(computer);
final CompletionStage<Optional<Computer>> stage =
computerRepository.lookup(21L);
19. View template
@main("404 - Page Not Found"){
<div class="jumbotron">
<h1>404</h1>
<h3>Page Not Found</h3>
</div>
20. Test View Template
public class Errors_404Test {
@Test
public void renderTemplate() {
HttpExecutionContext httpExecutionContext =
app.injector().instanceOf(HttpExecutionContext.class);
supplyAsync(()-> {
Content html = views.html.errors._404.render();
assertEquals("text/html", html.contentType());
assertTrue(contentAsString(html).contains("Page Not Found"));
return 0;
}, httpExecutionContext.current());
}
}
21. FunctionalTest
public class FunctionalTest extends WithApplication {
@Test
public void redirectHomePage() {
Result result = route(app, controllers.routes.HomeController.index());
assertThat(result.status()).isEqualTo(SEE_OTHER);
assertThat(result.redirectLocation().get()).isEqualTo("/computers");
}
....