I'm trying to mock static method inside of static method.
I wonder if it is possible without using any framework.
Below is what I have tried.
Goal: Mock ImageIO object so that I know what to expect for return value.
class Foo {
public static SomeObject Run(String path) {
File file = new File(path);
BufferedImage buff = ImageIO.read(file);
SomeObject some = new SomeObject(buff);
return some;
}
}
I first tried using interface and create a wrapper for ImageIO.
interface ImageReader {
public BufferedImage read(File file);
}
class RealReader implements ImageReader {
#Override
public BufferedImage read(File file) {
return ImageIO.read(file);
}
}
class MockReader implements ImageReader {
#Override
public BufferedImage read(File file) {
BufferedImage buff = new BufferedImage(0,0,0);
//do whatever I like here and return the buff
return buff;
}
}
class Foo {
public static SomeObject run(String path) {
File file = new File(path);
//ImageIO wrapper
ImageReader reader = getImageReader();
BufferedImage buff = reader.read(file);
SomeObject some = new SomeObject(buff);
return some;
}
public static getImageReader() {
return RealReader();
}
}
And I tried to override static getImageReader() so that I can return MockReader object
which then call mock version of read() method:
class MockFoo {
#Override
public static getImageReader() {
return MockReader();
}
}
This is my test code:
public void TestRun() {
MockFoo foo = new MockFoo();
foo.run();
}
Then I figured I can't override static method getImageReader()......
Is there a way to achieve my goal?
One way is to change the signature of run:
public static SomeObject run(ImageReader imageReader, String path) {
// ...
Related
public interface FileReader {
void readFile();
void writeFile();
}
public class XMLReader implements FileReader {
#Override
public void readFile() {
System.out.println("Hellp i am read");
}
#Override
public void writeFile() {
System.out.println("Hello i am write");
}
}
public class ExcelReader implements FileReader {
#Override
public void readFile() {
System.out.println("Hellp i am read");
}
#Override
public void writeFile() {
System.out.println("Hello i am write");
}
}
public class Context {
FileReader reader ;
public Context(FileReader reader) {
super();
this.reader = reader;
}
public void executeRead(){
reader.readFile();
}
}
public class TestStrategy {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(args[0]);
String s=args[0];
String[] a=s.split("\\.");
if(a[1].equals("csv")){
new Context(new XMLReader()).executeRead();
}else{
new Context(new ExcelReader()).executeRead();
}
}
}
I have a concern more file format are introduced we will create separate class for them but i have to change the if else code in TestStrategy class to create new object for the file pattern introduced.
Can we remove this if else code .Any suggestions.
You could use a registry that maps a files extension to the implementation.
public class Registry {
static Map<String,FileReader> reg = new HashMap<String,FileReader>();
public static void register(String ext, FileReader fr ) {
reg.put( ext, fr );
}
}
and let newly added implementation register themself e.g.
public class XMLReader implements FileReader {
static {
Registry.register( "xml", new XMLReader() );
}
....
public class ExcelReader implements FileReader {
static {
Registry.register( "xls", new ExcelReader() );
}
...
then you could simply lookup the registry for a suitable implementation with no if or switch required.
You can get a class by name. Build a Map to configure the FileReader to use for each extension.
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Map<String, String> extensionToClass = new HashMap<>();
extensionToClass.put("xml", "de.lhorn.XMLReader");
extensionToClass.put("xls", "de.lhorn.ExcelReader");
String s = "foo.xml";
String[] a = s.split("\\.");
String extension = a[1];
// Get the class that is configured for the extension.
String className = extensionToClass.get(extension);
Class clazz = Class.forName(className);
// Create a new instance of this class.
FileReader reader = (FileReader) clazz.newInstance();
// Use the FileReader.
new Context(reader).executeRead();
}
You can read extensionToClass from an external source, of course.
I have read a little bit about 3rd party serialization methods such as JSON, but was wondering if there is any other way to serialize objects such as an area that do not implement serializable. In other word would JSON be the best way to serialize such an object?
EDIT : Throwing A NotSerializable Exception
public class Test {
public static void main(String[] args) throws Exception {
Pojo pojo = new Pojo(new Area()); // The original object, NOT SERIALIZABLE !
byte[] pojoBytes = Serializer.serialize(pojo); // Serialize
pojo = (Pojo) Serializer.deserialize(pojoBytes); // Deserialize
System.out.println(pojo); // Good as new !
}
}
public class Pojo implements Serializable {
private final Area msg;
public Pojo(Area msg) {
this.msg = msg;
}
public Area getMsg() {
return msg;
}
public String toString() {
return "Pojo says : " + msg;
}
}
public class Serializer {
public static byte[] serialize(Object o) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileOutputStream fileOut = new FileOutputStream("Test.ser");
ObjectOutputStream oos = new SurrogateObjectOutputStream(fileOut); // Magically handle Pojos !
oos.writeObject(o);
oos.flush();
oos.close();
return baos.toByteArray();
}
public static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
FileInputStream fileIn = new FileInputStream("Test.ser");
ObjectInputStream ois = new ObjectInputStream(fileIn);
Object o = ois.readObject();
bais.close();
return o;
}
}
public class SurrogateObjectOutputStream extends ObjectOutputStream {
public SurrogateObjectOutputStream(OutputStream out) throws IOException {
super(out);
enableReplaceObject(true);
}
protected SurrogateObjectOutputStream() throws IOException, SecurityException {
super();
enableReplaceObject(true);
}
#Override
protected Object replaceObject(Object obj) throws IOException {
if (obj instanceof Pojo) {
return new PojoSurrogate((Pojo) obj);
} else return super.replaceObject(obj);
}
}
public class PojoSurrogate implements Serializable {
private Area foo;
public PojoSurrogate(Pojo pojo) {
this.foo = pojo.getMsg();
}
private Object readResolve() throws ObjectStreamException {
return new Pojo(foo);
}
}
It depends, if you want to use that Object in another program or another language for that matter, then yes JSON is the way to go (or XML).
But if you want to reuse that Object in another JAVA program then I guess it would be more convenient to look for a way to make possible non-serializable Objects serializable.
I haven't tested it yet, but I found a promising solution in this blog (which is in French, sorry). I'll try my best to summarize it:
what you have
say you have a class names Pojo and you want to serialize it although you don't know if it is serializable or not.
public final class Pojo {
private final String msg;
public Pojo(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
public String toString() {
return "Pojo says : " + msg;
}
}
what you need
what you need is a new class that acts as a surrogate, which will take the member variables of the real Pojo and simply replace it.
public class PojoSurrogate implements Serializable {
private String foo;
public PojoSurrogate(Pojo pojo) {
this.foo = pojo.getMsg();
}
private Object readResolve() throws ObjectStreamException {
return new Pojo(foo);
}
}
the last method ( readResolve() ) is the one that will in the end give you back your new Pojo later.
The other thing you need is your own surrogate version of the ObjectOutputStream:
public class SurrogateObjectOutputStream extends ObjectOutputStream {
public SurrogateObjectOutputStream(OutputStream out) throws IOException {
super(out);
enableReplaceObject(true);
}
protected SurrogateObjectOutputStream() throws IOException, SecurityException {
super();
enableReplaceObject(true);
}
#Override
protected Object replaceObject(Object obj) throws IOException {
if (obj instanceof Pojo) {
return new PojoSurrogate((Pojo) obj);
} else return super.replaceObject(obj);
}
}
And again here the last method replaceObject() will do the magic and transform the Pojo into the serializable version PojoSurrogate to store all the information as bytes.
Serialize like this
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new SurrogateObjectOutputStream(baos);
oos.writeObject(o);
oos.flush();
oos.close();
byte[] serializedPojo = baos.toByteArray();
Deserialize normally
ObjectInputStream bais = new ObjectInputStream(new ByteArrayInputStream( serializedPojo ));
Pojo myNewPojo = (Pojo) bais.readObject();
bais.close();
Sorry, long answer.. and I hope I haven't missed anything supercool from that blog where it is easier to create a more scalable solution.. hope this helps anyway!
[EDIT:]
I tried your code with an Area object and here is how I got some thing to work (although I am not sure if this is actually working with all Areas, so you might have to test if your areas still have the same characteristics after deserialization)
AreaSurrogate
public class AreaSurrogate implements Serializable {
private final Rectangle bounds;
public AreaSurrogate(Area area) {
this.bounds = area.getBounds();
}
private Object readResolve() throws ObjectStreamException {
return new Area(bounds);
}
}
SurrogateOutputStream
public class SurrogateOutputStream extends ObjectOutputStream {
public SurrogateOutputStream(OutputStream out) throws IOException {
super(out);
enableReplaceObject(true);
}
protected SurrogateOutputStream() throws IOException, SecurityException {
super();
enableReplaceObject(true);
}
#Override
protected Object replaceObject(Object obj) throws IOException {
if (obj instanceof Area) {
return new AreaSurrogate((Area) obj);
} else {
return super.replaceObject(obj);
}
}
}
Serializer
public class Serializer {
public static byte[] serialize(Object o) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new SurrogateOutputStream(baos); // Magically handle Pojos !
oos.writeObject(o);
oos.flush();
oos.close();
return baos.toByteArray();
}
public static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
bais.close();
return o;
}
}
Main (to test)
public static void main(String[] args) throws Exception {
Area area = new Area(new Rectangle(0, 0, 100, 100)); // The original object, NOT SERIALIZABLE !
System.out.println(area.contains(new Rectangle(1, 1, 10, 10))); // Good as new !
System.out.print("serializing...");
byte[] pojoBytes = Serializer.serialize(area); // Serialize
System.out.println("done");
System.out.print("deserializing...");
area = (Area) Serializer.deserialize(pojoBytes); // Deserialize
System.out.println("done");
System.out.println(area.contains(new Rectangle(1, 1, 10, 10))); // Good as new !
}
In the main() I create an Area from a Rectangle which starts at Coordinate (0,0) and is 100 wide and 100 high. Then I test if the Rectangle from (1,1) with 10 width and 10 height is inside the area (which it is, forcibly). After serializing and deserializing I test if the same Rectangle is still inside the new Area.
This might be insufficient, since the new Area Object is created from a Rectangle (see AreaSurrogate). so this might not work with other Area shapes..
expected output
true
serializing...done
deserializing...done
true
I'd like to change from which .properties file class should get them.
My class is like this now:
public class MyClass {
private String str;
public MyClass() throws IOException {
loadProperties();
}
private void loadProperties() throws IOException {
Properties props = new Properties();
props.load(getClass().getClassLoader().getResourceAsStream("my.properties"));
str= props.getProperty("property");
}
And whyle testing i would like properties to be loaded from another file.
It's apache camel app, so i have this now:
public class ConverterTest {
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new MyClass(); //--> Here i must load from another file
}
#Test
// test
}
Can this be achieved?
Just pass property file name to MyClass constructor
public MyClass(String propsFile) throws IOException {
loadProperties(propsFile);
}
There is something you can do:
public class MyClass {
private String str;
private String path = "my.properties";
public MyClass() throws IOException {
loadProperties();
}
protected void loadProperties() throws IOException {
Properties props = new Properties();
props.load(getClass().getClassLoader().getResourceAsStream(path));
str= props.getProperty("property");
}
And then, add a test to the same package with the code:
myClass = new MyClass();
ReflectionTestUtils.setField(path, "otherpathto.properties");
myClass.loadProperties();
It involves a small change in the code, but it might not be a big deal... depending on your project.
Arguably the cleanest solution would be to refactor MyClass and remove dependency on Properties object and inject the values needed via the constructor instead. Your case proves that hidden and hardcoded dependencies complicate testing.
Responsibility for reading the properties file and injecting the value into MyClass could be pushed back to its caller:
public class MyClass {
private final String str;
public MyClass(String strValue) {
this.str = strValue;
}
// ...
}
public class ProductionCode {
public someMethod() {
Properties props = new Properties();
props.load(getClass().getClassLoader().getResourceAsStream("my.properties"));
String str = props.getProperty("property");
MyClass obj = new MyClass(str);
obj.foo();
}
}
public class ConverterTest {
#Test
public void test() {
String testStr = "str for testing";
MyClass testee = new MyClass(testStr);
testee.foo();
// assertions
}
}
I have the following class:
class MyClass{
private static final int VERSION_VALUE = 8;
private static final String VERSION_KEY = "versionName";
public boolean myPublicMethod(String str) {
try {
return myPrivateMethod(str, VERSION_KEY, VERSION_VALUE,
new MyInnerClass() {
#Override
public InputStream loadResource(String name) {
//do something important
}
});
}
catch (Exception e) {
}
return false;
}
private boolean myPrivateMethod(String str, String key, int version,
ResourceLoader resourceLoader) throws Exception
{
//do something
}
private static abstract class MyInnerClass {
public abstract InputStream loadResource(String name);
}
}
I want to write unit test for myPrivateMethod for which I need to pass resourceLoader object and override it's loadResource method.
Here is my test method:
#Test
public void testMyPrivateMethod() throws Exception {
Class<?> cls = Class.forName("my.pack.MyClass$MyInnerClass");
Method method = cls.getDeclaredMethod("loadResource", String.class);
//create inner class instance and override method
Whitebox.invokeMethod(myClassObject, "testValue1", "testValue2", "name1", 10, innerClassObject);
}
Note, that I can't change code.
Well, you could use Javassist...
See this question. I haven't tried this, but you can call this method when you want the override:
public <T extends Object> T getOverride(Class<T> cls, MethodHandler handler) {
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(cls);
factory.setFilter(
new MethodFilter() {
#Override
public boolean isHandled(Method method) {
return Modifier.isAbstract(method.getModifiers());
}
}
);
return (T) factory.create(new Class<?>[0], new Object[0], handler);
}
Well, the problem i see with your code is that you are calling myPublicMethod and you are giving fourth parameter as new MyInnerClass(). Now in your private method fourth parameter is given as ResourceLoader and from your code i see no relation between MyInnerClass and ResourceLoader. So you can try out following code. It might help.
Despite your warning that you cannot change the code i have changed it because i was trying to run your code.
class MyClass{
private static final int VERSION_VALUE = 8;
private static final String VERSION_KEY = "versionName";
public boolean myPublicMethod(String str) {
try {
return myPrivateMethod(str, VERSION_KEY, VERSION_VALUE,
new MyInnerClass() {
#Override
public InputStream loadResource(String name) {
return null;
//do something important
}
});
}
catch (Exception e) {
}
return false;
}
private boolean myPrivateMethod(String str, String key, int version,
MyInnerClass resourceLoader) throws Exception
{
return false;
//do something
}
private static abstract class MyInnerClass {
public abstract InputStream loadResource(String name);
}
}
Hope it helps.
Need to create a class that i can load properties in and be able to call required properties from that class. such as propertiesClass.getname();
Here's my Class so far. I can't seem to initiate the property load.
So what i need is from another class in the project to just do (currently getting null)
String url = TestProperties.getBaseUrl();
*updated the class, here's what it looks like now.
public class TestProperties {
private static Properties testProperties;
private static String instanceUrl;
public TestProperties() throws Exception{
loadProperties();
getInstanceProperties();
instanceUrl = TestProperties.testProperties.getProperty("confluence.base.url","");
}
public static String getBaseUrl(){
return instanceUrl;
}
private void loadProperties() throws IOException {
InputStream testPropertiesInput = this.getClass().getClassLoader().getResourceAsStream("smoketest.properties");
TestProperties.testProperties = new Properties();
// if (null != testProperties) {
try{
TestProperties.testProperties.load(testPropertiesInput);
} finally {
IOUtils.closeQuietly(testPropertiesInput);
}
// }
}
}
my otherclass(){
String myurl = TestProperties.getBaseUrl();
}
The method
public void TestProperties() throws Exception{
was meant to be constructor but isn't one, so the class only gets a default no-arg constructor. Change that to:
public TestProperties() throws Exception{
i.e. remove the return type as constructors are distinguished from ordinary methods by not declaring a return type.
Please make sure you are able to rightly access your properties file smoketest.properties though InputStream testPropertiesInput.
EDIT:
There is no need to redefine the local variable in loadProperties and return it. It can be written very simply as:
private static void loadProperties() throws IOException {
InputStream testPropertiesInput = getClass().getClassLoader()
.getResourceAsStream("smoketest.properties");
Properties testProperties = new Properties();
try{
TestProperties.testProperties.load(testPropertiesInput);
} finally {
IOUtils.closeQuietly(testPropertiesInput);
}
TestProperties.testProperties = testProperties;
}
I think public void TestProperties() throws Exception{ is constructor of your class. If yes, please remove void from it as it making it as a method.
Finally you may want to use testProperties in your TestProperties() constructor as :
public TestProperties() throws Exception{
loadProperties();
getInstanceProperties();
instanceUrl = TestProperties.testProperties
.getProperty("confluence.base.url","");
}
Please Note: I don't think your class variables should be defined as static. Is there any reason of doing so?
EDIT: Here is the hopefully working sample code for you:
public class TestProperties {
private static Properties testProperties;
private static String instanceUrl;
public TestProperties(){
try{
loadProperties();
//getInstanceProperties();
instanceUrl = TestProperties.testProperties
.getProperty("confluence.base.url","");
}catch(IOException ioe){
ioe.printStackTrace();
}
}
static{
//Just to initialize the properties
new TestProperties();
}
private void loadProperties() throws IOException {
InputStream testPropertiesInput = getClass().getClassLoader()
.getResourceAsStream("smoketest.properties");
Properties testProperties = new Properties();
try{
testProperties.load(testPropertiesInput);
} finally {
IOUtils.closeQuietly(testPropertiesInput);
}
TestProperties.testProperties = testProperties;
}
public static String getBaseUrl(){
return instanceUrl;
}
public static String getPropertyValue(String key){
return TestProperties.testProperties.getProperty(key,"Not Found");
}
}
Now you can simply get your base URL any where as :
public static void main(String[] args) {
System.out.println(TestProperties.getBaseUrl());
System.out.println(TestProperties.getPropertyValue("confluence.base.url"));
System.out.println(TestProperties.getPropertyValue("test.property"));
}