Building Java file at runtime & running JUnit test on that class file - java

I am trying to build a Dynamic web project where user can practice Java code.
I got success on writing the code written by user in a .java file, compile the code & get error messages using Java Compile API.
Now, I need to run JUnit 1.4 Compatible test on that code.
I researched for it, and found something like parameterized junit testing. But my view on how should it be done isn't still clear.
UPDATE
This (http://codingbat.com/prob/p171896) is the exact thing what I'm trying to implement.

I solved this problem with the following steps:
Compile the JUnit TestClass and the ClassToTest
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int compilerResult = compiler.run(null, null, null,"/path/to/TestClass.java", "/path/to/ClassToTest.java");
System.out.println("Compiler result code: " + compilerResult);
Load the compiled classes into the classloader
File dir = new File("/path/to/class/files/");
URL url = dir.toURI().toURL();
URL[] urls = {url};
ClassLoader classLoader = new URLClassLoader(urls);
Run the tests (make sure that you add junit as a dependency of your project)
Class<?> junitTest = Class.forName("TestClass", true, classLoader);
Result result = junit.run(junitTest);
Edit: If you're not needing the JUnit tests to be dynamic you can skip the compilation of those tests and add them here: junit.run(JunitTest.class)

Related

Using JunitCore for tests in different projects

Background:
I am currently working on a project in eclipse that programatically executes JUnit tests that are pushed to a server.
So far everything works but I would like to know the results of the tests (specifically any failures) so I can push them out to an email. Right now the tests just output to the console but that doesn't seem to give me much output to actually use.
Right now I use the Runtime class to call the tests but that doesn't seem to have the functionality I need for getting results.
I have looked into the JUnitCore class but can't call any tests outside of the current java project.
So my main question would be how can I use JUnitCore to run junit tests in a specific JAR file? Or is there an easier way to approach this problem using a different class?
This is the only thing I've been able to get to work:
RunTests()
{
junitCore = new JUnitCore();
junitCore.run(AllTests.class);
}
But I would like to do something along the lines of this:
RunTests()
{
junitCore = new JUnitCore();
junitCore.run("C:\\$batch\\test\\hil research\\201507071307\\CommsTestRunner\\plugins\\TestSuite\\US35644.class");
}
I would appreciate any suggestions to this problem I am having. I'm an EE and was just introduced to java last month so this has been quite the challenge for me.
JUnitCore expects to read loaded classes, not class files in a JAR. Your real question is likely how to load the JAR (or directory of .class files) so it can be run.
Poke around with URLClassLoader; once you've amended the classpath appropriately, you can get a Class out of findClass and pass it into the JUnitCore methods you've found.
Since the tests might have classes that are also used by your server (but not necessarily at the same version) I would suggest not having your server directly run the tests. Instead, you can have your server start a new JVM that runs the tests. This is how IDEs like Eclipse run tests. You simply need to write a main class that has JUnit run the tests, and serializes the results on disk.
Your main class would look something like this:
public class MyRunner {
public static void main(String... args) throws IOException {
String path = System.getProperty("resultPath");
if (path == null) {
throw new NullPointerException("must specify resultPath property");
}
// Possibly install a security manager to prevent calls to System.exit()
Result result = new JUnitCore().runMain(new RealSystem(), args);
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(path)) {
out.writeObject(result);
}
System.exit(result.wasSuccessful() ? 0 : 1);
}
}
Then your server simply needs to construct a java command line with the jars that include the tests, the JUnit jar file, and a jar that contains MyRunner.

determine the directory where test cases should be run on it

