I have a problem when trying to match an array that is passed as a parameter to a method that receives a varargs array.
The anyVararg() matcher that is mentioned in other questions/answers doesn't work for me because I want to make sure the provided array is the one I need.
I reduced the problem to this example which is easier to understand and abstracts the problem (my real issue is production code and has busines logic so it would be confusing for the purpose of this question):
#RunWith(MockitoJUnitRunner.class)
public class UnitTest {
private Object[] objectArray;
private List<Object> expected;
private TestTarget target;
#Before
public void setUp() {
objectArray = new Object[]{ new Object() };
expected = Arrays.asList(new Object(), new Object());
target = Mockito.spy(new TestTarget());
}
#Test
public void testMakeList() { // this pass as eq works well with normal array
doReturn(expected).when(target).toList(Mockito.eq(objectArray));
Assert.assertEquals(expected, target.makeList(objectArray));
}
#Test
public void testMakeList1() { // this one fails as eq is not working with varargs
doReturn(expected).when(target).toList1(Mockito.eq(objectArray));
Assert.assertEquals(expected, target.makeList1(objectArray));
}
#Test
public void testMakeListWithAryEq() { // fails, aryEq is not working with varargs
doReturn(expected).when(target).toList1(AdditionalMatchers.aryEq(objectArray));
Assert.assertEquals(expected, target.makeList1(objectArray));
}
private class TestTarget {
public List<Object> makeList(Object[] objects) {
return toList(objects);
}
public List<Object> makeList1(Object[] objects) {
return toList1(objects);
}
protected List<Object> toList(Object[] objs) {
return null; // Not implemented "Intentionally"
}
protected List<Object> toList1(Object... objs) {
return null; // Not implemented "Intentionally"
}
}
}
When I run the test cases in the class, the first test case will pass but not the other two, neither using eq nor using aryEq. Showing the following trace:
java.lang.AssertionError: expected:<[java.lang.Object#56d5e457, java.lang.Object#7482384a]> but was:<null>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:743)
at org.junit.Assert.assertEquals(Assert.java:118)
at org.junit.Assert.assertEquals(Assert.java:144)
...
This happens because the eq matcher is not working with varargs arrays, is there any alternative to the eq matcher for this use case?
Ok, I think the answer here requires a custom built matcher, which can be implemented in your unit test as so:
private class MyVarargMatcher extends ArgumentMatcher<Object[]> implements VarargMatcher {
private Object[] expectedValues;
MyVarargMatcher(Object... expectedValues) {
this.expectedValues = expectedValues;
}
#Override
public boolean matches(Object varargArgument) {
return new EqualsBuilder()
.append(expectedValues, varargArgument)
.isEquals();
}
}
Then, in testMakeList1() change the first line to this:
Mockito.doReturn(expected).when(target).toList1(Mockito.argThat(new MyVarargMatcher(objectArray)));
Sources:
How to properly match varargs in Mockito
http://maciejmadej.blogspot.com/2011/11/capturing-varargs-argument-using-custom.html
This is no problem with matching varargs. The only limitation is that you have to specify each individual array entry as a matched argument. I have updated your code below to show what I mean. I created a second objectArray2 to make the point clearer. All tests pass:
#RunWith(MockitoJUnitRunner.class)
public class UnitTest {
private Object[] objectArray;
private Object[] objectArray2;
private List<Object> expected;
private TestTarget target;
private Object obj,obj2;
#Before
public void setUp() {
obj = new Object();
obj2 = new Object();
objectArray = new Object[]{ obj };
objectArray2 = new Object[]{ obj, obj2 };
expected = Arrays.asList(new Object(), new Object());
target = Mockito.spy(new TestTarget());
}
#Test
public void testMakeList() { // this pass as eq works well with normal array
doReturn(expected).when(target).toList(Mockito.eq(objectArray));
Assert.assertEquals(expected, target.makeList(objectArray));
}
#Test
public void testMakeList1() { // since objectArray has one entry you need to add one matching argument
doReturn(expected).when(target).toList1(Mockito.eq(obj));
Assert.assertEquals(expected, target.makeList1(objectArray));
}
#Test
public void testMakeListWithAryEq() { // since objectArray2 has two entries you need to add two matching arguments
doReturn(expected).when(target).toList1(Mockito.eq(obj),Mockito.eq(obj2));
Assert.assertEquals(expected, target.makeList1(objectArray2));
}
private class TestTarget {
public List<Object> makeList(Object[] objects) {
return toList(objects);
}
public List<Object> makeList1(Object[] objects) {
return toList1(objects);
}
protected List<Object> toList(Object[] objs) {
return null; // Not implemented "Intentionally"
}
protected List<Object> toList1(Object... objs) {
return null; // Not implemented "Intentionally"
}
}
}
Related
I have a problem with Java's Generic System.
In my program is a wrapper for lists, that should have a method to return it's inner list:
public class Wrapper<T, S extends List<T>> {
private S list;
public Wrapper(S list) {
this.list = list;
}
public S get() {
return list;
}
}
Then there is a Context that holds a Map with different Wrappers and a method that returns the list of the wrapper associated with the id:
public class Context {
private Map<String, Wrapper> map;
public Wrappers() {
map.put("a", new Wrapper(ArrayList<String>());
map.put("b", new Wrapper(LinkedList<Integer>());
}
public <T, S extends List<T>> S getList(String id) {
return map.get(id).get();
}
}
Now when I call getList() I want to have a compiler warning or at least a way to realise an error before a ClassCastException gets thrown.
public class Receiver {
public doSomething() {
Context c = new Context();
c.createWrappers();
// Ok
ArrayList<String> list1 = c.getList("a");
LinkedList<Integer> list2 = c.getList("b");
// Compiler error or some way do check in getList().
ArrayList<Integer> list3 = c.getList("a");
LinkedList<String> list4 = c.getList("b");
}
}
I've actually tried a lot of things like changing the Wrapper definition to:
public class Wrapper<T, S extends List>
But when I want to implement the get() function I run into a problem I can either define the function like this:
public List<T> get() {
return list;
}
or like this
public S get() {
return list;
}
In the first example it would still be possible to do this.
public doSomething() {
//...
LinkedList<String> list = c.getList("a");
}
and in the second example it would be possible to do this.
public doSomething() {
//...
ArrayList<Integer> list = c.getList("a");
}
Is there any way to define the get method in a way like this?
public S<T> get() {
return list;
}
It seems to me like there is no way to check both the type of the list and the type of the elements at the same time.
The compiler has no way of knowing what return type is associated with the particular string you passed (strings cannot be made type-safe).
However, you could replace strings with type-safe marker objects:
class ListId<T> {
public ListId(string name) { ... }
public static final ListId<ArrayList<String>> A = new ListId<>("a");
public static final ListId<LinkedList<Integer>> B = new ListId<>("b");
}
public T getList<T>(ListId<T> id)
It always complains with:
The method add(Matrix<T>) in the type List<Matrix<T>> is not applicable for the arguments (Matrix<String>)
In the line of the Extractor class:
matrixList.add(new Matrix<String>(attributes));
It seems there is a problem in defining my generics in these 3 classes. Is there an easy way to fix that? Tried different ways and can't figure it out.
public class Test {
public static void main(String[] args) {
Extractor<String> extractor = new Extractor<>();
extractor.extract();
}
}
class Extractor<T> {
private List<Matrix<T>> matrixList;
public Extractor() {
this.matrixList = new ArrayList<>();
}
public void extract() {
List<Attribute<String>> attributes = new ArrayList<>();
attributes.add(new Attribute<String>("Test 1"));
attributes.add(new Attribute<String>("Test 2"));
// !!!! The compiler is complaining here!
matrixList.add(new Matrix<String>(attributes));
}
public List<Matrix<T>> getList() {
return matrixList;
}
}
class Matrix<T> {
private List<Attribute<T>> attributes;
public Matrix(List<Attribute<T>> attributes) {
this.attributes = attributes;
}
public List<Attribute<T>> getAttributes() {
return attributes;
}
}
class Attribute<T> {
private T attribute;
public Attribute(T attr) {
attribute = attr;
}
public T getAttr() {
return attribute;
}
}
Your code simply does not make sense. You are making Extractor etc generic, which means you want it to work for different types.
However, in Extractor.extract() method, you are specifically creating a Matrix of String and put it into your List<Matrix<T>> matrixList.
If your code only works for String, then you shouldn't make it generic. Just make List<Matrix<String>> matrixList.
Give it a thought: if now you are creating a Extractor<Integer> intExtractor, and calling intExtractor.extract(), how can it be reasonable for your code to work?
Or, to further polish your design, make it:
interface Extractor<T> {
public List<Matrix<T>> extract();
}
class DummyStringMatrixExtractor implements Extractor<String> {
// useless now, can be put in extract()
private List<Matrix<T>> matrixList;
public Extractor() {
this.matrixList = new ArrayList<>();
}
#Override
public List<Matrix<String>> extract() {
List<Attribute<String>> attributes = new ArrayList<>();
attributes.add(new Attribute<String>("Test 1"));
attributes.add(new Attribute<String>("Test 2"));
matrixList.add(new Matrix<String>(attributes));
return matrixList;
}
// useless now
public List<Matrix<T>> getList() {
return matrixList;
}
}
You can't put Matrix<String> to list of Matrix<T> (T may be any type here). If the Extractor works only with Matrix of type String, remove the type argument from there and set matrixList type to List<Matrix<String>>.
A simple solution is to use matrixList.add((Matrix<T>) new Matrix<String>(attributes)); instead.
The code imposes a very common casting problem of generics.
The approach presented in the code matrixList.add(new Matrix<String>(attributes)) implies that you want to add a Matrix element to container matrixList which is defined to contain generic template type.
You may find simpler illustrations of such hurdles in the documentation over here
In Mockito, is it possible to define the behavior of a mock object in the event that it is type cast, perhaps as one can use Mockito's "when ... thenReturn" functionality to define the behavior of a mock object in the event that of its methods is called?
For example, in the following example class and test...
public class MyClass {
public String myMethod(ObjectString arg) {
ans = (String) arg;
return ans;
}
}
public class MyClassTest {
#Test
public void myMethod_should_convert_to_string() {
MyClass testMyClass = new MyClass();
ObjectString mockObjectString = Mockito.mock(ObjectString.class);
String expected = "expected string returned";
Mockito.when(mockObjectString.IS_CAST_TO_STRING).thenReturn(expected);
String actual = testMyClass.myMethod(mockObjectString);
Assert.assertEquals(expected, actual);
}
}
...is there something I can perhaps replace 'IS_CAST_TO_STRING' with that will cause mockObjectString to be cast to the specific value "expected string returned"?
An instance of ObjectString can never be cast to String. String does not inherit from any class called ObjectString nor does it implement any interface called ObjectString. Casting to String will always throw a ClassCastException unless arg is null.
On the other hand, if your class under test looked like this:
public class MyClass {
public String myMethod(final Object arg) {
final String ans = (String) arg;
return ans;
}
}
Then, you could achieve what you're looking for without Mockito:
#Test
public void myMethod_should_convert_to_string() {
MyClass testMyClass = new MyClass();
String expected = "expected string returned";
String actual = testMyClass.myMethod(expected);
assertEquals(expected, actual);
}
I would like to perform a JUnit Parametrized test with data being external.
I have a list of objects and just need to know how I can convert that to a collection of object arrays. I see the below stack overflow question, but I want to add data from a file which is read from my method.
Parameterized JUnit tests with non-primitive parameters?
Working code: something like this:
#RunWith(Parameterized.class)
public class sampletest {
private BranchMailChildSample branch;
public sampletest(BranchMailChildSample branch)
{
this.branch = branch;
}
#Parameters
public static Collection<Object[]> data()
{
String excel = "C:\\Resources\\TestData\\ExcelSheets\\BranchMail\\branchmail_TestData.xlsx";
ExcelMarshallerTool tool = new ExcelMarshallerTool(excel);
List<BranchMailChildSample> items = tool.unmarshallExcel(BranchMailChildSample.class);
//RIGHT HERE I NEED HELP: Convert list to Collection<Object[]>
//return items as Collection of object arrays
}
#Test
public void test()
{
System.out.println(branch.toString());
}
}
You don't have to convert the list.
#RunWith(Parameterized.class)
public class SampleTest {
#Parameters(name = "{0}")
public static List<BranchMailChildSample> data() {
String excel = "C:\\Resources\\TestData\\ExcelSheets\\BranchMail\\branchmail_TestData.xlsx";
ExcelMarshallerTool tool = new ExcelMarshallerTool(excel);
return tool.unmarshallExcel(BranchMailChildSample.class);
}
#Parameter(0)
public BranchMailChildSample branch;
#Test
public void test() {
System.out.println(branch.toString());
}
}
I used field injection, because it needs less code than constructor injection. Setting the name to the object under test prints more helpful output. Please have a look at the documentation of the Parameterized runner.
You can use constructor injection if you don't like the public field.
#RunWith(Parameterized.class)
public class SampleTest {
#Parameters(name = "{0}")
public static List<BranchMailChildSample> data() {
String excel = "C:\\Resources\\TestData\\ExcelSheets\\BranchMail\\branchmail_TestData.xlsx";
ExcelMarshallerTool tool = new ExcelMarshallerTool(excel);
return tool.unmarshallExcel(BranchMailChildSample.class);
}
private final BranchMailChildSample branch;
public SampleTest(BranchMailChildSample branch) {
this.branch = branch;
}
#Test
public void test() {
System.out.println(branch.toString());
}
}
That's why I would suggest you to use TestNG instead if possible.
It's work the same as JUnit but you can use a internal or external DataProvider, execute methods at a given order... It is much more convenient to use
//This method will provide data to any test method that declares that its Data Provider
//is named "test1"
#DataProvider(name = "test1")
public Object[][] createData1() {
return new Object[][] {
{ "Cedric", new Integer(36) },
{ "Anne", new Integer(37)},
};
}
//This test method declares that its data should be supplied by the Data Provider
//named "test1"
#Test(dataProvider = "test1")
public void verifyData1(String n1, Integer n2) {
System.out.println(n1 + " " + n2);
}
I got it to work doing this:
List<BranchMailChildSample> items = tool.unmarshallExcel(BranchMailChildSample.class);
Collection<Object[]> data = new ArrayList<Object[]>();
for(BranchMailChildSample item : items)
{
Object[] objItem = new Object[] { item };
data.add(objItem);
}
return data;
Actually, if you use JUnit 4.12 and the theories runner, something like this should just work:
#RunWith(Theories.class)
public class MyTest
{
#DataPoints public static List<MyClass> myFunctionThatReturnsTestData()
{
// TODO
}
#Theory
public void canDoTheThing(MyClass m) throws Exception {
I have a list of pojo's List<Pojo> pojoList; and pojo.getColour(); returns an Enum instance.
And I want to do this :
List<Pojo> newlist = new ArrayList<Pojo>();
for(Pojo pojo:pojoList){
if(pojo.getColour() == Colour.Red){
newList.add(pojo);
}
}
I could see myself using a similar function for lists of other types so rather than repeating a lot of code is their a way to make it generic and/or functional ? So that I could create sublists of different types based upon a different rule ?
First of all, I should note that if you just want a new ArrayList containing the matching elements, the way you did it in your example is just fine. Until Java has lambda expressions, you're not going to get it simpler or better looking than that.
Since you tagged this with guava, here's how you could do this with Guava. You're basically filtering the original list on the composition of a predicate (== Color.Red) and a function (pojo.getColour()). So if you had a static final Function<Pojo, Colour> called COLOUR on Pojo (like this):
public static final Function<Pojo, Colour> COLOUR =
new Function<Pojo, Colour>() {
#Override public Colour apply(Pojo input) {
return input.getColour();
}
};
you could create that combination like this:
Predicate<Pojo> isRedPojo = Predicates.compose(
Predicates.equalTo(Colour.Red), Pojo.COLOUR);
You can then create a filtered view of the original list:
Iterable<Pojo> redPojos = Iterables.filter(pojoList, isRedPojo);
And you could copy that filtered view into an ArrayList if you want:
List<Pojo> copy = Lists.newArrayList(redPojos);
You'd have to make your type implement a common interface for the check:
public interface Candidate {
public boolean isAddable();
}
The loop then would look like this
List<Candidate> newlist = new ArrayList<Candidate>();
for(Candidate pojo:pojoList){
if(pojo.isAddable()){
newList.add(pojo);
}
}
and the Pojo class would have to implement the interface:
public class Pojo implments Candidate {
// ...
#Override
public boolean isAddable() {
return isRed();
}
}
Depending on how often you use it / how many different filters (only red, only green etc.) you are using, it could make sense to create a Filter interface - if it is only to check isRed then it is probably too much code and you are better off with a simple static method.
The good thing about this design is you can use it with any objects that you want to filter (see example with String below).
public static void main(String[] args) {
List<Pojo> originalList = Arrays.asList(new Pojo(true), new Pojo(false), new Pojo(false));
List<Pojo> filteredList = Utils.getFilteredList(originalList, new Filter<Pojo>() {
#Override
public boolean match(Pojo candidate) {
return candidate.isRed();
}
});
System.out.println(originalList.size()); //3
System.out.println(filteredList.size()); //1
//Now with strings
List<String> originalStringList = Arrays.asList("abc", "abd", "def");
List<String> filteredStringList = Utils.getFilteredList(originalStringList, new Filter<String>() {
#Override
public boolean match(String candidate) {
return candidate.contains("a");
}
});
System.out.println(originalStringList.size()); //3
System.out.println(filteredStringList.size()); //2
}
public static class Utils {
public static <T> List<T> getFilteredList(List<T> list, Filter<T> filter) {
List<T> selected = new ArrayList<>();
for (T t : list) {
if (filter.match(t)) {
selected.add(t);
}
}
return selected;
}
}
public static class Pojo {
private boolean isRed;
public Pojo(boolean isRed) {
this.isRed = isRed;
}
public boolean isRed() {
return isRed;
}
}
public interface Filter<T> {
/**
* When passed a candidate object, match returns true if it matches the filter conditions,
* or false if it does not.
* #param candidate the item checked against the filter
* #return true if the item matches the filter criteria
*/
boolean match(T candidate);
}
make an generic filter interface
public interface Filter<T>{
public boolean match(T item);
}
make a method using the filter
public <T> List<T> getFilteredList(List<T> oldList, List<T> filter){
List<T> newlist = new ArrayList<T>();
for(T item:oldList){
if(filter.match(item)){
newlist.add(item);
}
}
return newlist;
}
put it all together
List<Pojo> myList = ..
List<Pojo> redList = getFilteredList(myList,new Filter<Pojo>(){
public boolean match(Pojo item){ return item.isRed()};
});
List<Pojo> blueList = getFilteredList(myList,new Filter<Pojo>(){
public boolean match(Pojo item){ return item.COLOR== Color.BLUE};
});