I am attempting to change some third party class definitions, before each test, to simulate different results. I have to use something like javassist because extending the classes, sometimes, is just not possible due to the access modifiers. Here is an example of what I am attempting to do with javassist and junit combined:
public class SimulatedSession extends SomeThirdParty {
private boolean isJoe = false;
public SimulatedSession(final boolean isJoe) {
this.isJoe = isJoe;
}
#Override
public void performThis() {
final ClassPool classPool = ClassPool.getDefault();
final CtClass internalClass = classPool.get("some.package.Class");
final CtMethod callMethod = internalClass.getDeclaredMethod("doThis");
if (isJoe) {
callMethod.setBody("{System.out.println(\"Joe\");}");
} else {
callMethod.setBody("{System.out.println(\"mik\");}");
}
internalClass.toClass();
}
}
#Test
public void firstTest() {
SimulatedSession toUse = new SimulatedSession(false);
// do something with this object and this flow
}
#Test
public void nextTest() {
SimulatedSession toUse = new SimulatedSession(true);
// do something with this object and this flow
}
if I run each test individually, I can run the code just fine. When I run them using the unit suite, one test after the other, I get a "frozen class issue". To get around this, I am looking at this post, however, I must admit I am unsure as to how one can use a different class pool to solve the issue.
Your current code will try to load twice the same class into the same ClassLoader which is forbidden, you can only load once a class for a given ClassLoader.
To make your unit tests pass, I had to:
Create my own temporary ClassLoader that will be able to load some.package.Class (that I replaced by javassist.MyClass for testing purpose) and that will be implemented in such way that it will first try to load the class from it before the parent's CL.
Set my own ClassLoader as context ClassLoader.
Change the code of SimulatedSession#performThis() to be able to get the class instance created by this method and to call internalClass.defrost() to prevent the "frozen class issue".
Invoke by reflection the method doThis() to make sure that I have different output by using the class instance returned by SimulatedSession#performThis() to make sure that the class used has been loaded with my ClassLoader.
So assuming that my class javassist.MyClass is:
package javassist;
public class MyClass {
public void doThis() {
}
}
The method SimulatedSession#performThis() with the modifications:
public Class<?> performThis() throws Exception {
final ClassPool classPool = ClassPool.getDefault();
final CtClass internalClass = classPool.get("javassist.MyClass");
// Prevent the "frozen class issue"
internalClass.defrost();
...
return internalClass.toClass();
}
The unit tests:
// The custom CL
private URLClassLoader cl;
// The previous context CL
private ClassLoader old;
#Before
public void init() throws Exception {
// Provide the URL corresponding to the folder that contains the class
// `javassist.MyClass`
this.cl = new URLClassLoader(new URL[]{new File("target/classes").toURI().toURL()}){
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
try {
// Try to find the class for this CL
return findClass(name);
} catch( ClassNotFoundException e ) {
// Could not find the class so load it from the parent
return super.loadClass(name, resolve);
}
}
};
// Get the current context CL and store it into old
this.old = Thread.currentThread().getContextClassLoader();
// Set the custom CL as new context CL
Thread.currentThread().setContextClassLoader(cl);
}
#After
public void restore() throws Exception {
// Restore the context CL
Thread.currentThread().setContextClassLoader(old);
// Close the custom CL
cl.close();
}
#Test
public void firstTest() throws Exception {
SimulatedSession toUse = new SimulatedSession(false);
Class<?> c = toUse.performThis();
// Invoke doThis() by reflection
Object o2 = c.newInstance();
c.getMethod("doThis").invoke(o2);
}
#Test
public void nextTest() throws Exception {
SimulatedSession toUse = new SimulatedSession(true);
Class<?> c = toUse.performThis();
// Invoke doThis() by reflection
Object o2 = c.newInstance();
c.getMethod("doThis").invoke(o2);
}
Output:
mik
Joe
Take a look at retransformer. It's a Javassist based lib I wrote for running tests just like this. It's a bit more terse than using raw Javassist.
Maybe another approach. We had a similar problem as we once mocked a dependency - we could not reset it. So we did the following: Before each test we replace the 'live' instance with our mock. After the tests, we restore the live instance. So I propose that you replace the modified instance of your third party code for each test.
#Before
public void setup()
{
this.liveBeanImpl = (LiveBean) ReflectionTools.getFieldValue(this.beanToTest, "liveBean");
ReflectionTools.setFieldValue(this.beanToTest, "liveBean", new TestStub());
}
#After
public void cleanup()
{
ReflectionTools.setFieldValue(this.beanToTest, "liveBean", his.liveBeanImpl);
}
The setFieldValue looks like this:
public static void setFieldValue(Object instanceToModify, String fieldName, Object valueToSet)
{
try
{
Field declaredFieldToSet = instanceToModify.getClass().getDeclaredField(fieldName);
declaredFieldToSet.setAccessible(true);
declaredFieldToSet.set(instanceToModify, valueToSet);
declaredFieldToSet.setAccessible(false);
}
catch (Exception exception)
{
String className = exception.getClass().getCanonicalName();
String message = exception.getMessage();
String errorFormat = "\n\t'%s' caught when setting value of field '%s': %s";
String error = String.format(errorFormat, className, fieldName, message);
Assert.fail(error);
}
}
So maybe your tests will pass if you reset your implementation for each test. Do you get the idea?
Related
I have one method like below:
public final class someTask {
public void sampleMethod(String filePath) {
//It is calling Files.delete() method.
Files.delete(new File(filePath).toPath)
}
}
When I test above method(for example ValidRecord in that file, Valid file parameter or not etc) test cases, most of the time my test case failed. Please some one can help me how to test for above cases.
To get file I am using below snippet
#Mock
File fileMock;
#Rule
ExpectedException exception = ExpectedException.none();
PowerMockito.whenNew(File.class).withArguments(VALID_Path).thenReturn(fileMock);
PowerMockito.when(fileMock.exists()).thenReturn(true);
PowerMockito.when(fileMock.isFile()).thenReturn(true);
In this, I am not planning to test Files.deplete() method but I am planning test behaviour of my own method. In that process every time I am getting "java.nio.file.NoSuchFileException" exception even I create temporary file.
Please some can provide with example, how to test this.
One might argue that your approach is wrong by verifying the behaviour through mocking and interactions. I would do the following
Create a temp file on local file system
Call the method sampleMethod
Verify that the file doesn't exist anymore.
A valuable approach to such kinds of question is to go one step back criticize your application design. Ask yourself, whether you have design issues, especially when you feel that testing your code seems to be a bit difficult.
And indeed: You can/should improve the design of your software!
Let's start with your class (btw, I changed names to camel-casing):
public final class SomeTask {
public void sampleMethod(String filePath) {
Files.delete(new File(filePath).toPath);
}
}
I am going to ignore exceptions. Also, somewhere in your code you are using this class:
SomeTask task = new SomeTask();
String filePath = ...
task.sampleMethod(filePath);
The first thing to realize: In your class SomeTask you have a dependency to a delete-a-file functionality. Make this dependency visible!
But how do you do that? With an interface:
#FunctionalInterface
public interface FileDeletor {
void delete(String filePath);
}
You now can change your class by adding a field for such a deletor:
public final class SomeTask {
private final FileDeletor deletor;
public SomeTask(FileDeletor deletor) {
this.deletor = Objects.requireNonNull(deletor);
}
public void sampleMethod(String filePath) {
deletor.delete(filePath);
}
}
With this approach you delegate the technical doing of a file deletion to another class that now must implement the interface (shown below). This also makes your class more coherent because it now can concentrate on its own feature(s).
We now need a class that implements that interface:
public final class DefaultFileDeletor implements FileDeletor {
#Override
public void delete(String filePath) {
Files.delete(new File(filePath).toPath());
}
}
Note, that I am still ignoring exceptions here. Now we must also change the using side:
FileDeletor deletor = new DefaultFileDeletor();
SomeTask task = new SomeTask(deletor);
String filePath = ...
task.sampleMethod(filePath);
With this approach you can also build up an application via Spring autowiring or similar dependency injection frameworks.
Let us move on to the testing area. If you wish, you can also test your new class DefaultFileDeletor. But you know that it only uses a JDK functionality which itself is tested enough. No need to test this simple class.
But how can you now test your class SampleTask? First, you need an implementation of the interface just for the purposes of testing:
public class FileDeletorForTestPurposes implements FileDeletor {
private String filePath;
private boolean deleted;
#Override
public void delete(String filePath) {
this.filePath = filePath;
deleted = true;
}
public String getFilePath() {
return filePath;
}
public boolean isDeleted() {
return deleted;
}
}
Now you are able to test your code:
FileDeletorForTestPurposes deletor = new FileDeletorForTestPurposes();
SomeTask task = new SomeTask(deletor);
String filePath = ...
task.someMethod(filePath);
assertEquals(filePath, deletor.getFilePath());
assertEquals(true, deletor.isDeleted());
You are now also able to simply create a FileDeletor mock and use that by expressing expectations on it.
How to test delete files with Files.delete fuction below is the example
Lets say you have one function like below
public boolean deleteFilesWithinPath(Path path, int retryCount){
boolean status=false;
while (retryCount < 3) {
try {
Files.delete(path);
LOG.info("File deleted successfully from work Dir");
status=true;
break;
} catch (IOException e) {
try {
LOG.error(e.getMessage());
LOG.info("File was not deleted . Retrying ..");
status=false;
Thread.sleep(2000);
retryCount++;
} catch (InterruptedException e1) {
status=false;
LOG.error(e1.getMessage());
Thread.currentThread().interrupt();
}
}
}
return status;
}
Then you can test above function like below with Powermockit or Mockito
#Test
public void testdeleteFilesWithinPath() throws Exception{
File mockFileTemp = temporaryFolder.newFile("test.csv");
PowerMockito.mockStatic(Paths.class);
PowerMockito.mockStatic(Files.class);
int retrycount=0;
Path path = mockFileTemp.getAbsoluteFile().toPath();
boolean status = mftFileDownloadServiceImp.deleteFilesWithinPath(path,retrycount);
assertTrue(status);
}
#Test
public void testdeleteFilesWithinPathForIOException() throws Exception{
File mockFile = temporaryFolder.newFile("test.csv");
PowerMockito.mockStatic(Paths.class);
PowerMockito.mockStatic(Files.class);
Path path = mockFile.getAbsoluteFile().toPath();
Files.delete(path);
int retrycount=0;
try{
mftFileDownloadServiceImp.deleteFilesWithinPath(path,retrycount);
} catch (Exception e) {
assertThat(e).isInstanceOf(IOException.class);
}
}
For example I've got a method and I want to test case when it throws certain exception type. There's no constructor without parameters in MyException class.
public class Clazz {
private Service service;
public void method() {
try {
int result = service.perform();
} catch (MyException exc) {
throw exc;
}
}
}
I created mock for service and set it into Clazz instance.
#Before
public void setUp() {
clazz = new Clazz();
service = createStrictMock(Service.class);
clazz.setService(service);
}
How should test look like? I mean how the test method for method should look like?
try:
expect(service.perform()).andThrow(new MyException(/* some params */));
For more details go to documentation (Working with Exceptions) or this thread
Because you rethrow exception you may also want to add #Test(expected=MyException.class) to the test method.
You could use the following (assuming your #setup works)
#Test(expected = MyException.class)
public void testExceptionThrown() throws Exception(){
Clazz subject = new Clazz();
subject.setService(service);
subject.method();
}
I'm using a custom Classloader to create and return an instance of a class, this seems to work ok however when I try to call a method (via the Reflection API) and pass in a custom object as described below I get a NoSuchMethodException:
Supposing that the custom class loader creates and returns an instance like so:
Object obj = customClassLoader.load(String className,Class[] paramTypes,Object[] param)
Then I call a method (via reflection) and pass in a custom object:
NOTE: THIS IS THE LINE CAUSING THE ERROR
Method m = obj.getClass.getDeclaredMethod("mName",new Class[]{aCustomObject.class})
m.invoke(obj,new Object[]{new CustomObject() })
I'm stumped as to what could be causing the exception since a method definitely does exist which takes the specified custom object, I have confirmed this by using reflection to list all methods.
How is your custom loader's load() method instantiating the object it is to return? Maybe the NoSuchMethodException arises during trying to find the correct constructor?
This example seems to work out OK:
package com.pholser;
import java.lang.reflect.Method;
public class ClassLoading {
public static class CustomLoader extends ClassLoader {
public Object load(String className, Class<?>[] paramTypes, Object[] params) throws Exception {
Class<?> loaded = loadClass(className);
return loaded.getConstructor(paramTypes).newInstance(params);
}
}
public static class ACustomObject {
}
public void foo(ACustomObject a) {
System.out.println("foo");
}
public static Object newCustomObject() throws Exception {
return new CustomLoader().load("com.pholser.ClassLoading$ACustomObject", new Class<?>[0], new Object[0]);
}
public static void main(String[] args) throws Exception {
ClassLoading obj = new ClassLoading();
Method m = obj.getClass().getDeclaredMethod("foo", ACustomObject.class);
m.invoke(obj, newCustomObject());
}
}
I implemented a ClassFileTransformer for a javaagent using ASM. Because it has some bugs, I want to write a JUnit test case for it. How do I do this?
Using pseudo-code I thought along the lines:
// Have a test class as subject
public static class Subject {
public void doSomething(){...}
}
// Manually load and transform the subject
...?
// Normally execute some now transformed methods of the subject
new Subject().doSomething();
// Check the result of the call (i.e. whether the correct attached methods were called)
Assert.assertTrue(MyClassFileTransformer.wasCalled());
Now the question is: How do I manually load and transform the subject and make the JVM/Classloader use my manipulated version of it? Or do I completely miss something?
I got it. One needs to implement an own ClassLoader that does the same transformation with the test subject as the ClassFileTransformer (e.g. calls it). And of course the subject class may not already be loaded, so there may not be any direct usage of it. So I used Java reflection API to execute the methods of the subject class.
In a separate file:
public static class Subject {
public void doSomething(){...}
}
In the test:
private static class TransformingClassLoader extends ClassLoader {
private final String className;
public TransformingClassLoader(String className) {
super();
this.className = className;
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.equals(className)) {
byte[] byteBuffer = instrumentByteCode(fullyQualifiedSubjectClass);
return defineClass(className, byteBuffer, 0, byteBuffer.length);
}
return super.loadClass(name);
}
}
#Test
public void testSubject(){
ClassLoader classLoader = new TransformingClassLoader(fullyQualifiedSubjectClass);
Class<?> subjectClass = classLoader.loadClass(fullyQualifiedSubjectClass);
Constructor<?> constructor = subjectClass.getConstructor();
Object subject = constructor.newInstance();
Method doSomething = subjectClass.getMethod("doSomething");
doSomething.invoke(subject);
Assert.assertTrue(MyClassFileTransformer.wasCalled());
}
i want to instantiate two TCP server applications within the same main method. Those server classes use lots of static and thread local fields. Is there a chance to load classes like in a different application domain?
this is my test case:
Tester class has simple getter and setter methods for setting global static object.
public class Tester {
public Tester() {
System.out.println(getClass().getClassLoader());
}
public void setText(String text) {
GlobalObject.globalText = text;
}
public String getText() {
return GlobalObject.globalText;
}
}
This is global object that is accessible from every where. I want to limit access to this object.
public class GlobalObject {
public static String globalText;
}
This is my test program.
public class Main {
public static void main(String[] args) {
// Default class loader;
Tester ta1 = new Tester();
ta1.setText("test");
System.out.println(ta1.getText());
Tester ta2 = new Tester();
System.out.println(ta2.getText());
// Custom class loader;
CustomClassLoader ccl = new CustomClassLoader();
try {
Tester tb = (Tester) ccl.loadClass("Tester").newInstance();
System.out.println(tb.getText());
} catch (Exception e) {
e.printStackTrace();
}
}
}
The ouput is:
sun.misc.Launcher$AppClassLoader#11b86e7
test
sun.misc.Launcher$AppClassLoader#11b86e7
test
sun.misc.Launcher$AppClassLoader#11b86e7
test
The output that i want:
sun.misc.Launcher$AppClassLoader#11b86e7
test
sun.misc.Launcher$AppClassLoader#11b86e7
test
sun.misc.Launcher$AppClassLoader#1234567
null
You don't tell us what CustomClassLoader is.
But in general, the default behaviour of class loaders is to delegate to their parent, so by default all class loaders eventually delegate to the actual system class loader.
Try creating a class loader without a parent. This is how it would look like with a standard classloader:
URL[] urls = new URL[] {new File("build/classes/").toURL()};
ClassLoader loader = new URLClassLoader(urls, null);
The second constructor parameter is the parent.