Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Eclipse 4 » E4 DI in unittests?
E4 DI in unittests? [message #900647] Tue, 07 August 2012 19:20 Go to next message
Markus Oley is currently offline Markus Oley
Messages: 248
Registered: July 2009
Location: Germany
Senior Member
Hi to anyone,

I want to write unittests regarding classes that are injected in my real app.
I want to inject these objects in my unittests too.
Is there a API for injecting objects and all their fields recursively?
What's the best way to test this enviroment?

Do you have an example to provide?

Thanks in advance

Best regards
Markus
Re: E4 DI in unittests? [message #900651 is a reply to message #900647] Tue, 07 August 2012 20:23 Go to previous messageGo to next message
Joseph Carroll is currently offline Joseph Carroll
Messages: 174
Registered: May 2012
Location: Milwaukee, WI
Senior Member

Not sure I understand your objective... Are you unit testing the DI framework or YourObject?

The only reason you would need the DI framework in your unit test is if you are unit testing the DI framework itself. If you are just testing YourObject (some object from your application), you should use some kind of mock framework [1] or similar. The DI framework will manage the re-injection when necessary [2]. Note that if two methods need injection with the same object, the order of injection is not API (AFAIK).

The one exception is if you are creating your own annotations for use with the DI framework. In testing @YourAnnotation you would need the DI framework (obviously) and you would mock the annotated object. If this is the case, I can help you setup the unit tests. (Note in this case you are unit testing the DI framework)


JD

[1] http://www.easymock.org/
[2] http://wiki.eclipse.org/Eclipse4/RCP/Dependency_Injection#Injection_Order
Re: E4 DI in unittests? [message #900680 is a reply to message #900651] Wed, 08 August 2012 03:21 Go to previous messageGo to next message
Markus Oley is currently offline Markus Oley
Messages: 248
Registered: July 2009
Location: Germany
Senior Member
Hi again,

my problem is the following:

I use DI to inject my own objects.
For example:

@Creatable
class A {
  @Inject private B someField; 

  @Inject private C someOtherField;
}

@Creatable
class B {
  @Inject C thirdField;
}


So from scratch, A.B.C is null, and I can't set it from outside.
OK, I could use mocking, but I cannot mock a private field with Mockito, I guess, so I have to make it at least package private.
Better would be to get these things injected, I think. This would simplify the test itself.

Could you give me a hint, what's best practice in this context?

Thanks a lot.

Best regards
Markus
Re: E4 DI in unittests? [message #900684 is a reply to message #900680] Wed, 08 August 2012 03:28 Go to previous messageGo to next message
Thomas Schindl is currently offline Thomas Schindl
Messages: 5014
Registered: July 2009
Senior Member
Am 08.08.12 09:21, schrieb Markus Oley:
> Hi again,
> my problem is the following:
> I use DI to inject my own objects. For example:
> @Creatable
> class A {
> @Inject private B someField;
> @Inject private C someOtherField;
> }
>
> @Creatable
> class B {
> @Inject C thirdField;
> }
>
> So from scratch, A.B.C is null, and I can't set it from outside. OK, I
> could use mocking, but I cannot mock a private field with Mockito, I
> guess, so I have to make it at least package private. Better would be to

Yeah - I find myself using package private for almost everything i do
with DI.

Tom
Re: E4 DI in unittests? [message #900705 is a reply to message #900684] Wed, 08 August 2012 04:38 Go to previous messageGo to next message
Sopot Cela is currently offline Sopot Cela
Messages: 589
Registered: December 2010
Senior Member

Regarding the recursiveness of the injection it happens automatically. When you inject calss A it will not get injected after all of it's fields are injected (excluding @Optional ones). If one of these fields is a class B which needs injection too, the B's injection (and of its fields ) will happen before A is ready to be injected.

If I got you correctly, you want to do DI on the JUnit class to harvest the @Creatable annotation and get the objects created by the injector so you don't mock them?

Re: E4 DI in unittests? [message #900788 is a reply to message #900680] Wed, 08 August 2012 09:35 Go to previous messageGo to next message
Joseph Carroll is currently offline Joseph Carroll
Messages: 174
Registered: May 2012
Location: Milwaukee, WI
Senior Member

To build off of your example here:

Markus Oley wrote on Wed, 08 August 2012 02:21

@Creatable
class A {
  @Inject private B someField; 

  @Inject private C someOtherField;
}

@Creatable
class B {
  @Inject C thirdField;
}



Just be very careful that there isn't a circular dependency such as:
@Creatable
class C {
  @Inject /*qualifier*/ A fourthField;
}


From a java standpoint this certainly does not create any issues. The problem is if there aren't any usable instance of A / B / C in the context, injection will fail. (I don't know if there will be any listed error/warning)

If you need/want the fields to be private for some reason, you can use the same approach the DI engine uses with reflection for the mock objects. (See the code below or the attached file. But in general I agree with Tom, (whether using DI or not) if I need to test a field in a unit test, I make it package-private.

Also, the code below is only a sample and isn't quite 'ready to go'. A couple of things still need to be implemented, but it will at least get you started.

JD


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class MockInjector {
	
	public Object getInstance(Class<?> testClass) {
		Object instance = null;
		try {
			instance = testClass.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		
		if (instance == null) {
			Constructor<?>[] constructs = testClass.getDeclaredConstructors();
			// choose a constructor
			
			Class<?>[] params = constructs[0].getParameterTypes();
			Object[] paramInstances = new Object[params.length];
			// need to create logic to provide parameters for desired constructor
			
			try {
				instance = constructs[0].newInstance(paramInstances);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		
		return instance;		
	}
	
	public void mockInject(Object testClassInstance, String fieldName, Object fieldValue) {
		Class<?> clazz = testClassInstance.getClass();
		Field testField = null;
		try {
			testField = clazz.getDeclaredField(fieldName);
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
		
		/*
		 * It doesn't matter if the field is private or public, by
		 * invoking setAccessible(true) it removes the accessibility 
		 * checks performed by the JVM by setting a flag.  It does not
		 * remove the security checks.  Effectively this eliminates 
		 * encapsulation.
		 */
		testField.setAccessible(true);
		try {
			testField.set(testClassInstance, fieldValue);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}
	
}
Re: E4 DI in unittests? [message #900821 is a reply to message #900647] Wed, 08 August 2012 11:39 Go to previous messageGo to next message
Markus Oley is currently offline Markus Oley
Messages: 248
Registered: July 2009
Location: Germany
Senior Member
Thanks for all you answers,

I guess I make the fields package private and create a utility-class to setup things

Best regards
Markus
Re: E4 DI in unittests? [message #901000 is a reply to message #900821] Thu, 09 August 2012 07:33 Go to previous messageGo to next message
Brian de Alwis is currently offline Brian de Alwis
Messages: 242
Registered: July 2009
Senior Member
If you want to test your objects under injection, you can create and populate your own IEclipseContext and then use it for injection:

IEclipseContext context = EclipseContextFactory.create("test context");
context.set(C.class, new C(...));
A a = ContextInjectionFactory.make(A.class, context);
// test a


Or see EclipseContextFactory#getServiceContext() to get a context that has access to the OSGi services.
Re: E4 DI in unittests? [message #902493 is a reply to message #901000] Fri, 17 August 2012 18:39 Go to previous message
Markus Oley is currently offline Markus Oley
Messages: 248
Registered: July 2009
Location: Germany
Senior Member
Ah, cool,
that's exactly what I was looking for.
thank you
Previous Topic:Code editor only shows expand icon for file list instead of files itself
Next Topic:ActionBarContributor
Goto Forum:
  


Current Time: Sat Apr 19 11:46:52 EDT 2014

Powered by FUDForum. Page generated in 0.08576 seconds