POTD 10: TDD Web App

Due 22-Nov-2025, 11am EST
Purpose:

You are encouraged to work through the TDD in process in your favorite IDE. Learning TDD is best accomplished by doing TDD.


You may work alone or with at most two other students in this course (feel free to make use of any communication channels of your choice).

You may use any web programming languages and/or any web servers of your choice -- as long as you use TDD to complete this potd.


Require (for tests): Java 8 (or higher), JUnit (you are recommended to use the latest version, JUnit 5), Selenium.

Require (for web deployment): XAMPP or any web server of your choice, a web browser

Additional resources:


You will use tests as guidelines to design and develop a small web software.

For this activity, we will stop periodically, and do group work one step at a time with discussion between steps.

Imagine you are developing a simple ToDo web program (sample screen shown below), using TDD.
Image showing a sample screen for ToDo program

Normally, the first step in web development is getting your web framework installed, configured, and setup — "Download this, install that, configure those, run the script ...." — but TDD requires a different mindset. When you are doing TDD, the first step is to "test first."

First, we write the test, run it and check that it fails as expected. Then we write code (build part of our app) to pass the test.

Another thing is that, in TDD, we only do one step at a time.

We are going to proceed with small steps. Assuming, we will build a ToDo app written in PHP, and hosted on XAMPP — You may use any web programming language or any web server of your choice. With the time constraints in class and to simplify the implementation, we pick PHP because it is a simple web programming language to learn and get start; we use XAMPP because it is a web server that is simple and quick to set up for PHP (and it comes with MariaDB, which is a relational database management system — allowing us to expand the scope of our software).

Note: The following shows the tasks you will perform. For each task, an example is provided. You may include the example as part of your development. You should come up with at least one (of your own) user story and follow the TDD process to fulfill your user story.

Please also note: The example requirements are simplified and incomplete. Feel free to add or modify the requirements as appropriate.


Task 1: Functionality and user story

Work with your team. Think about a few main functionalities of a ToDo software. Write one user story for each functionality.

Examples:

How about update or delete? What if some information is missing? Share tasks with colleagues? Mark that the task is done? Clear the form if the user changes mind?

The above shows a sample functionality and user story. Be sure to include all functionalities needed for your ToDo app.

Task 2: Test requirements

Pick at least one of your user stories. Design tests for the story (or stories). "Design" means to describe the inputs and expected outputs, resulting in concrete / executable / example-like tests.

Note: the following test requirements and test cases are designed based on the decision made previously — regarding a web programming language and a web server to be used.

Example: (assuming the software is a web app, accessible via a web browser)
Software requirements Test requirements Test cases
The ToDo program is available as a web app.
TR1: A web server is up and running.
TR2: The ToDo program is accessible via a web browser.
TC1: input: URL = "http://localhost"
expected output: title of the page = "XAMPP"
TC2: input: URL = "http://localhost/cs3250/todo.php"
expected output: title of the page = "My ToDo"
User story — A user enters task name, due date, priority. The newly added task is confirmed on the screen.
Task information including task name, due date, and priority (normal or high) can be entered through a web form.
TR3: There exists a textbox allowing a user to enter a task.
TR4: There exists a textbox allowing a user to enter a due date.
TR5: There exist radio buttons for priority (normal and high).
TR6: There exists a submit button to add task to the ToDo list

Note: these TRs can be combined, assuming all or nothing.

TC3: input: URL = "http://localhost/cs3250/todo.php"
expected output: There exists a form input of type="text" with name="taskdesc"
TC4: input: URL = "http://localhost/cs3250/todo.php"
expected output: There exists a form input of type="text" with name="duedate"
TC5: input: URL = "http://localhost/cs3250/todo.php"
expected output: There exist two form inputs of type=radio" with name="priority" and "normal" and "high" values
TC6: input: URL = "http://localhost/cs3250/todo.php"
expected output: There exists a form input of type="submit" labeled as "Add Task"
Once all information is added, the newly added task is confirmed on the screen.
TR7: The newly added task is confirmed on the screen.
TC7: input:
    taskdesc = "task1"
    duedate = "11/17/2025"
    priority = "normal"
    click "Add Task" button
expected output: "You entered task1 | 11/17/2025 | normal" is displayed

