Skip to content
Snippets Groups Projects
Commit 70d4c3c0 authored by Hallvard Trætteberg's avatar Hallvard Trætteberg
Browse files

Setup for UI test, controller more testable, add convenience methods

parent 75189f1f
Branches
Tags
No related merge requests found
Showing
with 245 additions and 76 deletions
...@@ -26,12 +26,25 @@ public class TodoItem { ...@@ -26,12 +26,25 @@ public class TodoItem {
this.checked = checked; this.checked = checked;
} }
public void toggleChecked() { public void set(TodoItem other) {
setChecked(!checked); this.checked = other.checked;
this.text = other.text;
} }
public void updateWith(TodoItem other) { // fluent API
setChecked(other.checked);
setText(other.text); public TodoItem checked(boolean checked) {
setChecked(checked);
return this;
}
public TodoItem text(String text) {
setText(text);
return this;
}
public TodoItem as(TodoItem other) {
set(other);
return this;
} }
} }
...@@ -9,6 +9,10 @@ public class TodoList implements Iterable<TodoItem> { ...@@ -9,6 +9,10 @@ public class TodoList implements Iterable<TodoItem> {
private List<TodoItem> items = new ArrayList<>(); private List<TodoItem> items = new ArrayList<>();
public TodoList(TodoItem...items) {
addTodoItems(items);
}
public TodoItem createTodoItem() { public TodoItem createTodoItem() {
return new TodoListItem(this); return new TodoListItem(this);
} }
...@@ -20,7 +24,8 @@ public class TodoList implements Iterable<TodoItem> { ...@@ -20,7 +24,8 @@ public class TodoList implements Iterable<TodoItem> {
* *
* @param item the TodoItem to add * @param item the TodoItem to add
*/ */
public void addTodoItem(TodoItem item) { public void addTodoItems(TodoItem...items) {
for (TodoItem item : items) {
TodoListItem todoListItem = null; TodoListItem todoListItem = null;
if (item instanceof TodoListItem) { if (item instanceof TodoListItem) {
todoListItem = (TodoListItem) item; todoListItem = (TodoListItem) item;
...@@ -29,10 +34,22 @@ public class TodoList implements Iterable<TodoItem> { ...@@ -29,10 +34,22 @@ public class TodoList implements Iterable<TodoItem> {
todoListItem.setText(item.getText()); todoListItem.setText(item.getText());
todoListItem.setChecked(item.isChecked()); todoListItem.setChecked(item.isChecked());
} }
items.add(todoListItem); this.items.add(todoListItem);
}
fireTodoListChanged(); fireTodoListChanged();
} }
/**
* Adds the provided TodoItem to this TodoList.
* If the TodoItem is not an instance of TodoListItem,
* its contents is copied in to a new TodoListItem and that is added instead.
*
* @param item the TodoItem to add
*/
public void addTodoItem(TodoItem item) {
addTodoItems(item);
}
public void removeTodoItem(TodoItem item) { public void removeTodoItem(TodoItem item) {
items.remove(item); items.remove(item);
fireTodoListChanged(); fireTodoListChanged();
......
...@@ -30,10 +30,10 @@ public class TodoListItem extends TodoItem { ...@@ -30,10 +30,10 @@ public class TodoListItem extends TodoItem {
} }
} }
public void updateWith(TodoItem other) { public void set(TodoItem other) {
boolean oldChecked = isChecked(); boolean oldChecked = isChecked();
String oldText = getText(); String oldText = getText();
super.updateWith(other); super.set(other);
if (oldChecked != other.isChecked() || oldText != other.getText() || oldText != null && !(oldText.equals(other.getText()))) { if (oldChecked != other.isChecked() || oldText != other.getText() || oldText != null && !(oldText.equals(other.getText()))) {
todoList.fireTodoListChanged(this); todoList.fireTodoListChanged(this);
} }
......
...@@ -29,16 +29,15 @@ public class TodoListTest { ...@@ -29,16 +29,15 @@ public class TodoListTest {
@Test @Test
public void testGetCheckedTodoItems_oneUncheckedItem() { public void testGetCheckedTodoItems_oneUncheckedItem() {
TodoItem item = newList.createTodoItem(); TodoItem item = newList.createTodoItem().text("item");
newList.addTodoItem(item); newList.addTodoItem(item);
assertTrue(newList.getCheckedTodoItems().isEmpty()); assertTrue(newList.getCheckedTodoItems().isEmpty());
} }
@Test @Test
public void testGetCheckedTodoItems_oneCheckedItem() { public void testGetCheckedTodoItems_oneCheckedItem() {
TodoItem item = newList.createTodoItem(); TodoItem item = newList.createTodoItem().checked(true);
newList.addTodoItem(item); newList.addTodoItem(item);
item.setChecked(true);
Collection<TodoItem> items = newList.getCheckedTodoItems(); Collection<TodoItem> items = newList.getCheckedTodoItems();
assertEquals(1, items.size()); assertEquals(1, items.size());
assertSame(item, items.iterator().next()); assertSame(item, items.iterator().next());
...@@ -102,8 +101,7 @@ public class TodoListTest { ...@@ -102,8 +101,7 @@ public class TodoListTest {
TodoItem item = newList.createTodoItem(); TodoItem item = newList.createTodoItem();
newList.addTodoItem(item); newList.addTodoItem(item);
Collection<TodoItem> items = newList.getTodoItems(); Collection<TodoItem> items = newList.getTodoItems();
assertEquals(1, items.size()); checkItems(items, item);
assertSame(item, items.iterator().next());
} }
@Test @Test
...@@ -112,8 +110,7 @@ public class TodoListTest { ...@@ -112,8 +110,7 @@ public class TodoListTest {
item.setChecked(true); item.setChecked(true);
newList.addTodoItem(item); newList.addTodoItem(item);
Collection<TodoItem> items = newList.getTodoItems(); Collection<TodoItem> items = newList.getTodoItems();
assertEquals(1, items.size()); checkItems(items, item);
assertSame(item, items.iterator().next());
} }
@Test @Test
...@@ -121,16 +118,18 @@ public class TodoListTest { ...@@ -121,16 +118,18 @@ public class TodoListTest {
TodoItem item = newList.createTodoItem(); TodoItem item = newList.createTodoItem();
newList.addTodoItem(item); newList.addTodoItem(item);
Collection<TodoItem> items1 = newList.getTodoItems(); Collection<TodoItem> items1 = newList.getTodoItems();
assertEquals(1, items1.size()); checkItems(items1, item);
assertEquals(item, items1.iterator().next());
item.setChecked(true); item.setChecked(true);
Collection<TodoItem> items2 = newList.getTodoItems(); Collection<TodoItem> items2 = newList.getTodoItems();
assertEquals(1, items2.size()); checkItems(items2, item);
assertSame(item, items2.iterator().next());
} }
// test iterator // test iterator
private void checkItems(Iterable<TodoItem> it, TodoItem... items) {
checkIterator(it.iterator(), items);
}
private void checkIterator(Iterator<TodoItem> it, TodoItem... items) { private void checkIterator(Iterator<TodoItem> it, TodoItem... items) {
int i = 0; int i = 0;
while (it.hasNext()) { while (it.hasNext()) {
...@@ -181,9 +180,14 @@ public class TodoListTest { ...@@ -181,9 +180,14 @@ public class TodoListTest {
assertEquals(3, receivedNotificationCount); assertEquals(3, receivedNotificationCount);
item.setChecked(true); item.setChecked(true);
assertEquals(4, receivedNotificationCount); assertEquals(4, receivedNotificationCount);
item.set(new TodoItem().checked(true).text("enda en endret verdi"));
assertEquals(5, receivedNotificationCount);
item.set(new TodoItem().checked(true).text("enda en endret verdi"));
assertEquals(5, receivedNotificationCount);
// test removeTodoListListener, too
newList.removeTodoListListener(listener); newList.removeTodoListListener(listener);
item.setChecked(false); item.setChecked(false);
assertEquals(4, receivedNotificationCount); assertEquals(5, receivedNotificationCount);
} }
@Test @Test
......
...@@ -50,6 +50,20 @@ ...@@ -50,6 +50,20 @@
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
</dependency> </dependency>
<!-- Test with TextFX -->
<dependency>
<groupId>org.testfx</groupId>
<artifactId>testfx-core</artifactId>
<version>4.0.16-alpha</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testfx</groupId>
<artifactId>testfx-junit5</artifactId>
<version>4.0.16-alpha</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
...@@ -12,6 +12,7 @@ import java.net.URL; ...@@ -12,6 +12,7 @@ import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import javafx.fxml.FXML; import javafx.fxml.FXML;
...@@ -28,41 +29,66 @@ public class TodoController { ...@@ -28,41 +29,66 @@ public class TodoController {
"{\"items\":[{\"text\":\"Øl\",\"checked\":false},{\"text\":\"Pizza\",\"checked\":true}]}"; "{\"items\":[{\"text\":\"Øl\",\"checked\":false},{\"text\":\"Pizza\",\"checked\":true}]}";
private TodoList todoList; private TodoList todoList;
// makes class more testable
TodoList getTodoList() {
return todoList;
}
private ObjectMapper mapper = new ObjectMapper(); private ObjectMapper mapper = new ObjectMapper();
/** @FXML
* Initializes the TodoController by filling a TodoList with default contents. String userTodoListPath;
*/
public TodoController() { @FXML
String sampleTodoListResource;
@FXML
TextField newTodoItemText;
@FXML
ListView<TodoItem> todoListView;
@FXML
Button deleteTodoItemButton;
private void initializeTodoList() {
// setter opp data // setter opp data
mapper.registerModule(new TodoModule()); mapper.registerModule(new TodoModule());
Reader reader = null; Reader reader = null;
try {
// try to read file from home folder first // try to read file from home folder first
if (userTodoListPath != null) {
try { try {
reader = new FileReader(Paths.get(System.getProperty("user.home"), "todolist.json") reader = new FileReader(Paths.get(System.getProperty("user.home"), userTodoListPath)
.toFile(), StandardCharsets.UTF_8); .toFile(), StandardCharsets.UTF_8);
} catch (IOException ioex) { } catch (IOException ioex) {
System.err.println("Fant ingen todolist.json på hjemmeområdet, prøver eksempelfil i stedet"); System.err.println("Fant ingen " + userTodoListPath + " på hjemmeområdet");
}
}
if (reader == null && sampleTodoListResource != null) {
// try sample-todolist.json from resources source folder instead // try sample-todolist.json from resources source folder instead
URL url = getClass().getResource("sample-todolist.json"); URL url = getClass().getResource(sampleTodoListResource);
if (url != null) { if (url != null) {
try {
reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8); reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8);
} catch (IOException e) {
System.err.println("Kunne ikke lese innebygget " + sampleTodoListResource);
}
} else { } else {
System.err.println("Fant ikke eksempelfil, bruker innebygget json-string"); System.err.println("Fant ikke innebygget " + sampleTodoListResource);
}
}
if (reader == null) {
// use embedded String // use embedded String
reader = new StringReader(todoListWithTwoItems); reader = new StringReader(todoListWithTwoItems);
} }
} try {
todoList = mapper.readValue(reader, TodoList.class); todoList = mapper.readValue(reader, TodoList.class);
} catch (IOException e) { } catch (IOException e) {
todoList = new TodoList(); todoList = new TodoList(
TodoItem item1 = todoList.createTodoItem(); todoList.createTodoItem().text("Øl"),
item1.setText("Øl"); todoList.createTodoItem().text("Pizza")
TodoItem item2 = todoList.createTodoItem(); );
item1.setText("Pizza");
todoList.addTodoItem(item1);
todoList.addTodoItem(item2);
} finally { } finally {
try { try {
if (reader != null) { if (reader != null) {
...@@ -74,19 +100,11 @@ public class TodoController { ...@@ -74,19 +100,11 @@ public class TodoController {
} }
} }
@FXML
TextField newTodoItemText;
@FXML
ListView<TodoItem> todoListView;
@FXML
Button deleteTodoItemButton;
private Collection<Button> selectionButtons; private Collection<Button> selectionButtons;
@FXML @FXML
void initialize() { void initialize() {
initializeTodoList();
selectionButtons = List.of(deleteTodoItemButton); selectionButtons = List.of(deleteTodoItemButton);
// kobler data til list-controll // kobler data til list-controll
updateTodoListView(); updateTodoListView();
...@@ -99,10 +117,10 @@ public class TodoController { ...@@ -99,10 +117,10 @@ public class TodoController {
} }
protected void updateTodoListView() { protected void updateTodoListView() {
List<TodoItem> viewList = todoListView.getItems(); List<TodoItem> items = new ArrayList<>();
viewList.clear(); items.addAll(todoList.getUncheckedTodoItems());
viewList.addAll(todoList.getUncheckedTodoItems()); items.addAll(todoList.getCheckedTodoItems());
viewList.addAll(todoList.getCheckedTodoItems()); todoListView.getItems().setAll(items);
} }
private void updateTodoListButtons() { private void updateTodoListButtons() {
...@@ -139,7 +157,8 @@ public class TodoController { ...@@ -139,7 +157,8 @@ public class TodoController {
} }
void saveTodoList() { void saveTodoList() {
Path path = Paths.get(System.getProperty("user.home"), "todolist.json"); if (userTodoListPath != null) {
Path path = Paths.get(System.getProperty("user.home"), userTodoListPath);
try (Writer writer = try (Writer writer =
new FileWriter(path.toFile(), StandardCharsets.UTF_8)) { new FileWriter(path.toFile(), StandardCharsets.UTF_8)) {
mapper.writerWithDefaultPrettyPrinter().writeValue(writer, todoList); mapper.writerWithDefaultPrettyPrinter().writeValue(writer, todoList);
...@@ -148,3 +167,4 @@ public class TodoController { ...@@ -148,3 +167,4 @@ public class TodoController {
} }
} }
} }
}
...@@ -43,12 +43,11 @@ public class TodoItemListCell extends ListCell<TodoItem> { ...@@ -43,12 +43,11 @@ public class TodoItemListCell extends ListCell<TodoItem> {
todoItemControl = new HBox(); todoItemControl = new HBox();
checkedView = new CheckBox(); checkedView = new CheckBox();
checkedView.selectedProperty().addListener((prop, oldValue, newValue) -> { checkedView.selectedProperty().addListener((prop, oldValue, newValue) -> {
TodoItem newItem = new TodoItem(); getItem().set(new TodoItem()
newItem.setChecked(checkedView.isSelected()); .checked(checkedView.isSelected())
if (isEditing()) { // use editor text if it's active, otherwise existing text
newItem.setText(textEditor.getText()); .text(isEditing() ? textEditor.getText() : getItem().getText())
} );
getItem().updateWith(newItem);
}); });
todoItemControl.getChildren().add(checkedView); todoItemControl.getChildren().add(checkedView);
} }
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.String?>
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
...@@ -7,6 +8,10 @@ ...@@ -7,6 +8,10 @@
<?import javafx.scene.control.ListView?> <?import javafx.scene.control.ListView?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="todolist.ui.TodoController"> <VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="todolist.ui.TodoController">
<fx:define>
<String fx:id="userTodoListPath" fx:value="todolist.json"/>
<String fx:id="sampleTodoListResource" fx:value="sample-todolist.json"/>
</fx:define>
<HBox> <HBox>
<Button fx:id="newTodoItemButton" text="New Item" onAction="#handleNewTodoItemAction"/> <Button fx:id="newTodoItemButton" text="New Item" onAction="#handleNewTodoItemAction"/>
<TextField fx:id="newTodoItemText" promptText="Skriv inn tekst her" onAction="#handleNewTodoItemAction"/> <TextField fx:id="newTodoItemText" promptText="Skriv inn tekst her" onAction="#handleNewTodoItemAction"/>
......
package todolist.ui;
import java.util.HashSet;
import java.util.Set;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.input.KeyCode;
import javafx.stage.Stage;
import todolist.core.TodoItem;
import todolist.core.TodoList;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.testfx.framework.junit5.ApplicationTest;
public class TodoAppTest extends ApplicationTest {
private TodoController controller;
private TodoList todoList;
@Override
public void start(final Stage stage) throws Exception {
final FXMLLoader loader = new FXMLLoader(getClass().getResource("TodoTest.fxml"));
final Parent root = loader.load();
this.controller = loader.getController();
this.todoList = this.controller.getTodoList();
stage.setScene(new Scene(root));
stage.show();
}
@Test
public void testController() {
assertNotNull(this.controller);
assertNotNull(this.todoList);
assertEquals(2, this.todoList.getTodoItems().size());
}
@Test
public void testTodoListView_initialItems() {
final ListView<TodoItem> todoListView = lookup("#todoListView").query();
// list contains same set of elements as the todo list (in some order)
Set<TodoItem> listViewItems = new HashSet<>(todoListView.getItems());
Set<TodoItem> todoListItems = new HashSet<>(this.todoList.getTodoItems());
assertEquals(todoListItems, listViewItems);
}
@Test
public void testNewTodoItem() {
Set<TodoItem> todoListItems1 = new HashSet<>(this.todoList.getTodoItems());
clickOn("#newTodoItemText").write("New item");
clickOn("#newTodoItemButton");
Set<TodoItem> todoListItems2 = new HashSet<>(this.todoList.getTodoItems());
todoListItems2.removeAll(todoListItems1);
// one new item
assertEquals(1, todoListItems2.size());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.String?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.ListView?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="todolist.ui.TodoController">
<fx:define>
<!--
<String fx:id="todoListUserHomePath" fx:value="todolist.json"/>
-->
<String fx:id="sampleTodoListResource" fx:value="test-todolist.json"/>
</fx:define>
<HBox>
<Button fx:id="newTodoItemButton" text="New Item" onAction="#handleNewTodoItemAction"/>
<TextField fx:id="newTodoItemText" promptText="Skriv inn tekst her" onAction="#handleNewTodoItemAction"/>
</HBox>
<ListView fx:id="todoListView">
</ListView>
<HBox>
<Button fx:id="deleteTodoItemButton" text="Delete Item" onAction="#handleDeleteItemAction"/>
</HBox>
</VBox>
{
"items": [
{
"text": "Øl",
"checked": false
},
{
"text": "Pizza",
"checked": false
}
]
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment