I'm having trouble with learning how to write and use my own classes. For example;
import java.text.NumberFormat;
public class BikeCommute {
private String route;
private double distanceTraveled;
private double timeRequired;
private String dateTraveled;
private String mode;
private double gallonsSaved;
final private int mpg = 25;
public BikeCommute(String mode, String dateTraveled, String route,
double distanceTraveled, double timeRequired)
{
mode = this.mode;
route = this.route;
distanceTraveled = this.distanceTraveled;
timeRequired = this.timeRequired;
dateTraveled = this.dateTraveled;
}
public double gallonsCalculated(){
gallonsSaved = distanceTraveled/mpg;
return gallonsSaved;
}
public double getGallons(){
return gallonsSaved;
}
public String toString(){
return mode + route + distanceTraveled + timeRequired + dateTraveled + gallonsSaved;
}
}
I'm trying to write a class called BikeCommute that will read in the route, mode of transportation, distance traveled, time required, and date that the trip was traveled, calculate how many gallons of gas were saved by dividing the distance traveled by the miles per gallon, and output the route, mode, distance traveled, time required, date traveled, and gallons saved. This, however, returns nullnull0.0null0.0 when I run it. Any thoughts?
Assignment is from right to left. Replace
mode = this.mode;
with
this.mode = mode
Same for the other field variables in BikeCommute
You have these the wrong way around:
mode = this.mode;
Should be:
this.mode = mode;
Your question is about a specific problem in this specific class; however, you should consider the general problem too.
If you have problems constructing or using your own classes, it's probably because you have confused the building of the class with the using of the class. Actually, you can get much better results if you "use the class" before you "build the class". I know that this sounds like nonsense, but it is the driving idea behind test driven development, which is one of the more effective ways to write robust usable programs.
Consider looking into JUnit and learn how to setup a "test" directory. I also recommend you minimally learn "just enough" maven to automate this. Don't start off trying to be a JUnit or Maven virtuoso. Cut-and-paste is fine for beginning in this area.
Then you can write your test first:
public void testBikeCommute() {
BikeCommute commute = new BikeCommute(...);
Assert.assertEquals(5, commute.getGallons());
}
Now you can see that your design is pretty odd, as most of my commutes don't have Gallons. As you attempt to "use" your software before you write it, you might find that your "commute" is really a comparison between two theoretical commutes, one in a car, and one on a bike.
public void testCommuteSavings() {
BikeCommute bike = new BikeCommute(...);
CarCommute car = new CarCommute(...);
SavingsCalculator calculator = new Calculator(car, bike);
Assert.assertEquals(5, calcuator.getSavedGallons());
}
This "use first" technique can allow you to develop a much cleaner representation of your problem, and with a toolkit like JUnit, can even become an automated testing strategy. Also it makes some items pretty easy to test, like
public void testBikeCommuteUsesZeroGallons() {
BikeCommute bike = new BikeCommute();
Assert.assertEquals(0, bike.getGallonsUsed());
}
Cheers
Related
I'm building an automatic game client that is supposed to make moves against me in a 4-Wins-Game. For making a move it should chose a column and send that chosen column by calling the move() function to my server.
public class AutomaticGameClient {
private String userName;
private String userPassword;
public AutomaticGameClient(String userName, String userPassword) {
this.userName = userName;
this.userPassword = userPassword;
}
public int makeMove() {
columnNo = 0;
move(columnNo);
return columnNo;
}
}
Right now it always simply moves by making the next move in the first row (columnNo = 0). This works.
Now I have to test that function, but I don't want to test the move() part. I only want to assert, that it's returning 0:
#Test
public void whenMakeMove_columnNoIsZero() {
AutomaticGameClient agcY = new AutomaticGameClient("Georg1", "geheim1");
int rowMoved = agcY.makeMove();
assertEquals(rowMoved, 0);
}
When I run this test, I get org.java_websocket.exceptions.WebsocketNotConnectedException, because move(columnNo) is trying to start a connection to my socket. So is there a way to skip this part of the function under test?
"So is there a way to skip this part of the function under test?" - Yes. One could create a partial mock/spy of the unit under test and then mock the move(...)-method. For more details please refer to this posts by Victor Grazi and its answers.
In this concrete situation, however, the cleaner solution would be to move socket-related code to a separate service. A unit should be tested as a unit and thus we should use mocks seldomly and only if no other possibility exists. This, however, is a boundary between units, thus socket-communication should be moved in a separate unit for better testability. Please notice that I do not say that each class is a unit. A unit can be composed of multiple classes. But one class should not be composed of multiple units since we then have to resort to solutions like partial mocks for the unit under test. In my opinion, mocking the unit under test is always a red flag.
So, I have a JUnit test for Uncle Bob's classic Bowling game example for TDD.
I refactored the test to use an array of games and expected scores.
The advantage is it's easy to add new tests.
The disadvantage is that it doesn't "self document" the code or the test.
Are there any best practices surrouding this?
public class ScoreTest {
int[][] games = {
{0,0,0,0,0,0,0,0,0,0},
{10,10,10,10,10,10,10,10,10,10,10,10
};
int[] scores = {0, 300};
#Test
public void testScore() {
for(int i=0; i<games.length; i++) {
let game = games[i];
let expectedScore = scores[i];
let score = new Score();
score.roll(game); // roll the entire game
let actualScore = score.total() // calculate the bowling score
assertEquals(expectedScore, actualScore);
}
}
}
Instead of an int[][], you might make a little inner class, and make an array of those.
private static class BowlingTestGame {
private String name;
private int[] game;
private int expectedResult;
}
BowlingTestGame[] games = {
{"Perfect Game", {10,10,10,10,10,10,10,10,10,10,10,10}, 300},
// ... more tests ...
}
Then you can include the name of the game as the failure message in the assert.
Also, this gets rid of you trying to maintain two parallel arrays, which is always a bad idea.
I think that if one does want to go down the path of "input x should produce output y"-tests, then one will not get much more readable as this. I would only recommend those kind of tests for trivial problems or well-known sequences (e.g. tests for something producing the fibonacci sequence).
For everything more complex, I would advice to use a behaviour-driven approach with, e.g., Given-When-Then tests. Those tests tend to be more expressive and more self-documenting.
A remark: If you are using JUnit5, I would propose to use parameterized tests.
I'm studying to be a Java developer and right now I'm learning test driven development, which means that im very new to JUnit and Mockito.
I've been struggling for a while now and I'm stuck.
I have no idea how to test this particular method that has no arguments, no return value and a randomizer.
Old logic:
public void getPlayerToStart(int randomNr) {
if (randomNr == 1) {
currentPlayer = p1;
opponentPlayer = p2;
} else {
currentPlayer = p2;
opponentPlayer = p1;
}
}
Old test
#Test
void testSetCurrentPlayerSetToPlayer1() {
gameEngine.getPlayerToStart(1);
assertEquals(gameEngine.getP1(), gameEngine.getCurrentPlayer());
assertEquals(gameEngine.getP2(), gameEngine.getOpponentPlayer());
}
#Test
void testSetCurrentPlayerSetToPlayer2() {
gameEngine.getPlayerToStart(2);
assertEquals(gameEngine.getP2(), gameEngine.getCurrentPlayer());
assertEquals(gameEngine.getP1(), gameEngine.getOpponentPlayer());
}
New logic:
public void getPlayerToStart() {
Random rand = new Random();
int randomNr = rand.nextInt(2) + 1;
if (randomNr == 1) {
currentPlayer = p1;
opponentPlayer = p2;
} else {
currentPlayer = p2;
opponentPlayer = p1;
}
}
I'm not sure how to be able to test the getPlayerToStart() without the argument "randomNr".. Can someone please just point me in the right direction!
Thanks in advance.
Always to please be keeping in mind that the thought "gee, this is hard to test" is TDD trying to scream at you that the design needs review.
I have no idea how to test this particular method that has no arguments, no return value and a randomizer.
Random numbers are a side effect, like I/O or time, and should be handled that way in your design.
Which is to say, if you are doing TDD, one of the things you should be recognizing is that the source of randomness is an input to your system; it's part of the imperative shell which is provided by your test harness when running tests, and is provided by your composition root in production.
The testable approach would separate "generate a seed" from "compute a state from the seed"; unit tests are great for the latter bit, because pure functions are really easy to test. Generating random numbers is state of sin level hard to test, but with some design you can simplify the code around it to the point that it "obviously has no deficiencies".
You may also want to review Writing Testable Code, by Misko Hevery, or Tales of the Fischer King.
another solution could be a strict interpretation of the single responsibility pattern: a class providing business logic sould not be responsible to create or acquire its dependencies. This leads to the concept of dependency injection:
class CodeUnderTest {
private final Random rand;
public CodeUnderTest(#NotNull Random rand){
this.rand = rand;
}
public void getPlayerToStart() {
int randomNr = rand.nextInt(2) + 1;
if (randomNr == 1) {
currentPlayer = p1;
opponentPlayer = p2;
} else {
currentPlayer = p2;
opponentPlayer = p1;
}
}
}
You'd need to enhance your Test to this:
class CodeUnderTestTest{
private final Random fakeRandom = new Random(1);
private CodeUnderTest cut;
#Before
public void setup(){
cut = new CodeUnderTest(fakeRandom);
}
// your test code relying on the repeatable order
// of Random object initialized with a fix seed.
}
You also need to change all places in your code where you instantiate CodeUnderTest to add a Random object without seed.
This looks like a downside at first but it provides the possibility to have only one instance of Random throughout your code without implementing the Java Singelton Pattern.
You could gain even more control if you replace the Random object with a mock. The easiest way to do that is to use a mocking framework like Mockito:
class CodeUnderTestTest{
#Rule
public MockitoRule rule = MockitoJUnit.rule();
#Mock
private Random fakeRandom;
// you could use #InjectMocks here
// instead of the setup method
private CodeUnderTest cut;
// This will NOT raise compile errors
// for not declared or not provided
// constructor arguments (which is bad in my view).
#Before
public void setup(){
cut = new CodeUnderTest(fakeRandom);
}
#Test
void testSetCurrentPlayerSetToPlayer1() {
doReturn(0).when(fakeRandom).nextInt(2);
cut.getPlayerToStart(1);
assertEquals(cut.getP1(), cut.getCurrentPlayer());
assertEquals(cut.getP2(), cut.getOpponentPlayer());
}
}
Move the call to new Random() into its own method, like this.
You can rewrite your getPlayerToStart method to use the other one, to save duplicated code (but you don't need to).
public void getPlayerToStart() {
Random rand = makeRandom();
int randomNumber = rand.nextInt(2) + 1
getPlayerToStart(randomNumber);
}
public Random makeRandom() {
return new Random();
}
Now you can use Mockito to
make a mock Random object;
make a spy of your class, which is the object you're going to test;
stub the makeRandom method of your spy, so that it returns your mock Random;
stub the mock Random so that it returns whichever value you like, in each test.
After that, you can write a test in which player 1 is expected to start, and another test in which player 2 is expected to start.
I agree with who says that you should use dependency injection and have your own abstraction (in this way you could mock the collaborator).
But, creating the abstraction you're simply moving the responsibility (and the test problem) elsewhere.
Did you know about the Random constructor that takes an integer argument called "seed"? Using the same seed you will have always the same sequence of results.
See: https://stackoverflow.com/a/12458415/5594926
I need to write a test using assertEquals to see how many of each coins will be returned to me when money is given and am not sure how to structure it or what info to include. Money can be inserted in the amounts of $1, $2, $5 and $10. Change returned must be in the fewest amount of coins possible.
import java.math.BigDecimal;
public class MakeChange {
BigDecimal numOfQuarters = new BigDecimal(0.00);
BigDecimal numOfDimes = new BigDecimal(0.00);
BigDecimal numOfNickels = new BigDecimal(0.00);
BigDecimal quarter = new BigDecimal(0.25);
BigDecimal dime = new BigDecimal(0.10);
BigDecimal nickel = new BigDecimal(0.05);
public MakeChange() {
}
public String changeAmount(BigDecimal amtToReturn) {
while (amtToReturn.compareTo(quarter) >= 0) {
numOfQuarters = amtToReturn.divideToIntegralValue(quarter);
amtToReturn = amtToReturn.subtract(numOfQuarters.multiply(quarter));
}
while (amtToReturn.compareTo(dime) >= 0) {
numOfDimes = amtToReturn.divideToIntegralValue(dime);
amtToReturn = amtToReturn.subtract(numOfDimes.multiply(dime));
}
while (amtToReturn.compareTo(nickel) >= 0) {
numOfNickels = amtToReturn.divideToIntegralValue(nickel);
amtToReturn = amtToReturn.subtract(numOfNickels.multiply(nickel));
}
return "You will receive: " + numOfQuarters + " Quarters " + numOfDimes + " Dimes and " + numOfNickels
+ " Nickels.";
}
}
Simply spoken: you have to write testable code in order to be able to test code.
Of course, you could parse that information out of the string that this method returns. But that would be the wrong approach.
Thing is: your code is mixing two responsibilities here: A) computing how/what coins need to be returned B) turning that into a human readable message.
In other words: you start by writing another method that returns something that can easily be tested by a computer. For example by creating a class that represents change, like:
public class Change {
public int getNumberOfQuaterts() { ...
public int getNumberOfDimes() { ...
...
Now you could change your method to say:
public Change changeAmount(BigDecimal amtToReturn) { ...
and now testing becomes super-easy:
Change change = new MakeChange().changeAmount(...
assertThat(change.getNumberOfQuarters, is(5));
Beyond that:
You can now #Override toString() in that Change class - to create that human readable message. You can also write simple testcases then where you actually check that some predefined Change results in some exact string that you would be expecting to represent that object.
there are various problems in your example code. For example: there is no point in having that empty default constructor. If the constructor has nothing to do, then don't write it down. And, really important: you should not use the constructor in BigDecimal that takes a double argument. Use BigDecimal("0.01") instead! (see here). There is a reason why that constructor is #deprecated!
Finally: you could also #Override equals() in the Change class, then test code could boil down to:
assertThat(underTest.changeAmount(whatever), is(new Change(....))
You can enable yourself to manually create change objects that allow for equality checks, so that you can use assertThat() to compare change objects directly.
As an exercise, I need to learn to write tests on the following class:
package bankAccount;
public class CurrentAccount {
int account[];
int lastMove;
CurrentAccount() {
lastMove = 0;
account = new int[10];
}
public void deposit(int value) {
account[lastMove] = value;
lastMove++;
}
public void draw(int value) {
account[lastMove] = value;
lastMove++;
}
public int settlement() {
int result = 0;
for (int i=0; i<account.length; i++) {
result = result + account[i];
}
return result;
}
public static void main(String args[]) {
CurrentAccount c = new CurrentAccount();
c.deposit(10);
}
}
I am relatively new to Unit testing, and a lot of the tutorials simply cover how to do tests for simple Mathematical operators (e.g. add, subtract etc). Can anyone recommend good resources for doing Unit testing of more complex functions? Am I best off using
http://junit.sourceforge.net/javadoc/org/junit/Assert.html
and working from there?
You should be testing against the specification of the object e.g.
what's the starting balance ?
what's the balance after I add £10 ?
can I go overdrawn ?
etc. All the above should be specified prior to writing the class (otherwise, how do you know what to write?)
I would create a test method for each of these scenarios, perform a setup and the action, and then use one (or more, if necessary) asserts to determine that all is well. Don't forget that in some cases you may be testing that an exception is thrown, and consequently you will want to check for a break in control flow. That wouldn't use assert.
Here's a possible example (imports etc. omitted)
public void testBalanceAfterTenPoundDeposit() {
// note the interface/impl separation so I can test different
// implementations with the same interface (this is Test By Contract)
CurrentAccount ca = new CurrentAccountImpl();
// check the starting balance
Assert.assertEquals(ca.settlement(), 0);
// deposit
ca.deposit(10);
// do I have £10 ?
Assert.assertEquals(ca.settlement(), 10);
}
It's important to note that this sort of testing should really be focused on the component (unit) as a black box. That is, the test should really be agnostic to the implementation and you wouldn't explicitly test the array implementation. I should be able to insert my own implementation (or rewrite yours) and the tests should remain working (that's the principle of a regression test).
Having said that, if you know of obvious limitations of the implementation (e.g. your fixed size array) you should try and stress that (e.g. in this situation, perform 11 inserts)