|
Project Information
Members
|
IntroductionEven now in 2011 there are lots of legacy projects that require Java 1.4 (particularly in big conservative business), and most of this projects were not designed with testability in mind, so they tend to use and abuse static (and sometimes final) methods. These methods are not easy to mock with standard tools like mockito. PowerMock is the solution, but it needs Java 1.5 or more. Well, if that sounds familiar to you, here is a solution. PowerMock-Legacy is a fork of PowerMock 1.3 that can help you mock objects and classes in Java 1.4. Requirements
And of course, PowerMock-Legacy, last version ;-) You can download all this files in one zip file in the Download section. News2011-08-18: Good news! (for my ego at least): Jan & Johan from JayWay, the original authors for PowerMock have been kind enough to link to this project. Yeah, I'm the greatest man alive! 2011-08-28: Bad news. I'm officially leaving the project. I don't need it anymore (working in java 1.5 for now on, oh yeah that's how I like it!), and I don't have either time or will to go on with this. Since PowerMock-Legacy had only 11 downloads, I assume there's not going to be a riot about that. If you want to use it, go on and download, it does work. If you want to improve it by fixing bugs and/or extending functionality, please do not fork it. Just drop me a line and I will give you full power over it. UsageRemember to put all needed jars in your buildpath! Notation is not very nice, but works. Imagine you have a nice class like package com.testpowermock;
public class ClassWithFinalMethod {
public final String getString() {
return "ORIGINAL_STRING";
}
}In a beautiful Java 1.5 world you can write something like @RunWith(PowerMockRunner.class) //Here you are saying "Use PowerMock Runner to run this class"
@PrepareForTest(ClassWithFinalMethod.class)//Here you are saying "Prepare ClassWithFinalMethod to be mocked with PowerMock"
public class TestClassWithFinalMethodClassic {
@Test //Here you are saying "This is a test"
public void getString() {
ClassWithFinalMethod classWithFinalMethod = PowerMockito.mock(ClassWithFinalMethod.class);
PowerMockito.when(classWithFinalMethod.getString()).thenReturn("NEW STRING");
assertEquals("NEW STRING", classWithFinalMethod.getString());
}
}
In our sad Java 1.4 world you'll need be more verbose public class TestClassWithFinalMethod
extends TestCase //this is the old JUnit 3.8.1 way to say "this is a test class" and gives you all the assert methods
implements IPrepareForTest //This interface says "We will need PowerMock-Legacy"{
//This method belongs to the interface IPrepareForTest and must return the list
//of classes that needs to be prepared
public Class[] classesToPrepare() {
return new Class[] { ClassWithFinalMethod.class};
}
//This method also belongs to the interface IPrepareForTest and can also be used
//to give classes and even namespaces that need to be prepared. But since we don't
//need it this time, we can simple return null
public String[] fullyQualifiedNamesToPrepare() {
return null;
}
//And this *static* method is the way to tell JUnit 3.8.1 to use PowerMock to run this test
public static TestSuite suite() throws Exception {
return new PowerMockSuite(new Class[] { TestAllAnnoyingClasses.class });
}
//And your test methods must start with "test", obviously
public void testGetString() {
//And this extra cast is needed because of the lack of generics
ClassWithFinalMethod classWithFinalMethod = (ClassWithFinalMethod) PowerMockito.mock(ClassWithFinalMethod.class);
PowerMockito.when(classWithFinalMethod.getString()).thenReturn("NEW STRING");
assertEquals("NEW STRING", classWithFinalMethod.getString());
}
}As you can see, it is more verbose, but not that verbose. You will pay a price to use this old version of Java. Don't blame on me. Another example for a static method. Let's say you have a class public class ClassWithStaticMethod {
public static String getString() {
return "ORIGINAL_STRING";
}
}You know what to do in Java 1.5. In Java 1.4 you need to write this: public class TestClassWithStaticMethod extends TestCase implements IPrepareForTest {
public Class[] classesToPrepare() {
return new Class[] { ClassWithStaticMethod.class};
}
public String[] fullyQualifiedNamesToPrepare() {
return null;
}
public static TestSuite suite() throws Exception {
return new PowerMockSuite(new Class[] { TestClassWithStaticMethod.class });
}
public void testGetString() {
PowerMockito.mockStatic(ClassWithStaticMethod.class);
PowerMockito.when(ClassWithStaticMethod.getString()).thenReturn("NEW STRING");
assertEquals("NEW STRING", ClassWithStaticMethod.getString());
}
}The general strategy is
Am I making myself clear? Check for other examples here Limitations, Bugs, Known Issues
Current status
TODO list
FAQWhy you changed annotations to interfaces?Annotations does not exist in Java 1.4 Wouldn't it be better to use a config file?Probably, buy I like my tests to be self contained What does the version number means? Do you really made 13 versions?Not at all. The 13 means this is a fork of PowerMock version 13. The 13.0.1 indicates the first working version of the fork, 13.0.2 the second one, and so on. How is this project organized?There are two Eclipse projects for each version called PowerMock-Legacy and PowerMock-Legacy-Test. PowerMock-Legacy is a Java 1.6 project I use to create a system that works both with annotations and interfaces. When I manage to make it work and pass some tests, I export it as jar file called C:\temp\PowerMock-Legacy.jar. Then I use RetroTranslator to create a Java 1.4 version called PowerMock-Legacy-jvm14-13.0.2 Once I get that file I copy it into PowerMock-Legacy-Test. This is a Java 1.4 project, create some tests and check what happens. Why you use Eclipse and even batch files in your project? Wouldn't it be better to use some IDE-OS-agnostic system like ANT or Maven?You are right, but I needed to make it work fast, and I work in Eclipse/Windows. So I was lazy. Next time I'll make a real decent project Please, don't go on reading...the next part is way too incomplete...just try and see if PowerMock-Legacy is what you need. Blast to the pastIf you need to make unit tests with Java 1.4, you must go back and get JUnit 3.8.1. Don't even dream about static imports and stuff like that.But that's not all. PowerMock works like an extension of EasyMock and Mockito that allows you to mock unmockable classes and methods, like final or static For my own personal needs, I only cared about making Mockito work. Unfortunately, like any other decent software, Mockito has abandon support for Java 1.4. Luckily, there is a guy called James Carr who has pretty much the same problem that me, and he had made a retro port of Mockito to Java 1.4. He had been nice enough to put share it here, and now Mockito people is offering it here. He managed to do that using Retrotranslator, an incredible tool that modifies Java 1.5 bytecode (not source) to make compatible with Java 1.4, and also uses a runtime jar to provide support for some classes that are not present in Java 1.4 VM, like AtomicInteger or Callable. I use Retrotranslator in this project too. Every version of PowerMock is heavily linked to some version of Mockito, so in order to use Mockito 1.8.0 I needed to go back and get PowerMock 1.3. So, to summarize, to create PowerMock-legacy I needed
The curse of annotationsRetrotranslator makes an excellent job, but in order to use PowerMock you need to use annotations, and there is no way of writing @PrepareForTest({ ClassWithStatic.class })
public class TestClassWithStatic {
public void testGetString() {
....
}
}All other components have a past in Java 1.4 and have a way to expose its features in a pre-annotation world. But PowerMock relays in annotations to know what classes should be loaded using its own custom classloader. The older way to mark a class in Java 1.4 was to use an interface.(As a matter of fact, an annotation is a strange kind of an interface). So, instead of writing an annotation in every class we need to prepare for test, we can make it implement an interface like this one public class TestAnnoyingClasses extends TestCase implements IPrepareForTest {
...
}
|