Reputation: 27
I have written a program which looks like this:
import java.util.Scanner;
import java.util.ArrayList;
public class Triangles {
public static void main(String[] args) {
Scanner user_input = new Scanner(System.in);
ArrayList<String> triangleLengths = new ArrayList<String>();
for (int i=0; i < 3; i++) {
System.out.print("Triangle length #" + i + ": ");
triangleLengths.add(i,user_input.next());
}
if (triangleLengths.get(0) == triangleLengths.get(1) && triangleLengths.get(1) == triangleLengths.get(2)) {
System.out.println("This triangle is an equilateral triangle");
} else if (triangleLengths.get(0) == triangleLengths.get(1) || triangleLengths.get(0) == triangleLengths.get(2) || triangleLengths.get(1) == triangleLengths.get(2)) {
System.out.println("This triangle is an isosceles triangle");
} else if (triangleLengths.get(0) != triangleLengths.get(1) && triangleLengths.get(1) != triangleLengths.get(2)) {
System.out.println("This triangle is a scalene triangle");
} else {
System.out.println("The input does not make a triangle!");
}
}
}
I have been tasked with writing a JUnit test case to essentially try and 'break' my program through testing with various inputs. I can't for the life of me figure out how to do this as a total Java newbie - could anyone point me in the right direction?
Upvotes: 0
Views: 2496
Reputation: 79807
The best thing for you to do is to redesign your code, so that
main
method is absolutely minimal. Then, you can unit test the triangle logic and the user input logic separately. And if main
is small enough, you can get away without writing a unit test for it.
However, that's not what you asked. If you want to keep your code exactly as you've written it, you can test it by setting System.in
and System.out
to streams where you control the input and output.
Your test class might look something like this. This redirects System.in
to read from a String
that you specify in each test, and System.out
to write to a stream whose content you can verify afterwards.
public class TrianglesTest {
@Test
public void identifiesEquilateralTriangle() {
ByteArrayOutputStream output = setStreams("5 5 5 ");
Triangles.main(new String[0]);
assertEquals("This triangle is an equilateral triangle", output.toString().trim());
}
// Plus a whole lot more tests for other cases
private ByteArrayOutputStream setStreams(String input) {
System.setIn(new ByteArrayInputStream(input.getBytes()));
ByteArrayOutputStream toReturn = new ByteArrayOutputStream();
System.setOut(new PrintStream(toReturn));
return toReturn;
}
}
Upvotes: 0
Reputation: 1751
So I made a suggestion on how to solve it.
you want to make it so that you can test with different parameters automatically without needing to enter it manually so i isolated the triangles part as seen below.
EDIT: I redid the code somewhat
The normal run class src/main.java
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner user_input = new Scanner(System.in);
List<String> triangleLengths = new ArrayList<>();
for (int i = 0; i < 3; i++) {
System.out.print("Triangle length #" + i + ": ");
triangleLengths.add(i, user_input.next());
}
// Result output will be here
Triangle subject = new Triangle(triangleLengths);
if (subject.getTriangleType() == Triangle.Type.INVALID) {
System.out.println("Triangle is invalid");
} else {
System.out.println("Triangle is: " + subject.getTriangleType());
}
}
}
The JUnit class test/TrianglesTest.java
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
public class TrianglesTest {
/**
* Testing with String inputs (what you'd enter manually)
*/
@Test
public void testWithStrings() {
List<String> triangleLengths = Arrays.asList("len1", "len2", "len3");
Triangle subject = new Triangle(triangleLengths);
// Example of checking if expected type
assertEquals(Triangle.Type.ISOSCELES, subject.getTriangleType());
}
/**
* Testing with numbers as what I'd expect the triangle to be made of
*
* Here you test with a triangle object
*
* Haven't tested what the 3 types is sorry :O
*/
@Test
public void testWithNumbersAsObject() {
Triangle subject = new Triangle(4, 5.32, 7);
assertEquals(Triangle.Type.SCALENE, subject);
}
/**
* This piece you check the static method but have no object
*/
@Test
public void testWithNumbersStaticMethod() {
assertEquals(Triangle.Type.ISOSCELES, Triangle.getTriangleType(3.4d, 4d, 1.111d));
}
}
And lastly the actual code you wanted to test src/Triangles.java
import java.util.List;
/**
* I created so you can both have an object of the triangle or make the check purely static
* maybe you need an object type for later?
*/
public class Triangle {
final double side0;
final double side1;
final double side2;
public Triangle(List<String> triangleLengths) {
this(Double.parseDouble(triangleLengths.get(0)),
Double.parseDouble(triangleLengths.get(1)),
Double.parseDouble(triangleLengths.get(2)));
}
public Triangle(double side0, double side1, double side2) {
this.side0 = side0;
this.side1 = side1;
this.side2 = side2;
}
public Triangle.Type getTriangleType() {
return Triangle.getTriangleType(side0, side1, side2);
}
public static Triangle.Type getTriangleType(double side0, double side1, double side2) {
if (isEquilateral(side0, side1, side2)) {
return Type.EQUILATERAL;
} else if (isIsosceles(side0, side1, side2)) {
return Type.ISOSCELES;
} else if (isScalene(side0, side1, side2)) {
return Type.SCALENE;
} else {
return Type.INVALID;
}
}
private static boolean isScalene(double side0, double side1, double side2) {
return side0 != side1 && side1 != side2;
}
private static boolean isIsosceles(double side0, double side1, double side2) {
return side0 == side1 || side0 == side2 || side1 == side2;
}
private static boolean isEquilateral(double side0, double side1, double side2) {
return side0 == side1 && side1 == side2;
}
public enum Type {
EQUILATERAL,
ISOSCELES,
SCALENE,
INVALID
}
}
Hope this helps.
Note that I return the answer from the Triangles class instead of writing it out immidiately. And only in the manual run I write it out from the main method.
Upvotes: 1
Reputation: 140417
The answer from Wisienkas is right on spot; but I think a bit more of conceptual background would be helpful.
You see, when you start coding, "user input" from stdin, using a Scanner, that's like "the big thing". You fetch some value, do some processing on that; and print some results.
The problem is: that is not how "real world" works; and therefore you ran into that problem that your code wasn't testable. The key thing to understand here is: you need just a little bit of abstraction, in order to make things better.
Those are:
Upvotes: 0