Task 3: Follow the TDD process to fulfill the user story (stories)

Follow the TDD process. Write one test, modify the software to make it work, refactor as needed. Repeat the process with new tests until the user story (stories) is (are) fulfilled. Use test doubles as needed.

Example:

You will use tests as guidelines to design and develop a ToDo software.

Test file (todo_Test.java) PHP program / SUT (todo.php)
1. Create a Selenium test file (alternatively, you may start with a JUnit test file and then import libraries necessary for Selenium)
import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;              
import org.junit.jupiter.api.Test;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

import org.openqa.selenium.firefox.FirefoxDriver;     // for Firefox
import org.openqa.selenium.chrome.ChromeDriver;       // for chrome

public class todo_Test 
{
} 
 
2. Set up test fixture
public class todo_Test 
{
   private WebDriver driver;   
   
   // specify SUT
   private String url = "http://localhost/cs3250/tdd-web/todo.php";  
	   
   @BeforeEach
   void setUp() throws Exception 
   {
   // uncomment the following if you use chrome	   
   // System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");  
   // driver = new ChromeDriver();   // create an instance of a web browser	

   // uncomment the following if you use firefox
   // System.setProperty("webdriver.gecko.driver", "path/to/geckodriver"); 
   // driver = new FirefoxDriver();  // create an instance of a web browser
      
      driver.get(url);                  // open the given url
   }

   @AfterEach
   void tearDown() throws Exception 
   {
      driver.quit();                   // close the browser
   } 
 
3. Create a test method to satisfy TR1
@Test
@DisplayName("Test 1: check if the server is available")
public void test_serverAvailable()
{
   driver.get("http://localhost");        
   assertEquals(true, driver.getTitle().contains("XAMPP"));
} 
Note: design decision on the web server (and the specific information on the domain / URL, and default port and title) — use tests to guide how the software will be built.
 
4. Run the test method.
The test fails because the web server is not running.
 
  5. Start a web server.
6. Run the test method.
The test passes.
 
7. Create a test method to satisfy TR2
@Test
@DisplayName("Test 2: check if the app is available")
public void test_appAvailable()
{
   assertEquals(driver.getTitle(), "My ToDo");         
}     
Note: design decision on the title — use tests to guide how the software will be built.
8. Run the test method.
The test fails because the SUT is unavailable.
 
  9. Create a PHP file named todo.php (text version) to be developed with TDD.
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet"
    href="href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
  <title>My ToDo</title>    
</head> 
<body>
</body>

You may create an empty PHP file. However, it will not pass the test since the title of the page is not set. In this example, we choose to specify the title of the page as soon as todo.php is created. We also decide to include bootstrap so that we can format the page later.

10. Run the test method.
The test passes.
 
11. Create a test method to satisfy TRs 3-6 (since all form inputs must exist, we choose to include these test requirements in a single test method)
@Test
@DisplayName("Test 3: check if the form inputs exist")
public void test_formInputExist_b()
{
   WebElement element1 = driver.findElement(By.xpath("(//input[@type='text'])")); 
   WebElement element2 = driver.findElement(By.xpath("(//input[@type='text'])[2]"));   
   List<WebElement> element3 = driver.findElements(By.xpath("//*[@type='radio']"));
   WebElement btn = driver.findElement(By.xpath("//*[@type='submit']"));
     
   assertAll("Check if task, due date, and priority exist", 
     () -> assertEquals("taskdesc", element1.getAttribute("name").toString()),
     () -> assertEquals("duedate", element2.getAttribute("name").toString()),
     () -> assertEquals("priority", element3.get(0).getAttribute("name").toString()),
     () -> assertEquals("normal", element3.get(0).getAttribute("value").toString()),
     () -> assertEquals("priority", element3.get(1).getAttribute("name").toString()),
     () -> assertEquals("high", element3.get(1).getAttribute("value").toString()),
     () -> assertEquals("Add Task", btn.getAttribute("value"))
   );
      
// note: design decision on names, kinds, and orders of the form inputs
// — use tests to guide how the software will be built.
}
Note: to use List, import java.util.List;
12. Run the test method.
The test fails because the form inputs do not exist.
 
