Generic JUnit tests - java

I've got an abstract generic collections class GCollection, and a class that extends that called GStack.
To test the implementation I have an abstract JUnit test class, which I extend for each of the GCollection implementations I do:
public abstract class GCollectionTest<T extends GCollection<E>, E> {
private GCollection<? extends Object> collection;
protected abstract GCollection<T> createInstance();
#Before
public void setup() throws Exception {
collection = createInstance();
}
// Tests down here.
This is extended like so:
public class GStackCollectionInterfaceTest<S extends GCollection<E>> {
protected GDSStack<? extends Object> createInstance() {
return new GDSStack<String>();
}
}
I test first with a GStack holding String objects, then re-run the tests with Date objects to ensure it works with different object types.
#Test
public void testIsEmpty() {
assertTrue(collection.isEmpty()); // Fresh Stack should hold no objects
collection.add(new String("Foo")); // Error here.
assertFalse(collection.isEmpty());
}
The error given is:
The method add(capture#24-of ? extends Object) in the type GCollection is not applicable for the arguments (String)
My understanding of the error is that I can't put a String object into a GCollection<T extends GCollection<E>> object, but I don't think that's what I'm trying to do.
What am I doing wrong?
How can I solve this error while maintaining tests that are as generic as possible?

The type of collection is GCollection<? extends Object>. It is not possible to add anything to this collection, see: can't add value to the java collection with wildcard generic type.
There's no need for wildcards or bounds in the subclass so you can simplify the generics. Something like:
abstract class GCollectionTest<T> {
protected Collection<T> collection;
protected abstract Collection<T> createCollection();
protected abstract T createObject();
#Before
public void setup() throws Exception {
collection = createCollection();
}
#Test
public void testIsEmpty() {
assertTrue(collection.isEmpty());
collection.add(createObject());
assertFalse(collection.isEmpty());
}
}
class GStackCollectionInterfaceTest extends GCollectionTest<String> {
protected GDSStack<String> createCollection() {
return new GDSStack<String>();
}
protected String createObject() {
return new String("123");
}
}
Using different types with the collection is allowed because of the generic type, and checked by the compiler, so it doesn't really need testing. I would just test the different container types, but you could create another subclass that uses Date instead of String.

Related

How to satisfy parameter type Class<? extends someInterface> in java

Consider the following code
#Test
public void testFunction() {
// This cause error
callDoSomething(new myInterfaceImpl());
}
public interface myInterface {
int doSomething();
}
public class myInterfaceImpl implements myInterface {
public int doSomething() {
return 1;
}
}
public void callDoSomething(Class<? extends myInterface> myVar) {
System.out.println(myVar.doSomething());
}
On this line callDoSomething(new myInterfaceImpl()); I get the following error.
Error:(32, 25) java: incompatible types: com.myProject.myTest.myInterfaceImpl
cannot be converted to java.lang.Class<? extends com.myProject.myTest.myInterface>
How do I satisfy the parameter type? If only an interface is provided to me.
I want to bound the class that has an interface, but it seems like this is not avaiable to me
Class<? implements myInterace>
Edit:
The reason I want to do this is because I want to provide a custom kafka partitioner.
public Builder<K, V> withCustomPartitionner(Class<? extends Partitioner> customPartitioner) {
this.customPartitioner = customPartitioner;
return this;
}
It looks like you want to be able to call methods on the parameter that's given. In that case, you'll want the actual instance of your interface, not the Class associated with it.
public void callDoSomething(myInterface myVar) {
System.out.println(myVar.doSomething());
}
Class<> is used when you want to use reflection to do something with the specific class type that you're interested in:
public void outputClassInfo(Class<? extends myInterface> myClass) {
System.out.println(myClass.getName());
}
If that's what you're going for, you'll want to provide the class at compile time like this:
outputClassInfo(myInterfaceImpl.class);
Or, if you won't know which class you're dealing with until runtime, you can use reflection:
myInterface thing = getThing();
outputClassInfo(thing.getClass());
So, in the example you're providing in your edit, I'm guessing you want:
public Builder<K, V> withCustomPartitioner(Class<? extends Partitioner> customPartitioner) {
this.customPartitioner = customPartitioner;
return this;
}
// Usage
builder
.withCustomPartitioner(FooPartitioner.class)
...
This type Class<? extends myInterface> myVar corresponds to a Class instance not to an instance of myInterface.
You generally don't pass a class as parameter (but for reflection purposes or to bypass generics erasures). So what you need as parameter is probably :
public void callDoSomething(myInterface myVar) {
System.out.println(myVar.doSomething());
}
That you could invoke :
#Test
public void testFunction() {
// This cause error
callDoSomething(new myInterfaceImpl());
}
The parameter to callDoSomething shouldn't be a class. It must be an instance of that class or it's subclass.
public <T extends myInterface> void callDoSomething(T myVar) {
System.out.println(myVar.doSomething());
}
On a side note, don't name Java classes/interfaces starting with lower case.
As rightly mentioned by Andy Turner#, there is no need to use a type parameter here and you can just refer to the type as myInterface
public void callDoSomething(myInterface myVar) {
System.out.println(myVar.doSomething());
}
You need to pass the Class not an instance.
callDoSomething(MyInterfaceImpl.class);

Why is this class not considered a supertype as a parameter?

Given the following example, why am I able to override the return type List<? extends IConfigUser> as List<ConfigUser> in getUserList() but cannot do the same for the parameter of setUserList()?
Isn't ConfigUser considered a supertype of IConfigUser in this case?
public class Test {
public interface IConfigUser {
}
public interface IConfig {
public List<? extends IConfigUser> getUserList();
public void setUserList(List<? extends IConfigUser> list);
}
public class ConfigUser implements IConfigUser {
}
// The type Test.Config must implement the inherited abstract method
// Test.IConfig.setUserList(List<? extends Test.IConfigUser>)
public class Config implements IConfig {
#Override
public List<ConfigUser> getUserList() {
return null;
}
// The method setUserList(List<ConfigUser> list) of type Test.Config
// must override or implement a supertype method
#Override
public void setUserList(List<ConfigUser> list)
{
}
}
}
You can achieve your goal by adding a generic type parameter to IConfig:
public class Test {
public interface IConfigUser {
}
public interface IConfig<T extends IConfigUser> {
public List<T> getUserList();
public void setUserList(List<T> list);
}
public class ConfigUser implements IConfigUser {
}
public class Config implements IConfig<ConfigUser> {
#Override
public List<ConfigUser> getUserList() {
return null;
}
#Override
public void setUserList(List<ConfigUser> list)
{
}
}
}
You can return a more specific type in an override, but you can't require that you accept a more specific type. Get rid of the generics, and you can override a method returning Object with a method returning String, but you can't override a method accepting an Object parameter with a method accepting a String parameter.
All of this is so that callers are compatible. Consider:
IConfig config = new Config();
List<SomeOtherConfigUser> list = new ArrayList<SomeOtherConfigUser>();
list.add(new SomeOtherConfigUser());
config.setUserList(list);
Oops - your Config.setUserList is expecting every element to be a ConfigUser, not a SomeOtherConfigUser.
You can return ("specialize") the return type of getUserList() due to covariance, i.e. if you call that method on a IConfig reference all you know is that you'll get a List<? extends IConfigUser> and a List<ConfigUser> is a List<? extends IConfigUser> so the requirements are satisfied.
If you call that on a Config reference the information is more concrete but the basic requirements are still met.
With setUserList(...) the situation is different: it allows you to pass any "subclass" of List<? extends IConfigUser> which can be a List<ConfigUser> but it also can be something else, e.g. a List<SomeConfigUser>.
Btw, since you don't know the concrete generic parameter of list in setUserList(List<ConfigUser> list) the compiler will also only allow you to read from that list, never add to it - for the same reason as above: you don't know what you get and whether adding a ConfigUser is allowed because the list could only allow SomeConfigUser instances to be added.

ClassCastException when mocking generic type using Mockito

I have a abstract test class with a generic parameter.
public abstract class AbstractCarTest<G> {
...
#Mock
protected G carMock;
...
}
I've implemented a concrete test class of it.
public class TruckTest extends AbstractCarTest<Truck> {
...
when(truckFactory.getTruck(anyString()).return(carMock);
...
}
The method signature looks like this
public Truck getTruck(String name);
When running TruckTest I get a ClassCastException saying
java.lang.ClassCastException: org.mockito.internal.creation.jmock.ClassImposterizer$ClassWithSuperclassToWorkAroundCglibBug$$EnhancerByMockitoWithCGLIB$$... cannot be cast to com.example.Truck
Why is that? Any way I can work around that?
Mockito is generating your mock class at runtime. Mocks are created by subclassing a given class and by overriding all of its methods. (Thus the limitation of not mocking final classes or methods.) At runtime, all generic types are erased and replaced by their upper type bound, in your AbstractCarTest this is Object as you do not specify an explicit upper bound. Therefore, Mockito sees your raw class as:
public abstract class AbstractCarTest {
#Mock
protected Object carMock;
}
at runtime and will create a mock that extends Object instead of your desired Truck class. (You cannot see this because cglib has a bug where it cannot extend Object directly. Instead, Mockito is extending an internal class called ClassWithSuperclassToWorkAroundCglibBug.) Usually, the compiler would issue a type error at compile time where the generic type is still available but at runtime, you experience heap pollution instead.
A work around would be as follows:
public abstract class AbstractCarTest<T> {
protected abstract T getCarMock();
// define some base test cases here that use the generic type.
}
public class TruckTest extends AbstractCarTest<Truck> {
#Mock
private Truck carMock;
#Override
protected Truck getCarMock() { return carMock; }
// ...
when(truckFactory.getTruck(anyString()).return(getCarMock());
}
By defining your mocked type without using generics, you are able to access the mock from the abstract base class by a getter where the mocked type is correctly defined as Truck.
It seems like Mockito will not mock a class when it does not know it's concrete type.
You can get around the problem by doing the following:
public abstract class AbstractCarTest<G> {
...
protected G carMock;
...
#Before
public void setUp() throws Exception {
carMock= Mockito.mock(getClazz());
}
protected abstract Class<G> getClazz();
}
public class TruckTest extends AbstractCarTest<Truck> {
...
when(truckFactory.getTruck(anyString()).return(carMock);
...
#Override
protected Class<Truck> getClazz() {
return Truck.class;
}
}
Just wanted to improve a bit on the answer from #geoand.
public abstract class AbstractCarTest<G> {
...
protected G carMock;
...
#Before
public void setUp() throws Exception {
carMock= Mockito.mock(getClazz());
}
private Class<G> getClazz() {
return (Class<G>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
}
public class TruckTest extends AbstractCarTest<Truck> {
...
when(truckFactory.getTruck(anyString()).return(carMock);
...
}
This ends up removing the need for the abstract method. The index supplied to getActualTypeArguments() will depend on where the Type Argument appears in your declaration. In most cases this will be 0 however if you have something like this:
public abstract class AbstractCarTest<A, B, C, G>
And you wanted to use the Class of G you would need to supply and index of 3.

Java Generic Setter with Interface and Abstract Class

i have a problem with an Interface and generic setters that i tried to solve for some hours now.
i have an interface where i want to define some getter and setter functions. the getter functions should be implemented by some abstract class since they usually shouldn't change.
the setter functions on the other hand should be overrideable multiple times by a defined class. In the case i try to describe it would be that the childClass should be able to implement 2 setFunctions with the same name and different input values
Interface TestClass {
public abstract void setSomething(List<?> value);
public abstract List<String> getSomething();
}
abstract class AbstractTestClass implements TestClass {
List<String> someData;
public List<String> getSomething() {
return someData;
}
}
class TestClassImplementation extends AbstractTestClass() {
#Override
public void setSomething(List<String> data) {
someData = data;
}
#Override
public void setSomething(List<SomeOtherType> data) {
someData = convertToStringList(data);
}
private List<String> convertToStringList(List<SomeOtherType> data) {
... do some conversion ...
return returnList;
}
}
hope this gives the idea of what i want to do. I would even prefer to implement the setSomething with the stringlist in the abstract class. But both setters must be reachable.
Thanks
You simply can't do that. Generics are not retained at runtime (google type erasure for more infos on this or just read the wikipedia page).
This means that your setSomething-methods all have the same signature, as their only parameter is of type List.
Well, you should try with generic solution:
Interface TestClass<T> { //Generic type T that you will provide when extending with actual class
public abstract void setSomething(List<T> value);
public abstract List<T> getSomething();
}
class TestClassImplementation extends AbstractTestClass<RealType> {
#Override
public void setSomething(List<RealType> data) {
someData = data;
}
}
The thing that confuses you is that the wildcard sign ? does not mean it changes any type, it just denotes an unknown type.

Creating a list of instances of derivatives from a generic abstract class

Consider this base class:
public abstract class TestGenericList<T> {
public TestGenericList() {
}
protected abstract void doSomething();
}
Now, two derived classes:
public class DerivedA extends TestGenericList<DerivedA> {
#Override
protected void doSomething() {
//
}
}
public class DerivedB extends TestGenericList<DerivedB> {
#Override
protected void doSomething() {
//
}
}
Application class:
public class TestListMain {
public static void main(String[] args) {
DerivedA dA = new DerivedA();
DerivedB dB = new DerivedB();
List<TestGenericList> genericList = new ArrayList<TestGenericList>();
genericList.add(dA);
genericList.add(dB);
}
}
The line with the list creation gives this warning:
TestGenericList is a raw type. References to generic type TestGenericList<T> should be parameterized
How can I generate a list of derivatives of a generic abstract base class?
The type information will get lost if you add both types to the list. Thus you will only know that the list contains TestGenericList objects of an unkown type <?>.
List<TestGenericList<?>> genericList = new ArrayList<TestGenericList<?>>();
This might be sufficient in your case, but you should always think about if this is what you want. Why did you introduce a type information when it gets lost later? There are cases when it makes sense. E.g. take a look at Collections.disjoint(Collection<?> c1, Collection<?> c2).
TestGenericList<T> is a generic type declaration, and you are using this as raw type. Here you should use TestGenericList<DerivedA> or TestGenericList<?>in order to remove warning.

Categories

Resources