My program gets as input a jar file and a directory contains Java source code. The jar file contains test cases that should be run on the input java source codes. If I define java input directory as source folder, then I can load test cases and run them as below:
URLClassLoader clsLoader = URLClassLoader.newInstance(new URL[] {new URL("file:/D:/workspace/tests.jar")});
//the name of my test case class that I want to load
Class cls = clsLoader.loadClass("temp.testMulDiv");
Result result = new JUnitCore().run(cls);
for (Failure failure : result.getFailures())
System.out.println(failure.toString());
However, while I put the source code as an input directory, then test cases can not find the code. I know that I should in someway tell test cases to search for source code in the input directory, but I do not know how to do that. Any suggestion?
I also check this piece of code, but still I have the above problem. While I can load the method that I want to run, but in this case "Class.forName" can not find the class ("temp.testMulDiv") and I get "ClassNotFoundException" error.
URLClassLoader clsLoader = URLClassLoader.newInstance(new URL[] {new URL("file:/D:/workspace/tests.jar")});
//the name my test case class that I want to load
Class cls = clsLoader.loadClass("temp.testMulDiv");
Method[] methods = cls.getDeclaredMethods();
// I want to run only the first method
Request request = Request.method(Class.forName(cls.getName()), methods[0].getName());
Result result = new JUnitCore().run(request);
for (Failure failure : result.getFailures())
System.out.println(failure.toString());
}
Any suggestion, how can I run test cases on an input directory (and not a source folder)? How programmatically can I do that?
A good practice is to place test classes in the same package as the source Java classes so that they have visibility of each another.
For example, in Maven, you would have src/main/java, and src/test/java to contain the source Java classes and test classes respectively. These are standard Maven layouts.
Say i have a source Java class MyService.java in net/java/service:
src/main/java/net/java/service/MyService.java
Then i should place the corresponding test class MyServiceTest.java in net/java/service too:
src/test/java/net/java/service/MyService.java
Assume you have the following public method in MyService.java:
public void multiDiv(){
...
}
Then you should have the following test method in MyServiceTest.java:
#Test
public void testMultiDiv(){
MyService service = new MyService();
service.multiDiv();
// assert ....
}

Drools accessing generated java files

I am executing Drools rule through Mockito test. The rule fails at run time reporting the error with a line number of a java file having some long arbitrary name. It seems that Drools generates java files on the fly and injects into JVM. But when I search those files on my disc I don't find any. Is there a way I could store them on my disc?
Got the solution:
You can dump the Drools generated java files in two ways.
1) Through command line:
-Ddrools.dump.dir="target/dumpDir"
e.g. I use maven command to execute the rule so it would be
mvn -Ddrools.dump.dir="target/dumpDir" -Dtest=DroolsRuleTest test
2) Through the API
public class FileKnowledgeBaseFactory implements KnowledgeBaseFactory {
private Log log = LogFactory.getLog(FileKnowledgeBaseFactory.class);
public KnowledgeBase load(String drlFullFilename) {
KnowledgeBuilderConfiguration config = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration();
config.setOption(DumpDirOption.get(new File("target/dumpDir")));
KnowledgeBuilder knowledgeBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(config);
....
....
}
}

Building a ServiceLoader file with gradle: howto?

