Order of imports seems to matter for compilation to succeed? - java

I'm writing a unit test for some Java class in Eclipse. I always let Eclipse handle my imports automatically, and it orders them according to whatever default scheme.
Now, I have the following situation. The unit test builds and runs just fine in Eclipse. However, when I build the tests on the command line (using some Ant targets), javac complains that it can't find one of the classes I've imported. Since I'm testing something with caching, the relevant bits of the test file (Test.java let's say) are:
import com.abc.my.stuff.MyCacheLoader.Operation;
import com.google.common.cache.CacheLoader;
public class Test {
... test code, such as ...
List<MyKey> recordedKeys = dummyLoader.getKeysFor(Operation.LoadAll);
}
// Dummy cache loader which just records the keys
class MyCacheLoader extends CacheLoader<MyKey, MyValue> {
enum Operation { Load, LoadAll }
#Override
public MyValue load(MyKey key) throws Exception { .... whatever .... }
#Override
public MyValue loadAll(Iterable<MyKey> key) throws Exception { .... whatever .... }
public List<MyKey> getKeysFor(Operation op) { ... impl ... }
}
Notice that the import for my local class's inner enum appears before the import for the Guava CacheLoader class, because they are ordered alphabetically. When I try to compile this using Ant on the command line, javac can't find 'CacheLoader'. When I switch the order of the imports, the build succeeds (whereas Eclipse doesn't care either way).
I'm mystified as to what's going on here.

Apparently, the compiler in Eclipse doesn't have the following Sun bug:
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6391197
which I found via:
http://unimplemented.blogspot.com/2007/08/my-java-puzzle-does-order-of-import.html
This explains why flipping the order of imports makes no difference to Eclipse, but it does to the Sun compiler. Part of the problem is fixed in Java 8, but my particular manifestation is targeted for a fix in Java 9: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7101822

Related

Groovy #Immutable classes in Java

I often recommend Groovy's #Immutable AST transformation as an easy way to make classes, well, immutable. This always works fine with other Groovy classes, but someone recently asked me if I could mix those classes into Java code. I always thought the answer was yes, but I'm hitting a snag.
Say I have an immutable User class:
import groovy.transform.Immutable
#Immutable
class User {
int id
String name
}
If I test this using a JUnit test written in Groovy, everything works as expected:
import org.junit.Test
class UserGroovyTest {
#Test
void testMapConstructor() {
assert new User(name: 'name', id: 3)
}
#Test
void testTupleConstructor() {
assert new User(3, 'name')
}
#Test
void testDefaultConstructor() {
assert new User()
}
#Test(expected = ReadOnlyPropertyException)
void testImmutableName() {
User u = new User(id: 3, name: 'name')
u.name = 'other'
}
}
I can do the same with a JUnit test written in Java:
import static org.junit.Assert.*;
import org.junit.Test;
public class UserJavaTest {
#Test
public void testDefaultCtor() {
assertNotNull(new User());
}
#Test
public void testTupleCtor() {
assertNotNull(new User(3, "name"));
}
#Test
public void testImmutableName() {
User u = new User(3, "name");
// u.setName("other") // Method not found; doesn't compile
}
}
This works, though there are troubles on the horizon. IntelliJ 15 doesn't like the call to new User(), claiming that constructor is not found. That also means the IDE underlines the class in red, meaning it has a compilation error. The test passes anyway, which is a bit strange, but so be it.
If I try to use the User class in Java code directly, things start getting weird.
public class UserDemo {
public static void main(String[] args) {
User user = new User();
System.out.println(user);
}
}
Again IntelliJ isn't happy, but compiles and runs. The output is, of all things:
User(0)
That's odd, because although the #Immutable transform does generate a toString method, I rather expected the output to show both properties. Still, that could be because the name property is null, so it's not included in the output.
If I try to use the tuple constructor:
public class UserDemo {
public static void main(String[] args) {
User user = new User(3, "name");
System.out.println(user);
}
}
I get
User(0, name)
as the output, at least this time (sometimes it doesn't work at all).
Then I added a Gradle build file. If I put the Groovy classes under src\main\groovy and the Java classes under src\main\java (same for the tests but using the test folder instead), I immediately get a compilation issue:
> gradle test
error: cannot find symbol
User user = new User(...)
^
I usually fix cross-compilation issues like this by trying to use the Groovy compiler for everything. If I put both classes under src\main\java, nothing changes, which isn't a big surprise. But if I put both classes under src\main\groovy, then I get this during the compileGroovy phase:
> gradle clean test
error: constructor in class User cannot be applied to the given types;
User user = new User(3, "name");
required: no arguments
found: int,String
reason: actual and formal arguments differ in length
Huh. This time it's objecting to the tuple constructor, because it thinks it only has a default constructor. I know the transform adds a default, a map-based, and a tuple constructor, but maybe they're not being generated in time for the Java code to see them.
Incidentally, if I separate the Java and Groovy classes again, and add the following to my Gradle build:
sourceSets {
main {
java { srcDirs = []}
groovy { srcDir 'src/main/java' }
}
}
I get the same error. If I don't add the sourceSets block, I get the User class not found error from earlier.
So the bottom line is, what's the correct way to add an #Immutable Groovy class to an existing Java system? Is there some way to get the constructors to be generated in time for Java to see them?
I've been making Groovy presentations to Java developers for years and saying you can do this, only to now run into problems. Please help me save face somehow. :)
I did try your scenario as well, where you have a single project with a src/main/java and a src/main/groovy directory and ended up with compilation errors similar to what you saw.
I was able to use Groovy immutable objects in Java when I put the Groovy immutables in a separate project from the Java code. I have created a simple example and pushed it to GitHub (https://github.com/cjstehno/immut).
Basically it's a Gradle multi-project with all the Groovy code (the immutable object) in the immut-groovy sub-project and all the Java code in the immut-java project. The immut-java project depends on the immut-groovy project and uses the immutable Something object:
public class SomethingFactory {
Something createSomething(int id, String label){
return new Something(id, label);
}
}
I added a unit test in the Java project which creates a new instance of the immutable Groovy class and verifies its contents.
public class SomethingFactoryTest {
#Test
public void createSomething(){
Something something = new SomethingFactory().createSomething(42, "wonderful");
assertEquals(something.getId(), 42);
assertEquals(something.getLabel(), "wonderful");
}
}
This is not really ideal, but it works.

Why is Java type inference choosing the wrong overload?

public class MyProperties {
private Map<String, Object> properties = new HashMap<String, Object>();
public void setProperty(String name, Object value) {
properties.put(name, value);
}
#SuppressWarnings("unchecked")
public <T> T getProperty(String name) {
return (T) properties.get(name);
}
}
#Test
public void test() {
MyProperties props1 = new MyProperties();
props1.setProperty("name", "John Smith");
MyProperties props2 = new MyProperties();
props2.setProperty("name", "John Smith");
assertEquals(props2.getProperty("name"), props1.getProperty("name"));
}
The above unit tests passes on my machine but fails in our jenkins environment with the following error:
java.lang.String cannot be cast to [Ljava.lang.Object;
The unit test passes on my machine when run via Eclipse and ant (eclipse 4.4 luna, ant 1.9.6, jdk_8_u60, Windows 7 64bit) but fails in our jenkins environment (ant 1.9.6 jdk_8_u60, Ubuntu 12.04.4). It's also failing in several other environments, and working in several other -- with no apparent rhyme or reason.
So I have two questions:
1)Why is Java choosing the overload org.junit.Assert.assertEquals(Object[],Object[]) instead of org.junit.Assert.assertEquals(String,String) or org.junit.Assert.assertEquals(Object,Object)?
2) and why is the unit test passing in some environments, and failing in others with the same versions of java, ant, and junit?
Regarding 2:
The different behaviour is most likely not caused by the runtime environment, but by the compiler used to compile the class.
Eclipse has its own Java compiler whereas Jenkins uses javac from the JDK.
Seems that Eclipse 4.4. generates a call for the assertEquals line which allows for a successful test run whereas javac generates a call to org.junit.Assert.assertEquals(Object[],Object[]) and then the test fails.
This does not work in Eclipse 4.5 which will at compile time complain about the call to the deprecated method Assert.assertEquals(Object[],Object[]) and the test also fails when run in Eclipse 4.5.
You can test the hypothesis by decompiling the classes generated by Eclipse 4.4 and javac (using (javap -c) and examine what Assert method they chose.
Regarding 1:
Assert has two assertEquals-methods which accept two objects:
Assert.assertEquals(Object expected, Object actual);
Assert.assertEquals(Object[] expecteds, Object[] actuals);
According to the JLS (15.12) the compiler has to choose the most specific from all applicable methods when when compiling a method invocation. In the example this is Assert.assertEquals(Object[] expecteds, Object[] actuals).
This might be confusing, and because of that JUnit could have decided to deprecate the method with array arguments.

Why/how does JUnit pass tests with compiler errors?

I've just started learning about TDD and am trying to write a simple project that way.
I'm using Eclipse and JUnit, and every time I make a change I run all the tests in the relevant package.
But then I'm very surprised to see in the package explorer that one of my test cases has a big red cross indicating a compiler problem... Annoyed I figured that I got my eclipse shortcuts mixed up and haven't been running all the tests, as they are all passing.
But when I start fiddling about, I realise that it seems Eclipse + JUnit will run and pass tests even if there are compiler errors...
The JUnit test case:
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
public class ATestCase {
private Command command;
private Invoker invoker;
#Before
public void setUp() throws Exception {
command = new Command() {
public void methodA(){};
//haven't implemented methodB()
};
invoker = new Invoker(command);
}
#Test
public void test(){
invoker.invoke();
}
}
interface Command{
void methodA();
void methodB();
}
The Invoker class:
class Invoker{
private Command command;
public Invoker(Command command) {
this.command = command;
//if I invoke methodB() I get UnresolvedCompilationError
}
public void invoke(){
command.methodA();
//only if I call methodB here
//do I get Unresolved compilation problem
// command.methodB();
}
}
The command object I create in setUp in the test case only implements one of the interface's methods. This of course causes a compilation error warning in Eclipse.
However unless I actually call that method in the test case, the test passes.
If I do call the method, then the test fails with 'unresolved compilation error'.
Can anyone explain to me what exactly is going on?
******EDIT******
I'm not sure why this was closed as a duplicate.
Apparently I'm supposed to edit this question to make the difference clear:
Well the question I'm supposed to be duplicating asks in the first line:
What are the possible causes of a "java.lang.Error: Unresolved
compilation problem"?
The title of my question states I'm asking:
Why/how does JUnit pass tests with compiler errors?
As in how can code which shouldn't compile be run by JUnit without causing errors?
I fully understand the causes of the Unresolved Compilation Error, it's the obvious unresolved compilation error in my code. What I don't understand is how the error doesn't always occur (it only occurs when I specifically call an unimplemented method) and how the tests pass?!
It may be that these issues are related, but unless there is a specific answer explaining how they are related I fail to see how they are in any way duplicate questions...
When a class fails to implement an interface method, the Java compiler does not reject the code but instead emits bytecode for the method that will raise the runtime error seen. This explains why JUnit is able to run the tests and why the test passes if you don't call methodB - the runtime error does not get raised.
Per this page, this does not occur by default but requires that you have the Java -> Debug 'Suspend execution on compilation errors' setting enabled.
I'm assuming that is by design: to allow for testing specific methods without having to worry about whether other dependencies which the compiler would pick up on anyway are resolved or not. i.e. we shouldn't be using JUnit to tell us whether our entire project can compile or not.

Mockito fails in Maven but succeeds in Eclipse

We have a test wich fails when executed in Maven, but succeeds in Eclipse.
Basically the problem is, that when executed with Maven Mockito fails to mock a method which comes from a superclass from another maven module with package private modifier.
Questions
Why is this so?
Is this a known bug? If not where to file it? surefire, mockito ...?
how to fix it?
I have found a description of a similar problem, with the recommended fix to use surefire-2.7.1 instead of 2.7.0 but we are already on 2.10 (and also see the problem in 2.16)
Obviously the simplest solution would be to make the BaseClass public, but we can't do it, since it is not under our control.
Another alternative would be to overwrite close in MockedClass, which would be ugly but possible.
The error message is
failsCallingOriginalMethod(ModifierTest) Time elapsed: 0.156 sec <<< ERROR!
java.lang.RuntimeException: must not have called me
Relevant Code
The real code is not in the default package, but all code is in the same package; import statements removed for brevity.
Maven Module 1
public class ModifierTest
{
#Test
public void failsCallingOriginalMethod()
{
MockedClass mock = Mockito.mock(MockedClass.class);
doNothing().when(mock).close();
}
}
Maven Module 2
public class MockedClass extends BaseClass
{
}
class BaseClass
{
public void close()
{
throw new RuntimeException("must not have called me");
}
}
Versions of stuff involved
Maven Version 3.0.5 (can't change that due to other bugs)
Oracle JDK 1.6.0_20 (reproducable with IBM JDK 1.5)
Mockito 1.95
surefire plugin 2.10 (reproducable with 2.16)
Do you have control over MockedClass?
If so, you could consider adding a delegating method in MockedClass:
public void close() {
super.close();
}
This doesn't solve the issue, but it's a quick workaround.

Interface binding in Eclipse

I have the following code in Eclipse(Helios)/STS which runs and prints console output when doing a Run As> Java Application, in spite of obvious compilation issues
public interface ITest{
String func();
}
public static class Test implements ITest{
void printFunc(){
System.out.println("Inside Test Function");
}
}
public static void main(String[] args) {
Test test = new Test();
test.printFunc();
}
Can anyone pinpoint the reasoning behind this Eclipse functioning.
Note: Doing a javac externally obviously fails to compile.
Eclipse's Java compiler is designed to cope with flaky, non-compiling code. It will add whatever stuff is necessary to the code to get it to compile.
See this question What is the difference between javac and the Eclipse compiler?
It might have been that you have coded the class successfully before the errors. Eclipse auto-compiles your file while you are coding. Just then, you happen to have errors.. then you decide to run as Java Application, Eclipse will run the most recent compiled class.
I tried your code, implemented the necessary methods to remove the errors, then removed it again to put back the errors.. sure enough, it printed out "Inside Test Function". I also tried commenting out System.out.println("Inside Test Function"); and it still printed out.
In another try, I created another class, added your code, then run (without implementing the errors to avoid auto-compiling), then it printed out an error..
java.lang.NoSuchMethodError: main
Exception in thread "main"

Categories