New OpenAPI doesn't support mock testing

I am trying to update my plugins to Pallada (unittest, tdd, clearcase). Unfortunately this is very difficult because all my internal tests mock your openapi and some concrete classes that I have to extend to mock don't like to be instantiated outside of IDEA. Most specifically the new peer implementation of DialogWrapper and VirtualFile constructors throw a NPE.

As I said many times I believe that this is VERY IMPORTANT aspect of a good api: it should be very easy to mock. Usually that translates into providing interfaces instead of classes but when you mean to have the implementer derive your class PLEASE PLEASE make that class resilient to the type of environment it is constructed under or provide both an interface AND a base class that implements that interface. Always use the interface.

I am dead in the water. I hate to say it but these days I have less time than I used to and I don't have the time to change my testing strategy. I hope that you can make

Thanks in advance

Jacques

15 comments
Comment actions Permalink

I hope you can make the necessary changes for the next build.

(Damn Enter key ;)

0
Comment actions Permalink

Jacques Morel wrote:

I am trying to update my plugins to Pallada (unittest, tdd, clearcase). Unfortunately this is very difficult because all my internal tests mock your openapi and some concrete classes that I have to extend to mock don't like to be instantiated outside of IDEA. Most specifically the new peer implementation of DialogWrapper and VirtualFile constructors throw a NPE.

As I said many times I believe that this is VERY IMPORTANT aspect of a good api: it should be very easy to mock. Usually that translates into providing interfaces instead of classes but when you mean to have the implementer derive your class PLEASE PLEASE make that class resilient to the type of environment it is constructed under or provide both an interface AND a base class that implements that interface. Always use the interface.

I am dead in the water. I hate to say it but these days I have less time than I used to and I don't have the time to change my testing strategy. I hope that you can make

Thanks in advance

Jacques

I'm not quite sure I understand what the problem is. Would you please attach an NPE stacktrace at least?

--
Maxim Shafirov
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"

0
Comment actions Permalink

Thanks Maxim for responding that quickly! You would be my hero if you could solve these 2 NPEs. Here they are:

The DialogWrapper NPE is a straightforward one.

(DialogWrapper.java:63)
	at org.intellij.plugins.ui.SelectDialog.(SelectDialog.java:70)
	at org.intellij.plugins.ui.test.SelectDialogTest.testCellRenderer(SelectDialogTest.java:45)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at com.intellij.rt.execution.junit2.JUnitStarter.main(Unknown Source)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at com.intellij.rt.execution.application.AppMain.main(Unknown Source)
]]>

I mischaracterized the second NPE. It isn't because of VirtualFile but because of FileStatus. The NPE is wrapped into a ExceptonInInitializerError and occurs in the construction of the FileStatus constants. My code does this:

(MockVirtualFile.java:46)
	at org.intellij.openapi.testing.ObjectMother.createPsiDirectory(ObjectMother.java:97)
	at org.intellij.openapi.testing.ObjectMother.createSourceRootPsiDirectory(ObjectMother.java:136)
	at org.intellij.openapi.testing.ObjectMother.findOrCreateSourceRootPsiDirectory(ObjectMother.java:122)
	at org.intellij.openapi.testing.ObjectMother.createPsiClass(ObjectMother.java:61)
	at org.intellij.openapi.testing.ObjectMother.createPsiClass(ObjectMother.java:54)
	at org.intellij.openapi.testing.ObjectMother.createPsiClass(ObjectMother.java:42)
	at org.intellij.openapi.testing.ObjectMother.reset(ObjectMother.java:37)
	at org.intellij.openapi.testing.ObjectMother.(ObjectMother.java:31)
	at org.intellij.openapi.testing.ObjectMother.(ObjectMother.java:21)
	at org.intellij.openapi.test.PluginContextTest.setUp(PluginContextTest.java:46)
	at com.intellij.rt.execution.junit2.JUnitStarter.main(Unknown Source)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at com.intellij.rt.execution.application.AppMain.main(Unknown Source)
Caused by: java.lang.NullPointerException
	at com.intellij.peer.b.b(b.java:2)
	at com.intellij.openapi.vcs.FileStatus.(FileStatus.java:11)
	... 29 more
]]>

0
Comment actions Permalink

Looks like you've mocked a little too much. At least it seems ApplicationManager.getApplication() returns null in your
configuration.

FileStatus.NOT_CHANGED is declared as:
public static final FileStatus NOT_CHANGED = PeerFactory.getInstance().getFileStatusFactory()
.createFileStatus("NOT_CHANGED", "Up to date", COLOR_NOT_CHANGED);


