Refactoring TDD down to an array of tests - java

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.

Related

Leetcode: My solution works for Individual test cases but not for All test cases together

This is Leetcode question no: 940. Distinct Subsequences II
My code is using Recursion to fetch the subsequences. And I am using an external HashSet to keep count of the unique subsets. The reason I am subtracting one from the size is to incorporate for the empty string because as per the solution we are not supposed to include the empty string. When I run the code for individual test cases, it runs fine. But when the test cases are run together consecutively, my solution is deemed wrong. Can anybody point me in the direction as to where I could be going wrong in my code? Is it to do with the Set usage or the recursion code itself? I am aware that this problem can be solved using Dynamic Programming(which I am yet to tackle), but I just wanted to know if there is a solution possible through Recursion ??
Please refer attached images for solution and test cases runs:
Code that I have written on Leet code
The individual test cases that I have run on Leetcode
The joint test cases run by Leetcode
Code:
class Solution {
static Set<String> myset = new HashSet<String>(0);
public int distinctSubseqII(String s) {
int i=0;
String curr="";
subsets(s, curr, i);
int val = myset.size()-1;
return val;
}
public static void subsets(String str,String curr,int i){
if(i==str.length()){
//System.out.println(curr);
myset.add(curr);
return;
}
subsets(str, curr, i+1);
subsets(str, curr+str.charAt(i), i+1);
}
}
Looking at your code, I can see that the static variable is the problem. The contents of that Set carry over from one call of distinctSubseqII to the next. That means that the size of the Set will be wrong ... for the second and all later calls.
Cutting to the chase ...
You need to do the recursion in a helper function.
In your example, it might look like this.
public class Solution {
public int distinctSubseqII(String s) {
Set<String> myset = new HashSet<String>(0);
return recursive(s, mySet);
}
// Helper function
private int recursive(String s, Set<String> mySet) {
...
// call 'recursive' ... recursively
...
}
}
The "helper function" is a common pattern for recursive solutions.

How to test a method that uses Random(), without arguments and return value, using JUnit or Mockito

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

How do I store information in a method so I can use it in a different method later?

I am very new to Java, so sorry if this is stupid and obvious or worded poorly. I don't really know enough yet to know what I don't know.
So I decided that since I have to learn Java, I'd just jump in head first and try to figure it out as I go. So far, it's worked decently. I'm trying to reinforce some basic concepts I already know by writing small programs that do trivial stuff.
I decided I'd write a little text based adventure game and it's working well so far. I'm using Scanners and Switches to call methods that use Scanners and Switches to call other methods. That's all working fine.
So far it's a very linear straight line, like an old choose your own adventure book. But, I wanted to add a player inventory. I have a very vague idea of how to do it, but I have a pretty solid idea of what I want it to do.
So, basically I want to store a piece of information that says the player has a specific item. I want to be able to test for the presence of more than one item at once. And I want to be able to tell the player what items he has at any point in the game.
I don't really know how to ask the question better.
My best guess is doing something like
int key, potion;
key = 0
potion = 2
and then testing the values of each one
if (key = 0) {
System.out.println("you don't have the key ");
}
if (key > 0) {
System.out.prinln("You unlock the door");
}
I'm doing each new room as a separate method, so the whole game is just a big chain of methods. So my hope is that the information about items can be stored in a separate method that I can access through switches or if/else in the current room method the player is in. So, the player is unlocking a door in room2, which is its own method, and he picked up the key in room1, which is its own method, and the key is stored as an integer in the inventory method. But the key was one use, so the key integer is set to 0 and the method room3 starts. If that makes any sense.
Again, sorry if this is really stupid basic stuff. I'm very new to programming.
No problem and I applaud you for choosing to learn programming. This is basic data structures. If you want to hold a value, in most programming languages, you'll have an array. I think breaking your logic down is a good idea i.e., (store an item, test for > 1, list items). The first step is making this as simple as possible and than maybe adding getters/setters later through refactors. Ultimately, your goal is to make the most basic code work first (like this) and than refactoring towards an object oriented class with getters/setters and/or a HashMap.
1:
public class PlayerInventory
{
private String[] inventoryStr = new String[20]; // basic implementation
inventoryStr[0] = "Phone";
inventoryStr[0] = "Book";
}
2:
int arrayLength = inventoryStr.length;
3:
for(int i=0; i < inventoryStr.length; i++) {
System.out.println( inventoryStr[i] );
}
Refactor (after you write unit tests for this)
1*: (with a list)
import java.util.*;
import java.util.*;
public class CollectionGetterSetter {
private List<String> playerInventory;
public void setPlayerInventory(List<String> inv) {
this.playerInventory = inv;
}
public List<String> getPlayerInventory() {
return this.playerInventory;
}
public static void main(String[] args) {
CollectionGetterSetter app = new CollectionGetterSetter();
List<String> PlayerInventory = new ArrayList();
PlayerInventory.add("phone");
PlayerInventory.add("book");
PlayerInventory.add("glasses");
PlayerInventory.add("nav");
app.setPlayerInventory(PlayerInventory);
System.out.println("Player 1: " + PlayerInventory);
List<String> PlayerInventory2 = new ArrayList();
PlayerInventory2.add("cap");
PlayerInventory2.add("gown");
PlayerInventory2.add("foo");
PlayerInventory2.add("bar");
}
}

Constructing my own classes?

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

Resources for JUnit testing of a class

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)

Categories

Resources