I am just trying to go through with Spock test framework and as per the documentation it looks better than JUnit and tried to write the simple test case but somehow it's not working.
Class:
public class SwapTwoNumber {
public void swapNumber(int a,int b){
a =a+b;
b=a-b;
a=a-b;
}
}
Spock Test Groovy:
import spock.lang.Specification
class SwapTwoNumberTest extends Specification{
def "swap to number as given. #a and #b"() {
given:
int a = 5
int b = 6
when:
SwapTwoNumber sw =Mock()
sw.swapNumber(a,b)
then:
a==6
b==5
}
}
I am not familiar with the testing framework you mentioned but this is most likely not a framework problem. You are passing primitives arguments.
As stated in the docs
Primitive arguments, such as an int or a double, are passed into methods by value. This means that any changes to the values of the parameters exist only within the scope of the method. When the method returns, the parameters are gone and any changes to them are lost.
Related
I have a test case where I have provided my test data in the form of enums. Like
enum TestTransactions {
TestTransactions(Transaction T1, Transaction T2, String expectedOutput){}
}
In my Test class, I have to use it as:
class Test {
private final static int REPETITION_COUNT = TestTransactions.values().length;
#RepeatedTest(value=REPETITION_COUNT)
private void testAllTransactions(RepetitionInfo info) {
TestTransactions currentTest = TestTransactions.values()[info.getCurrentRepetition()];
logger.info("Executing test for " + currentTest.name());
setExpectationsFor(currentTest);
whenControllerIsCalled();
Assert.assertEquals(currentTest.getExpectedOutput(), result.getBody());
}
}
Here this line #RepeatedTest(value=REPETITION_COUNT) is giving compilation error saying "Attribute value must be constant."
Is there any way to achieve this? Though I have tried assigning REPETITION_COUNT (declared as final) inside constructor and static block as well and during declaration as shown in this example.
If I understand your use case correctly, you want to use #ParameterizedTest with #EnumSource instead of #RepatedTest - this is how JUnit5 supports such use case out of the box.
First, add dependency on org.junit.jupiter:junit-jupiter-params (which provides support for #ParameterizedTest) and then:
class Test {
#ParameterizedTest
#EnumSource
void testAllTransactions(TestTransactions currentTest) {
logger.info("Executing test for " + currentTest.name());
setExpectationsFor(currentTest);
whenControllerIsCalled();
Assertions.assertEquals(currentTest.getExpectedOutput(), result.getBody());
}
}
Also side notes for JUnit 5:
#Test methods should be package-private (no visibility qualifier), not private
use Assertions and not Assert
What you’re experiencing is a constraint of the Java compiler. Unless the Java language specification will be changed you cannot do what you want.
What you could do is make a feature request for Jupiter to also accept a value provider, e.g. of type Class<? extends Supplier<Integer>>. Or you could simulate it using Jupiter‘s dynamic tests feature.
I found something strange in my project. I create a test class using JUnit to test my service layer. The service layer itself is not my question. My problem is, I don't know why after I assigned a value to an int variable in my first test method, and then when I try to use that value in the second test method, the variable value is 0
Executed in order because I use #FixMethodOrder(MethodSorters.NAME_ASCENDING)
int id;
#Test
public void firstMethodToTest() {
id = 10;
System.out.println(id); // here printed correctly 10
}
#Test
public void secondMethodToTest() {
System.out.println(id); // here printed 0
}
I also try to change int to Integer but it returns null not 0 anymore.
I wonder if in JUnit Test class like this, Java variable acts differently.
Thanks.
Thanks to #Ivan for giving me a clue to the answer
For each test method (the method annotated with #Test), a new instance of YourTestClass will be created. This is the behavior of Junit.
So, the point is if you want to use a class member for all test methods, just declare the variable as static. In my case: static int id;
In JUnit 4 it was easy to test invariants across a bunch of classes by using the #Parameterized annotation. The key thing is that a collection of tests are being run against a single list of arguments.
How to replicate this in JUnit 5, without using JUnit-vintage?
#ParameterizedTest is not applicable to a test class. #TestTemplate sounded like it might be appropriate, but that annotation's target is also a method.
An example of such a JUnit 4 test is:
#RunWith( Parameterized.class )
public class FooInvariantsTest{
#Parameterized.Parameters
public static Collection<Object[]> data(){
return new Arrays.asList(
new Object[]{ new CsvFoo() ),
new Object[]{ new SqlFoo() ),
new Object[]{ new XmlFoo() ),
);
}
private Foo fooUnderTest;
public FooInvariantsTest( Foo fooToTest ){
fooUnderTest = fooToTest;
}
#Test
public void testInvariant1(){
...
}
#Test
public void testInvariant2(){
...
}
}
The parameterized test feature in JUnit 5 doesn't provide the exact same features than those provided by JUnit 4.
New features with more flexibility were introduced... but it also lost the JUnit4 feature where the parameterized test class uses the parameterized fixtures/assertions at the class level that is for all test methods of the class.
Defining #ParameterizedTest for each test method by specifying the "input" is so needed.
Beyond that lack I will present the main differences between the 2 versions and how to use parameterized tests in JUnit 5.
TL;DR
To write a parameterized test that specifies a value by case to test as your in your question,
org.junit.jupiter.params.provider.MethodSource should do the job.
#MethodSource allows you to refer to one or more methods of the test
class. Each method must return a Stream, Iterable, Iterator, or array
of arguments. In addition, each method must not accept any arguments.
By default such methods must be static unless the test class is
annotated with #TestInstance(Lifecycle.PER_CLASS).
If you only need a single parameter, you can return instances of the
parameter type directly as demonstrated by the following example.
As JUnit 4, #MethodSource relies on a factory method and may also be used for test methods that specify multiple arguments.
In JUnit 5, it is the way of writing parameterized tests the closest to JUnit 4.
JUnit 4 :
#Parameters
public static Collection<Object[]> data() {
JUnit 5 :
private static Stream<Arguments> data() {
Main improvements :
Collection<Object[]> is become Stream<Arguments> that provides more flexibility.
the way of binding the factory method to the test method differs a little.
It is now shorter and less error prone : no more requirement to create a constructor and declares field to set the value of each parameter. The binding of the source is done directly on the parameters of the test method.
With JUnit 4, inside a same class, one and only one factory method has to be declared with #Parameters.
With JUnit 5, this limitation is lifted : multiple methods may indeed be used as factory method.
So, inside the class, we can so declare some test methods annotated with #MethodSource("..") that refer different factory methods.
For example here is a sample test class that asserts some addition computations :
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.api.Assertions;
public class ParameterizedMethodSourceWithArgumentsTest {
#ParameterizedTest
#MethodSource("addFixture")
void add(int a, int b, int result) {
Assertions.assertEquals(result, a + b);
}
private static Stream<Arguments> addFixture() {
return Stream.of(
Arguments.of(1, 2, 3),
Arguments.of(4, -4, 0),
Arguments.of(-3, -3, -6));
}
}
To upgrade existing parameterized tests from JUnit 4 to JUnit 5, #MethodSource is a candidate to consider.
Summarize
#MethodSource has some strengths but also some weaknesses.
New ways to specify sources of the parameterized tests were introduced in JUnit 5.
Here some additional information (far being exhaustive) about them that I hope could give a broad idea on how deal with in a general way.
Introduction
JUnit 5 introduces parameterized tests feature in these terms :
Parameterized tests make it possible to run a test multiple times with
different arguments. They are declared just like regular #Test methods
but use the #ParameterizedTest annotation instead. In addition, you
must declare at least one source that will provide the arguments for
each invocation.
Dependency requirement
Parameterized tests feature is not included in the junit-jupiter-engine core dependency.
You should add a specific dependency to use it : junit-jupiter-params.
If you use Maven, this is the dependency to declare :
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.0.0</version>
<scope>test</scope>
</dependency>
Sources available to create data
Contrary to JUnit 4, JUnit 5 provides multiple flavors and artifacts to write parameterized tests
The ways to favor depend generally on the source of data you want to use.
Here are the source types proposed by the framework and described in the documentation :
#ValueSource
#EnumSource
#MethodSource
#CsvSource
#CsvFileSource
#ArgumentsSource
Here are the 3 main sources I actually use with JUnit 5 and I will present:
#MethodSource
#ValueSource
#CsvSource
I consider them as basic as I write parameterized tests. They should allow to write in JUnit 5, the type of JUnit 4 tests that you described.
#EnumSource, #ArgumentsSource and #CsvFileSource may of course be helpful but they are more specialized.
Presentation of #MethodSource, #ValueSource and #CsvSource
1) #MethodSource
This type of source requires to define a factory method.
But it also provides much flexibility.
In JUnit 5, it is the way of writing parameterized tests the closest to JUnit 4.
If you have a single method parameter in the test method and you want to use any type as source, #MethodSource is a very good candidate.
To achieve it, define a method that returns a Stream of the value for each case and annotate the test method with #MethodSource("methodName") where methodName is the name of this data source method.
For example, you could write :
import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ParameterizedMethodSourceTest {
#ParameterizedTest
#MethodSource("getValue_is_never_null_fixture")
void getValue_is_never_null(Foo foo) {
Assertions.assertNotNull(foo.getValue());
}
private static Stream<Foo> getValue_is_never_null_fixture() {
return Stream.of(new CsvFoo(), new SqlFoo(), new XmlFoo());
}
}
If you have multiple method parameters in the test method and you want to use any type as source, #MethodSource is also a very good candidate.
To achieve it, define a method that returns a Stream of org.junit.jupiter.params.provider.Arguments for each case to test.
For example, you could write :
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.api.Assertions;
public class ParameterizedMethodSourceWithArgumentsTest {
#ParameterizedTest
#MethodSource("getFormatFixture")
void getFormat(Foo foo, String extension) {
Assertions.assertEquals(extension, foo.getExtension());
}
private static Stream<Arguments> getFormatFixture() {
return Stream.of(
Arguments.of(new SqlFoo(), ".sql"),
Arguments.of(new CsvFoo(), ".csv"),
Arguments.of(new XmlFoo(), ".xml"));
}
}
2)#ValueSource
If you have a single method parameter in the test method and you may represent the source of the parameter from one of these built-in types (String, int, long, double), #ValueSource suits.
#ValueSource defines indeed these attributes :
String[] strings() default {};
int[] ints() default {};
long[] longs() default {};
double[] doubles() default {};
You could for example use it in this way :
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
public class ParameterizedValueSourceTest {
#ParameterizedTest
#ValueSource(ints = { 1, 2, 3 })
void sillyTestWithValueSource(int argument) {
Assertions.assertNotNull(argument);
}
}
Beware 1) you must not specify more than one annotation attribute.
Beware 2) The mapping between the source and the parameter of the method can be done between two distinct types.
The type String used as source of data allows particularly, thanks to its parsing, to be converted into multiple other types.
3) #CsvSource
If you have multiple method parameters in the test method, a #CsvSource may suit.
To use it, annotate the test with #CsvSource and specify in a array of String each case.
Values of each case are separated by a comma.
Like #ValueSource, the mapping between the source and the parameter of the method can be done between two distinct types.
Here is an example that illustrates that :
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
public class ParameterizedCsvSourceTest {
#ParameterizedTest
#CsvSource({ "12,3,4", "12,2,6" })
public void divideTest(int n, int d, int q) {
Assertions.assertEquals(q, n / d);
}
}
#CsvSource VS #MethodSource
These source types serve a very classic requirement : mapping from the source to multiple method parameters in the test method.
But their approach is different.
#CsvSource has some advantages : it is clearer and shorter.
Indeed, parameters are defined just above the tested method, no requirement to create a fixture method that may in addition generate "unused" warnings.
But it also has an important limitation concerning mapping types.
You have to provide an array of String. The framework provides conversion features but it is limited.
To summarize, while the String provided as source and the parameters of the test method have the same type (String->String) or rely on built-in conversion (String->int for example), #CsvSource appears as the way to use.
As it is not the case, you have to make a choice between
keeping the flexibility of #CsvSource by creating a custom converter (ArgumentConverter subclass) for conversions not performed by the framework or using #MethodSource with a factory method that
returns Stream<Arguments>. It has the drawbacks described above but it also has the great benefit to map out-of-the box any type from the source to the parameters.
Argument Conversion
About the mapping between the source (#CsvSource or #ValueSource for example) and the parameters of the test method, as seen, the framework allows to do some conversions if the types are not the same.
Here is a presentation of the two types of conversions :
3.13.3. Argument Conversion
Implicit Conversion
To support use cases like #CsvSource, JUnit Jupiter provides a number
of built-in implicit type converters. The conversion process depends
on the declared type of each method parameter.
.....
String instances are currently implicitly converted to the following
target types.
Target Type | Example
boolean/Boolean | "true" → true
byte/Byte | "1" → (byte) 1
char/Character | "o" → 'o'
short/Short | "1" → (short) 1
int/Integer | "1" → 1
.....
For example in the previous example, an implicit conversion is done between String from source and int defined as parameter:
#CsvSource({ "12,3,4", "12,2,6" })
public void divideTest(int n, int d, int q) {
Assertions.assertEquals(q, n / d);
}
And here, an implicit conversion is done from String source to LocalDate parameter:
#ParameterizedTest
#ValueSource(strings = { "2018-01-01", "2018-02-01", "2018-03-01" })
void testWithValueSource(LocalDate date) {
Assertions.assertTrue(date.getYear() == 2018);
}
If for two types, no conversion is provided by the framework,
which is the case for custom types, you should use an ArgumentConverter.
Explicit Conversion
Instead of using implicit argument conversion you may explicitly
specify an ArgumentConverter to use for a certain parameter using the
#ConvertWith annotation like in the following example.
JUnit provides a reference implementation for clients who need to create a specific ArgumentConverter.
Explicit argument converters are meant to be implemented by test
authors. Thus, junit-jupiter-params only provides a single explicit
argument converter that may also serve as a reference implementation:
JavaTimeArgumentConverter. It is used via the composed annotation
JavaTimeConversionPattern.
Test method using this converter :
#ParameterizedTest
#ValueSource(strings = { "01.01.2017", "31.12.2017" })
void testWithExplicitJavaTimeConverter(#JavaTimeConversionPattern("dd.MM.yyyy") LocalDate argument) {
assertEquals(2017, argument.getYear());
}
JavaTimeArgumentConverter converter class :
package org.junit.jupiter.params.converter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZonedDateTime;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalQuery;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.jupiter.params.support.AnnotationConsumer;
/**
* #since 5.0
*/
class JavaTimeArgumentConverter extends SimpleArgumentConverter
implements AnnotationConsumer<JavaTimeConversionPattern> {
private static final Map<Class<?>, TemporalQuery<?>> TEMPORAL_QUERIES;
static {
Map<Class<?>, TemporalQuery<?>> queries = new LinkedHashMap<>();
queries.put(ChronoLocalDate.class, ChronoLocalDate::from);
queries.put(ChronoLocalDateTime.class, ChronoLocalDateTime::from);
queries.put(ChronoZonedDateTime.class, ChronoZonedDateTime::from);
queries.put(LocalDate.class, LocalDate::from);
queries.put(LocalDateTime.class, LocalDateTime::from);
queries.put(LocalTime.class, LocalTime::from);
queries.put(OffsetDateTime.class, OffsetDateTime::from);
queries.put(OffsetTime.class, OffsetTime::from);
queries.put(Year.class, Year::from);
queries.put(YearMonth.class, YearMonth::from);
queries.put(ZonedDateTime.class, ZonedDateTime::from);
TEMPORAL_QUERIES = Collections.unmodifiableMap(queries);
}
private String pattern;
#Override
public void accept(JavaTimeConversionPattern annotation) {
pattern = annotation.value();
}
#Override
public Object convert(Object input, Class<?> targetClass) throws ArgumentConversionException {
if (!TEMPORAL_QUERIES.containsKey(targetClass)) {
throw new ArgumentConversionException("Cannot convert to " + targetClass.getName() + ": " + input);
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
TemporalQuery<?> temporalQuery = TEMPORAL_QUERIES.get(targetClass);
return formatter.parse(input.toString(), temporalQuery);
}
}
This question already has answers here:
How do I test a class that has private methods, fields or inner classes?
(58 answers)
Closed 6 years ago.
I read this answer about unit-test a wrapper method. In following code:
public static Object methodA(Object arg) {
if (arg == null) {
return null;
} else {
return methodB();
}
}
It looks reasonable that I don't need to test all functionality in methodB(), just test 2 cases where arg is null and not because methodB() should be tested already.
However, if methodB() is private method, I should test all functionality that methodB() will provide because according to another answer, private methods are implementation details.
The problem is, if I have 2 methods methodA1() and methodA2() and they all call methodB() like this:
public static MyObject methodA1(int x) {
MyObject obj = new MyObject();
obj.setX(x);
return methodB(obj);
}
public static MyObject methodA2(int y) {
MyObject obj = new MyObject();
obj.setY(y);
return methodB(obj);
}
private static MyObject methodB(MyObject obj) {
// doSomething with obj
return obj;
}
Should I test methodA1() and methodA2() separately? Or I can just test the private method methodB() because methodA1() and methodA2() are just wrapper methods of methodB(), so if methodB() is correctly tested, I won't need to test methodA1() and methodA2() at all.
EDIT:
I wrote separate tests for public methods at first. But the problem is, if there are many variations of the methodA, and some of the functionalities / requirements are shared in some of them, then the code of the test cases will be duplicated. That's why I wonder if I should test private method methodB().
The real problem I met is I have requirement to add a record in database, and there are many different APIs I should provide. For example, I should provide:
MyObject addMale(String name, String job); // fill sex with "Male"
MyObject addStudent(String name, String sex); // fill job with "Student"
And all of them checks the parameter is valid or not, fill the field that is not specified and call the private method to actually insert the record into the database, which is so called methodB().
There are many APIs like this, so if I can only test methodB() with all fields situation, maybe I can reduce the duplication of the test cases, but is that a good practice to do the unit tests like this?
EDIT2:
I know I can test private methods because I used reflection in my other test cases, and I know it can invoke private methods, too. But my question is about in this situation, testing private methods is a proper solution or not.
This is actually a question I've wondered about for awhile, and while it's not necessarily correct, I'll share my opinion.
The main problem is that private methods are hard to test. I did some Googling, and there some tools and techniques that let you access private methods (reflection is the main one I encountered), but they seemed a little convoluted.
But the reason you wrote a private method is because there is another method, a public method, that calls it. So while you can't directly test the private method, you can check its functionality by testing the public method which calls it.
If I were you, I would extensively test methodA1() and methodA2(), ensuring that all the functionality of methodB() is tested by the tests you run on the public methods.
You've received a few opinions why you should write unit tests for methodA1() and methodA2(); you've pushed back on them. You asked the question here; you are clearly looking for some justification for not writing complete unit test for them. Let's see if we can give you the answer you want. ;-)
From what you've added in your edit, it looks like you have a variation on a builder pattern. Eg)
MyObject o = new Builder().setX(1).setY(2).setZ(3).setW(4).build();
How would we test the builder?
Since the builder has 4 attributes, which could be set in any order, there would be 4! = 24 different orderings. A complete test suite would have to include:
#Test public void testXYZW() { ... }
#Test public void testXYWZ() { ... }
// ... 21 more permutations ...
#Test public void testWZYX() { ... }
But is that all? No! Some of those attributes could have default values, so we would have to test those patterns as well. Total orderings is now P(4,4)+P(4,3)+P(4,2)+P(4,1)+P(4,0) = 24+24+12+4+1 = 85 unit test.
#Test public void testXWY() { ... }
#Test public void testWY() { ... }
#Test public void testZ() { ... }
// ... 61 more permutations ordering
And this only tests each permutation of X, Y, Z, W attributes with one test value per attribute per test. It is clearly unwieldy to write an exhaustive set of tests for every possible combination and permutation.
The designer of the builder class would understand that the permutation of the attribute settings does not affect the resulting construction; writing tests for permutations of the order does not actually increase the test coverage. Tests for omitted attributes are useful, as they test the default values. Testing of different combinations of the omitted attributes again would not increase the test coverage. So, after careful thought, only two tests might be required:
#Test
public void testXYZW() {
MyObject o = new Builder().setX(1).setY(2).setZ(3).setW(4).build();
assertThat(o.wasBuiltProperly());
}
#Test void testDefaults() {
MyObject o = new Builder().build();
assertThat(o.wasBuiltProperlyFromDefaults());
}
If proper, complete testing of methodB() is being done, then you could safely get away with testing only the validation of inputs in methodA1() and methodA2().
#Test void testMethodA1Professor() {
MyObject o = methodA1("professor");
assertThat(o.wasBuiltProperlyWithProfessor());
}
#Test void testMethodA1WithNull() {
MyObject o = methodA1(null);
assertThat(o.wasBuiltProperlyWithNull());
}
Oftentimes, when a private method is used by multiple methods inside a class, there's a distinct concept lurking behind it. It may deserve to have its own class.
it should not be used outside of the class
There are a few ways you could extract methodB to its own class and not make it available as part of your public API :
Nest it inside the class that uses it and give it a restricted scope
Put it in another module at a lower level. Make that module available from your API but not from the clients.
I would like to ask a simple question about the best practice of testing methods. Lets say I have a class like this,
public static class MyClass {
private int num;
public MyClass(int num) {
this.num = add(num);
}
public int getNum() {
return num;
}
private int add(int num) {
return num + 1;
}
}
Is it better to write a single test method like this
Assert.assertEquals(3, new MyClass(2).getNum());
or to stub the add method and write a test method for constructor to assert inner num value and getNum seperately? I know it is a simple class and no need for such complications for this one but I wanted to point out the issue with a simple example. It would be appreciated if you explain your answer, thanks in advance.
Let's say your test fails.
Will you be able to see right away where your error is located? No. You'd have to check both methods manually.
Let's say your test succeeds.
Can you be sure both of your methods work correctly? No. They both might do something not intended just returning the checked for value by accident
So my clear advice on this: One test per method.
I think, you should test both ways - by stubbing private methods and without stubbing private methods.
Isolation of unit test methods is important but execution of full logical path is equally important too so test both ways and name your test methods accordingly.