  13. Create form inputs.
<div class="container">
<h1> My ToDo</h1>     
  <form action="your-file-name.php" method="post">    
    <div class="mb-3 mt-3">     
      <label for="taskdesc" class="form-label">Task</label>
      <input type="text" class="form-control" name="taskdesc" /> 
    </div>
    <div class="mb-3">
      <label for="duedate" class="form-label">Due date
      <input type="text" class="form-control" name="duedate" />
    </div>            
    <div class="form-check">
      <input class="form-check-input" type="radio" name="priority" value="normal" />
      <label class="form-check-label" for="normalRadio">Normal</label>
    </div>
    <div class="form-check">
      <input class="form-check-input" type="radio" name="priority" value="high" />
      <label class="form-check-label" for="highRadio">High</label>
    </div>             
    <div class="d-grid gap-2 mt-3">
      <input type ="submit" value="Add Task" name="add"  
             class="btn btn-dark" title="Add a task to My ToDo" />   
    </div>                
  </form>
</div>
Note: this sample code uses bootstrap for formatting.

Be sure to modify the form's action attribute so that the form is submitted to your app.

<form action="your-file-name.php" method="post"> 
14. Run the test method.
The test passes.
 
15. Create a test method to satisfy TR7
@Test
@DisplayName("Test 4: check that the newly added task is confirmed")
public void test_formEntry()
{
   WebElement taskdesc = driver.findElement(By.xpath("(//input[@name='taskdesc'])"));
   WebElement duedate = driver.findElement(By.xpath("(//input[@name='duedate'])"));
   WebElement normal = 
     driver.findElement(By.xpath("(//input[@name='priority' and @value='normal'])"));
   WebElement btn = driver.findElement(By.xpath("//*[@type='submit']"));
   
   taskdesc.sendKeys("task1");
   duedate.sendKeys("11/17/2025");
   normal.click();     
   btn.click();        
   
   String expected_output = "You entered task1 | 11/17/2025 | normal";
   assertTrue(driver.getPageSource().contains(expected_output));
}
Note: design decision on the output and the format of the output — use tests to guide how the software will be built.
16. Run the test method.
The test fails because the output does not contain the expected string (task info).
 
  17. Add code to make the test pass

[Key idea: make the test pass as quickly as possible and with minimal effort]

<?php 
if ($_SERVER['REQUEST_METHOD'] == "POST")
{
   if (!empty($_POST['taskdesc']) && 
       !empty($_POST['duedate']) && 
       !empty($_POST['priority']))
   {
      echo "You entered " . $_POST['taskdesc'] . " | " . 
            $_POST['duedate'] . " | " . $_POST['priority'] . "<br/>";
   }
} 
?> 
18. Run the test method.
The test passes.
 
Remember to refactor the code
Refactoring is to improve the design and quality of the code. It is not rewriting the code nor changing the functionality of the code/program.
[Reminder: test code is also code and should be refactored when possible]

Repeat the TDD process — complete the remaining functionalities of your app


Task 4: Wrap-up questions

Think about your experience with tasks 1-3 and answer the following questions.

  1. Compare your experience applying TDD to develop a standalone Java program (Duhhuh, Tic-Tac-Toe) and a web program (above).
    • Briefly discuss the similarities and differences.
  2. Besides a standalone software and a web app, come up with one software application that the TDD process is appropriate. This could be any kind of software; e.g., a mobile app, embedded software, safety-critical software, ...
    • What might that software be?
    • Why do you think the TDD process is appropriate for that software?
    • How would you apply the TDD process to develop that software?

Grading rubric

[Total: 10 points]: Done (or provide evidence of your attempt, full or reasonable effort)

(-2.5 points) for 24 hours late (submitted after 22-Nov-2025 11am EST, by 23-Nov-2025 11am EST)
(-5 points) for 48 hours late (submitted after 23-Nov-2025 11am EST, by 24-Nov-2025 11am EST)


Submission

Making your submission available to instructor and course staff is your responsibility; if we cannot access or open your file, you will not get credit. Be sure to test access to your file before the due date.


Copyright © 2025 Upsorn Praphamontripong
Released under the Creative Commons License CC-BY-NC-SA 4.0 license.
Last updated 2025-11-18 18:31