This test class:
#RunWith(Theories.class)
public class TheoriesConfusion
{
#DataPoints
public static int[] ints()
{
System.out.println("Generator called");
return new int[]{1, 2, 3, 4, 5};
}
#Theory
public void twoArgTest(int x, int y)
{
assertTrue(x < y || x >= y);
}
}
Prints the following output:
Generator called
Generator called
Generator called
Generator called
Generator called
Generator called
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.137 sec
This is quite counterintuitive, as I expect the data-generating function to be called only once. This has implications when creating random data, or any case where the data-generating method returns different results on each call, so I'd like to understand it.
After some experimentation, I've found that testing an array of length n against a Theory with c args, the generate function is called x times, where x = n^c + n^(c-1) + ... + n^0.
The source is a little difficult to comprehend, but my assumption is that it works something like this (pseudocode):
for firstArg in generateArgs():
for secondArg in generateArgs():
for thirdArg in generateArgs():
testTheory(firstArg, secondArg, thirdArg)
Which makes some sense, basically it's just not caching the results of the method, so if you want the method to be called just once, you have to annotate a static field, like:
#DataPoints
public static int[] ints = ints();
public static int[] ints()
{
System.out.println("Generator called");
return new int[]{1, 2, 3, 4, 5};
}
Related
I have written tests which call a method to find the missing number in a given 1 to n array. The test code is as follows:
public class MissingNumberInArrayTest {
private int arrElements;
private int[] arr;
#Test
public void mustReturnMissingNumberCase1() {
// Given
arrElements = 5;
arr = new int[]{1, 2, 3, 4};
// When & Then
callAssertion(arr, arrElements, 5);
}
#Test
public void mustReturnMissingNumberCase2() {
// Given
arrElements = 5;
arr = new int[]{2, 3, 4, 5};
// When & Then
callAssertion(arr, arrElements, 1);
}
public void callAssertion(int[] arr, int arrElements, int expected) {
// When
int missingNumber = MissingNumberInArray.findMissingNumber(arr, arrElements);
// Then
Assertions.assertEquals(expected, missingNumber);
}
}
As you can see, I have multiple cases to test and have used the assertion in common method which takes input and asserts the data. Is this a good way of writing tests?
This question doesn't have an exact answer (IMO), so it will probably be deleted soon.
I wouldn't do it for simple cases like your example but if it makes your life easier, I don't see anything wrong with it. You could rename the method as assertMissingNumber and it wouldn't be that much different than usual (although the method you really want to test is in the assertion call).
I think your specific example is perfect for parameterized tests, though.
This is what it would look like with Junit 4:
#RunWith( Parameterized.class )
public class MissingNumberInArrayTest {
#Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList( new Object[][] {
{ 5, new int[]{1, 2, 3, 4}, 5},
{ 5, new int[]{2, 3, 4, 5}, 1}
});
}
#Parameterized.Parameter(0)
public int arrElements;
#Parameterized.Parameter(1)
public int[] arr;
#Parameterized.Parameter(2)
public int expected;
#Test
public void mustReturnMissingNumberCase() {
// When
int missingNumber = MissingNumberInArray.findMissingNumber(arr, arrElements);
// Then
assertEquals( expected, missingNumber);
}
}
You can check on the JUnit documentation for examples with JUnit 5
I have just started learning Java as part of my university course and am having a problem with y first project. I am just starting to create a project that basically sorts coins. I am trying to make a method called printCoinList() that prints the contents of a coin list, indicating what denominations are currently in circulation (i.e "The current coin denominations are in circulation: 200,100,50,20,10), in pennies.
So far I have declared my instance fields, created a parameter and attempted to make this method. My only issue is when i try and test it in the main() method it seems to have a problem with me using an array as the coinList parameter. This is what I have so far:
public class CoinSorter {
//Instance Fields
String currency;
int minCoinIn;
int maxCoinIn;
int[] coinList;
//constructor
public CoinSorter(String Currency, int minValueToExchange, int maxValueToExchange, int[] initialCoinList) {
currency=Currency;
minCoinIn=minValueToExchange;
maxCoinIn = maxValueToExchange;
coinList= initialCoinList;
}
public void printCoinList() {
System.out.println("The current coin denominations are in circulation"
+ coinList);
}
public static void main(String[] args) {
//An example
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, {10,20,50,100,200});
The only problems seems to be in exampleOne as when I take this out the rest of the code seems to run fine.
The error message is:
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
The constructor CoinSorter(String, int, int, int, int, int, int, int) is undefined
Syntax error on token "{", delete this token
Syntax error on token "}", delete this token
So does anyone know what I am doing wrong?
This is because an array initializer may only be specified at declaration site or as part of an array creation expression. (JLS § 10.6)
Below is an array initializer at declaration site.
int[] array = { 2, 3, 5, 7, 11 };
This is short for
int[] array = new int[] { 2, 3, 5, 7, 11 };
However, it may not be used as 'array literal', unlike a string literal. That means that you must write out the array creation expression:
new CoinSorter("pounds", 0, 10000, new int[] { 10, 20, 50, 100, 200 });
Arrays in java can be declared/initialized using one of the following ways.
int[] myIntArray = {10,20,50,100,200};
int[] myIntArray = new int[]{10,20,50,100,200};
replace CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, {10,20,50,100,200});
with
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, myIntArray );
OR
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, new int[]{10,20,50,100,200});
Firt of all , in java , you need to specify the type of your Array :
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, new int[]{10,20,50,100,200});
Then your "printCoinList" method will not work as excepted , this should print :
The current coin denominations are in circulation [I#7852e922
Your final code should be :
import java.util.Arrays;
public class CoinSorter {
//Instance Fields
String currency;
int minCoinIn;
int maxCoinIn;
int[] coinList;
//constructor
public CoinSorter(String Currency, int minValueToExchange, int maxValueToExchange, int[] initialCoinList) {
currency=Currency;
minCoinIn=minValueToExchange;
maxCoinIn = maxValueToExchange;
coinList= initialCoinList;
}
public void printCoinList() {
System.out.println("The current coin denominations are in circulation : "
+ Arrays.toString(coinList));
}
public static void main(String[] args) {
//An example
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, new int[]{10,20,50,100,200});
exampleOne.printCoinList();
}
}
Result :
The current coin denominations are in circulation : [10, 20, 50, 100, 200]
Good luck :) I hope I could access to you future exchange and buy and hold some crypto :D
before creating an object CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, {10,20,50,100,200}); declare and initialize an array int arr[]={10,20,50,100,200}and then pass it in the constructor rather than passing {10,20,50,100,200}
like this
int arr[]={10,20,50,100,200};
CoinSorter exampleOne = new CoinSorter("pounds", 0, 10000, arr);
I cannot figure out if it is a missing feature, see the JUnit issue 1154 and my comment there, or just my inability to write properly the syntax for name of the #ParameterizedTest in JUnit5. Given the fact that issue has been open for last 3 years, I'm afraid I cannot wait till I get answer or even the implementation there, so I'm trying to ask here as well.
In my example ParameterizedTest, I'm using #MethodSource, which returns a Stream of ints arrays and I would like to use only those array's attributes instead of all {arguments} which is anyway same if I use {0}. Hard to explain verbally here, better to use code example bellow.
package net.delphym.unittests;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
class DynamicTestCreationTestParam {
private static Stream<int[]> data() {
return Stream.of(new int[][] { {1, 2, 2}, {5, 3, 15}, {121, 4, 484} });
}
#DisplayName("Multiplication test")
#ParameterizedTest(name = "#{index} for {arguments}: {0} x {1} = {2}")
#MethodSource("data")
void testWithStringParameter(int[] data) {
MyClass tester = new MyClass();
int m1 = data[0];
int m2 = data[1];
int expected = data[2];
assertEquals(expected, tester.multiply(m1, m2));
}
// class to be tested
class MyClass {
public int multiply(int i, int j) {
return i *j;
}
}
}
The test results output looks something like this for the 1st run: #1 for [1, 2, 2]: [1, 2, 2] x {1} = {2}
Ideally, I would like to see this: #1 multiply: 1 x 2 = 2.
So question is what would be the #ParametrizedTest(name) definition to match my expectation?
I was trying something like this:
name = "#{index} multiply : {[0][0]} x {[0][1]} = {[0][2]}"
But it is syntacticly incorrect.
Alright, as suggested by #Murat Karagöz, I'm only reposting suggestions which I receive on Github to have all at one place.
In my opinion, without no doubt, those elegant solutions are only workarounds to the JUnit5 #ParameterizedTest API limitations, but they both satisfied my needs.
Proposed workaround #1 if I don't need to use int[]
Solution presented here is preserving #MethodSource("data") and only altering its return type. Now the data() method returns Stream of Arguments objects (3 in each), instead Stream of int arrays (of 3 in each).
Then the code reads:
private static Stream<Arguments> data() {
return Stream.of(Arguments.of(1, 2, 2), Arguments.of(5, 3, 15), Arguments.of(121, 4, 484));
}
#DisplayName("Multiplication test")
#ParameterizedTest(name = "#{index}: {0} x {1} = {2}")
#MethodSource("data")
void testWithStringParameter(int m1, int m2, int expected) {
MyClass tester = new MyClass();
assertEquals(expected, tester.multiply(m1, m2));
}
Credits goes to Marc Philipp for his post # Github.
Proposed workaround #2 to unroll test data to match display needs##
This solution provided by sormarus suggest to use #CsvSource instead of the #MethodSource and provides the set of the test data within the annotation.
I like this as simple example how to quickly prepare same basic variation without a need to have a specific data generating method.
The code looks like this:
#DisplayName("Multiplication test")
#ParameterizedTest(name = "#{index} multiply: {0} x {1} = {2}")
#CsvSource({"1,2,2", "3,5,15", "121,4,484"})
void testWithStringParameter(int m1, int m2, int expected) {
MyClass tester = new MyClass();
assertEquals(expected, tester.multiply(m1, m2));
}
Just for reference, I've taken like one high school class on Java, so this question should be super easy to respond to. I'm just having trouble figuring out a way to call a class method, the constructor, as a new object without putting in new values for the parameters. The constructor looks like this:
public Skills(int courage, int intelligence, int stamina, int crafting,
int blacksmithery, int herbalism, int slingSkill,
int bowSkill, int swordSkill, int armor, int stealth, int
lifeForceSkill){
this.courage = courage;
this.intelligence = intelligence;
this.stamina = stamina;
this.crafting = crafting;
this.blacksmithery = blacksmithery;
this.herbalism = herbalism;
this.slingSkill = slingSkill;
this.bowSkill = bowSkill;
this.swordSkill = swordSkill;
this.armor = armor;
this.stealth = stealth;
this.lifeForceSkill = lifeForceSkill;
}
And when I establish it it my main method I do this:
Skills skills = new Skills(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
To set all the levels to 1 and then I have the object skills to work with later. (Am I getting the terms constructors and objects mixed up? If it's wrong just switch them all in your mind I think I'm being consistent with it at least).
The problem is I have a toString() method in the Skills class that I want to call in a separate method in the main class.
else if (optionChoice.equalsIgnoreCase("View Skills")){
Skills skills = new Skills();
System.out.println(skills.toString());
break;
}
Of course the object I create here throws an error because I cannot just write Skills(); without putting in all twelve values for the parameter. So how do I call the method from the Skills class when I can't create an object for it? It's imperative to functionality that the values do not change, and since they are variable and change with the program, I can't put any value in for them.
I know it's probably a super basic question but that's the level I'm at so any super basic answers would be helpful. Shouldn't take too much of your time. Thanks in advance!
Why not just make your skills object a static field instead of creating it within the main method?
public class WhateverYourClassIsCalled {
static Skills skills = new Skills(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
public static void main(String[] args) {
// ...
}
void yourOtherMethod() {
// ...
else if (optionChoice.equalsIgnoreCase("View Skills")) {
System.out.println(skills.toString());
break;
}
}
}
I am creating a method where if you pass in a parameter of type Random, then it will return a random object. Here is basically what I am trying to do:
public T choose(Random r) {
int randomInt = r.nextInt(randomList.size()); // randomList is just a instance variable
return randomList.get(randomInt);
}
The random list has this the following strings:[2, 2, 2, 1, 1, 1, 1, c, c, c, a, a, a, a]
Then I made a driver with the following code
for (int i = 0; i < 10; i++) {
System.out.print(rndList.choose(rnd)); // rnd is initialized as a static Random variable
}
However my outputs are not coming out random. I used the debugger and found out that my choose method generates an integer that is relatively low, so it will always print out either 2's or 1's but never c's or a's. I can't figure out why this is happening and help would be greatly appreciated.
EDIT: Problem was solved. I left out alot of detail, but when I called the size() method, that was something I overwrote which had an error which would return a smaller number than I would have liked. Thanks to dtech for noticing my silly mistake. Thank you for everyone who tried to help me!
At first glance nothing seems wrong with the code, so it might just be a random result. But your "Print it and check" method is very unreliable. Just use something like this:
final int N = 10000; // test 10.000 times
HashTable<Object, Integer> count = new HashTable(N);
for(int i=0;i < N;i++){
Object o = rndList.choose(rnd);
count.put(o, (count.get(o)==null?0:count.get(o))+1);
}
for(Map.Entry<Object, Integer> map : count.entrySet()){
System.out.println(String.format("%s: %d", map.getKey().toString(), map.getValue()));
}
This will print on average something like:
2: 1429
1: 2857
c: 2143
a: 2857
Only if the numbers differ creatly you should be concerned.
Also make sure that you use the new Random() constructor, not new Random(somenumber). If you use the latter you will get the same number sequence every time.
send you random initialization code, are you getting exactly the same results each time? are you using a seed to create the Random object?
Here is the code I used. You need to provide more code to see why yours doesn't work.
public class Main<T> {
private List<T> randomList = new ArrayList<T>();
public T choose(Random r) {
int randomInt = r.nextInt(randomList.size()); // randomList is just a instance variable
return randomList.get(randomInt);
}
public static void main(String... args) throws IOException, InterruptedException, ExecutionException {
Main<String> rndList = new Main<String>();
rndList.randomList.addAll(Arrays.asList("2, 2, 2, 1, 1, 1, 1, c, c, c, a, a, a, a".split(", ")));
Random rnd = new Random();
for (int i = 0; i < 10; i++) {
System.out.print(rndList.choose(rnd)); // rnd is initialized as a static Random variable
}
}
}
prints
1ca1caa1a2