I am starting to switch from a well-known Java build system to Gradle to build all my projects, and after barely two hours into it I have already been able to publish a new version of one of my projects without a problem -- a breeze.
But now I encounter a difficulty. In short, I need to replicate the functionality of this Maven plugin which generates the necessary files for a ServiceLoader-enabled service.
In short: given a base class foo.bar.MyClass, it generates a file named META-INF/services/foo.bar.MyClass whose content is a set of classes in the current project which implement that interface/extend that base class. Such a file would look like:
com.mycompany.MyClassImpl
org.othercompany.MyClassImpl
In order to do this, it uses I don't know what as a classloader, loads the Class objects for com.myCompany.MyClassImpl or whatever and checks whether this class implements the wanted interface.
I am trying to do the same in Gradle. Hours of googling led me to this plugin, but after discussing with its author a little, it appears this plugin is able to merge such files, not create them. So, I have to do that myself...
And I am a real beginner both with Gradle and Groovy, which does not help! Here is my current code, link to the full build.gradle here; output (which I managed to get somehow; doesn't work from a clean dir) shown below (and please bear with me... I do Java, and I am final happy; Groovy is totally new to me):
/*
* TEST CODE
*/
final int CLASS_SUFFIX = ".class".length();
final URLClassLoader classLoader = this.class.classLoader;
// Where the classes are: OK
final File classesDir = sourceSets.main.output.classesDir;
final String basePath = classesDir.getCanonicalPath();
// Add them to the classloader: OK
classLoader.addURL(classesDir.toURI().toURL())
// Recurse over each file
classesDir.eachFileRecurse {
// You "return" from a closure, you do not "continue"...
if (!isPotentialClass(it))
return;
// Transform into a class name
final String path = it.getAbsolutePath();
final String name = path.substring(basePath.length() + 1);
final String className = name.substring(0, name.length() - CLASS_SUFFIX)
.replace('/', '.');
// Try and load it
try {
classLoader.loadClass(className);
println(className);
} catch (NoClassDefFoundError ignored) {
println("failed to load " + className + ": " + ignored);
}
}
boolean isPotentialClass(final File file)
{
return file.isFile() && file.name.endsWith(".class")
}
The output:
com.github.fge.msgsimple.InternalBundle
failed to load com.github.fge.msgsimple.bundle.MessageBundle: java.lang.NoClassDefFoundError: com/github/fge/Frozen
failed to load com.github.fge.msgsimple.bundle.MessageBundleBuilder: java.lang.NoClassDefFoundError: com/github/fge/Thawed
com.github.fge.msgsimple.bundle.PropertiesBundle$1
com.github.fge.msgsimple.bundle.PropertiesBundle
com.github.fge.msgsimple.provider.MessageSourceProvider
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$1
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$2
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$3
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$Builder
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider
com.github.fge.msgsimple.provider.MessageSourceLoader
com.github.fge.msgsimple.provider.StaticMessageSourceProvider$Builder
com.github.fge.msgsimple.provider.StaticMessageSourceProvider$1
com.github.fge.msgsimple.provider.StaticMessageSourceProvider
com.github.fge.msgsimple.source.MessageSource
com.github.fge.msgsimple.source.MapMessageSource$Builder
com.github.fge.msgsimple.source.MapMessageSource$1
com.github.fge.msgsimple.source.MapMessageSource
com.github.fge.msgsimple.source.PropertiesMessageSource
com.github.fge.msgsimple.locale.LocaleUtils
com.github.fge.msgsimple.serviceloader.MessageBundleFactory
com.github.fge.msgsimple.serviceloader.MessageBundleProvider
:compileJava UP-TO-DATE
The problem is in the two first lines: Frozen and Thawed are in a different project, which is in the compile classpath but not in the classpath I managed to grab so far... As such, these classes cannot even load.
How do I modify that code so as to have the full compile classpath availabe? Is my first question. Second question: how do I plug that code, when it works, into the build process?
Here are some hints:
Create a new URLClassLoader, rather than reusing an existing one.
Initialize the class loader with sourceSets.main.compileClasspath (which is an Iterable<File>) rather than classesDir.
Turn the code into a Gradle task class. For more information, see "Writing a simple task class" in the Gradle User Guide.
Ideally, you'd use a library like ASM to analyze the code, rather than using a class loader. To avoid the case where you cannot load a class because it internally references a class that's not on the compile class path, you may want to initialize the class loader with sourceSets.main.runtimeClasspath instead.

Loading Remote Groovy Script

I'm loading a Groovy script/class from a remote server, creating a new instance, and invoking a method, as shown below:
String[] scriptUrls = { "http://10.74.192.186/groovy/Test.groovy" };
GroovyScriptEngine gse = new GroovyScriptEngine(scriptUrls);
Class groovyClass = gse.loadScriptByName("Test.groovy");
GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
Object[] callArgs = {};
System.out.println(groovyObject.invokeMethod("getTest", callArgs));
How is the Groovy file compiled to bytecode when pulled off a server like this? Do I only need a JRE to run this?
I'm just a little confused how this works internally.
For reference, here is the Groovy file:
class Test {
String test = "test"
}
Thanks.
The Groovy file is compiled with the Groovy compiler, no JDK is needed (Groovy compiles directly to bytecode).

Categories