I'm currently working on a text formatter project, and I'm starting with the LineAssembler class. However, we are required to write unit testing before writing any code and I'm kind of stuck.
Would anyone shed some lights please? I've also put the PageAssembler skeleton and its given unit test for reference. Thanks!!
package tex61;
import java.util.ArrayList;
import static tex61.FormatException.error;
/** An object that receives a sequence of words of text and formats
* the words into filled and justified text lines that are sent to a receiver.
*/
class LineAssembler {
/** A new, empty line assembler with default settings of all
* parameters, sending finished lines to PAGES. */
LineAssembler(PageAssembler pages) {
_pages = pages;
// FIXME
}
/** Add TEXT to the word currently being built. */
void addText(String text) {
// FIXME
}
/** Finish the current word, if any, and add to words being accumulated. */
void finishWord() {
// FIXME
}
/** Add WORD to the formatted text. */
void addWord(String word) {
// FIXME
}
/** Add LINE to our output, with no preceding paragraph skip. There must
* not be an unfinished line pending. */
void addLine(String line) {
// FIXME
}
/** Set the current indentation to VAL. VAL >= 0. */
void setIndentation(int val) {
// FIXME
}
/** Set the current paragraph indentation to VAL. VAL >= 0. */
void setParIndentation(int val) {
// FIXME
}
/** Set the text width to VAL, where VAL >= 0. */
void setTextWidth(int val) {
// FIXME
}
/** Iff ON, set fill mode. */
void setFill(boolean on) {
// FIXME
}
/** Iff ON, set justify mode (which is active only when filling is
* also on). */
void setJustify(boolean on) {
// FIXME
}
/** Set paragraph skip to VAL. VAL >= 0. */
void setParSkip(int val) {
// FIXME
}
/** Set page height to VAL > 0. */
void setTextHeight(int val) {
// FIXME
}
/** Process the end of the current input line. No effect if
* current line accumulator is empty or in fill mode. Otherwise,
* adds a new complete line to the finished line queue and clears
* the line accumulator. */
void newLine() {
// FIXME
}
/** If there is a current unfinished paragraph pending, close it
* out and start a new one. */
void endParagraph() {
// FIXME
}
/** Transfer contents of _words to _pages, adding INDENT characters of
* indentation, and a total of SPACES spaces between words, evenly
* distributed. Assumes _words is not empty. Clears _words and _chars. */
private void emitLine(int indent, int spaces) {
// FIXME
}
/** If the line accumulator is non-empty, justify its current
* contents, if needed, add a new complete line to _pages,
* and clear the line accumulator. LASTLINE indicates the last line
* of a paragraph. */
private void outputLine(boolean lastLine) {
// FIXME
}
/** Destination given in constructor for formatted lines. */
private final PageAssembler _pages;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
package tex61;
import static tex61.FormatException.error;
/** A PageAssembler accepts complete lines of text (minus any
* terminating newlines) and turns them into pages, adding form
* feeds as needed. It prepends a form feed (Control-L or ASCII 12)
* to the first line of each page after the first. By overriding the
* 'write' method, subtypes can determine what is done with
* the finished lines.
*/
abstract class PageAssembler {
private int _textHeight;
static final int UNLIMITED = Integer.MAX_VALUE;
/** Create a new PageAssembler. Initially, its text height is unlimited.
* It prepends a form feed character to the first line of each page
* except the first. */
PageAssembler() {
_textHeight = UNLIMITED;
// FIXME
}
/** Add LINE to the current page, starting a new page with it if
* the previous page is full. A null LINE indicates a skipped line,
* and has no effect at the top of a page. */
void addLine(String line) {
// FIXME
}
/** Set text height to VAL, where VAL > 0. */
void setTextHeight(int val) {
if(val > 0) {
_textHeight = val;
}
// FIXME
}
/** Perform final disposition of LINE, as determined by the
* concrete subtype. */
abstract void write(String line);
// FIXME
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
package tex61;
import java.io.PrintWriter;
/** A PageAssembler that sends lines immediately to a PrintWriter, with
* terminating newlines.
*/
class PagePrinter extends PageAssembler {
/** A new PagePrinter that sends lines to OUT. */
PagePrinter(PrintWriter out) {
// FIXME
}
/** Print LINE to my output. */
@Override
void write(String line) {
// FIXME
}
// FIXME
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
package tex61;
import java.util.List;
import java.util.ArrayList;
import java.io.StringWriter;
import java.io.PrintWriter;
import org.junit.Test;
import static org.junit.Assert.*;
/** Unit tests of PageAssemblers.*/
public class PageAssemblerTest {
private static final String NL = System.getProperty("line.separator");
private void setupWriter() {
output = new StringWriter();
writer = new PrintWriter(output);
}
private void setupCollector() {
outList = new ArrayList<>();
}
private void makeTestLines(int n) {
testLines = new ArrayList<>();
for (int i = 0; i < n; i += 1) {
testLines.add("Line " + i);
}
}
private void writeTestLines() {
for (String L : testLines) {
pages.addLine(L);
}
}
private String joinLines() {
StringBuilder S = new StringBuilder();
for (String L : testLines) {
S.append(L);
S.append(NL);
}
return S.toString();
}
@Test
public void testPrinterContents1() {
makeTestLines(20);
setupWriter();
pages = new PagePrinter(writer);
writeTestLines();
writer.close();
assertEquals("wrong contents: printer", joinLines(), output.toString());
}
@Test
public void testCollectorContents1() {
makeTestLines(20);
setupCollector();
pages = new PageCollector(outList);
writeTestLines();
assertEquals("wrong contents: collector", testLines, outList);
}
/** Collects output to a PrintWriter. */
private StringWriter output;
/** Collects output from a PageAssembler. */
private PrintWriter writer;
/** Lines of test data. */
private List<String> testLines;
/** Lines from a PageCollector. */
private List<String> outList;
/** Target PageAssembler. */
private PageAssembler pages;
}
Writing unit test before implementing your solution is used to
So this is more about creating requirements for your modules and designing your app.
When one has troubles with this, he could break this rule and build a proof of concept to understand what he's dealing with. Then rethink the API, create tests and adjust his prototype to the new API. That's what I do when I'm creating something which I'm not very familiar or just complex.