and PeerFactory is an application-level component so getInstance is implemented as
public static PeerFactory getInstance() {
return ApplicationManager.getApplication().getComponent(PeerFactory.class);
}

So, I feel the only case for you is to mock deeper having mock Application class that returns mock PeerFactory being
queried for getComoponent().

As the matter of fact I wonder you've faced with this problem now only since it's very common practice here in IDEA.
There could be the other way to mock actually by patching IdeaTestComponent.xml (or including its copy earlier in
classpath).

--
Maxim Shafirov
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"

0
Comment actions Permalink

Isn't PeerFactory obfuscated? I would hate to have to locate the class every build because its new one character package and class name has changed ;-( I am doing this with some member of PsiClass. It is a pain and it is just a member.

Could you explain a little more your other alternative (IdeaTestComponent.xml)?

What about DialogWrapper?

Thanks

jacques

0
Comment actions Permalink

Jacques Morel wrote:

Isn't PeerFactory obfuscated? I would hate to have to locate the class every build because its new one character package and class name has changed ;-( I am doing this with some member of PsiClass. It is a pain and it is just a member.

Could you explain a little more your other alternative (IdeaTestComponent.xml)?

What about DialogWrapper?

Thanks

jacques


PeerFactory would became open in next build. DialogWrapper problem is just the same as FileStatus. One creates some peer
implementation via PeerFactory.
IdeaTestComponents.xml contains interface->implementation class map for application/project/module components in test
application. IdeaComponents.xml is used instead in plain IDEA.

The syntax is as follows:
com.intellij.ide.GeneralSettings com.intellij.ide.GeneralSettings com.intellij.peer.PeerFactory com.intellij.peer.impl.PeerFactoryImpl .... ... ]]>

Such a scheme allows you to substitute real implementation classes with their mocks.

--
Maxim Shafirov
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"

0
Comment actions Permalink

Awesome!

Did you do this peer architecture just to support mock testing or is there other reasons to? In any case it is very nice and should work beautifully.

The only request I have is the following (I am pretty sure you have already thought of that):
With the opening up of the PeerFactory will I be able to "register" my own component implementation programmatically?
This is what I do now having wrapped all the "getInstance()" methods in a similar concept as the PeerFactory.

Jacques

0
Comment actions Permalink

>PeerFactory would became open in next build

So now you know what inevitably follows this kind of response, right? ;)
So when?

Jacques

0
Comment actions Permalink

Jacques Morel wrote:

Awesome!

Did you do this peer architecture just to support mock testing or is there other reasons to? In any case it is very nice and should work beautifully.

The only request I have is the following (I am pretty sure you have already thought of that):
With the opening up of the PeerFactory will I be able to "register" my own component implementation programmatically?
This is what I do now having wrapped all the "getInstance()" methods in a similar concept as the PeerFactory.

Jacques


As the matter of fact the PeerFactory is one of the general ApplicationComponent scheme sample. This kind of mocking
have always been possible. The PeerFactory appeared to do demand of much more stuff need to separate implementation
while opening OpenApi sources.

--
Maxim Shafirov
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"

0
Comment actions Permalink

Jacques Morel wrote:

>>PeerFactory would became open in next build


So now you know what inevitably follows this kind of response, right? ;)
So when?

Jacques


Hope for the beginning of the next week. Yet another build script problem...

--
Maxim Shafirov
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"

0
Comment actions Permalink

Maxim,

Thanks for the new build.

Where can I find this IdeaComponents.xml?
Where should I place my IdeaTestComponents.xml?
The only thing I could find is IdeaComponents.xxx in the resources.jar

Thanks

Jacques

0
Comment actions Permalink

Jacques Morel wrote:

Maxim,

Thanks for the new build.

Where can I find this IdeaComponents.xml?
Where should I place my IdeaTestComponents.xml?
The only thing I could find is IdeaComponents.xxx in the resources.jar

Thanks

Jacques

Fixed

--
Maxim Shafirov
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"

0
Comment actions Permalink

Does anyone know where we should place IdeaTestComponents.xml? Should we just update the one in IDEA_HOME/lib/resources.jar?

TIA

Franck

0
Comment actions Permalink

I do it another way. Since ApplicationManager.application is protected you can set your own MockApplication.
All application components are retrieved from ApplicationManager.application.getComponent(Class). Implementing this with your own will provide you with the hooks to replace any IDEA components and in the case of this thread, all peer factories.
BTW you can already do that with project components.



Attachment(s):
mockTesting.zip
0
Comment actions Permalink

Hi Jacques,

Many thanks for sharing the mock implementations, using them did the trick!

Cheers

Franck

0

Please sign in to leave a comment.