Yes it is possible. Suppose you have a wired class, possibly extracting something from the database and want to mock it through an aspect:
package de.scrum_master.aop.app; public class HardWired { private int id; private String name; public HardWired(int id, String name) { this.id = id; this.name = name; } public void doSomething() { System.out.println("Fetching values from database"); } public int getSomething() { return 11; } @Override public String toString() { return "HardWired [id=" + id + ", name=" + name + "]"; } }
Then there is a small driver application that uses this same class (and not an interface):
package de.scrum_master.aop.app; public class Application { public static void main(String[] args) { HardWired hw = new HardWired(999, "My object"); System.out.println(hw); hw.doSomething(); System.out.println(hw.getSomething()); } }
The output is as follows:
HardWired [id=999, name=My object] Fetching values from database 11
Now you define your derived mock class, which should replace the original for testing purposes:
package de.scrum_master.aop.mock; import de.scrum_master.aop.app.HardWired; public class HardWiredMock extends HardWired { public HardWiredMock(int id, String name) { super(id, name); } @Override public void doSomething() { System.out.println("Mocking database values"); } @Override public int getSomething() { return 22; } @Override public String toString() { return "Mocked: " + super.toString(); } }
And finally, you define an aspect with a simple pointcut and advice for replacing the original value during each constructor call:
package de.scrum_master.aop.aspect; import de.scrum_master.aop.app.HardWired; import de.scrum_master.aop.mock.HardWiredMock; public aspect MockInjector { HardWired around(int p1, String p2) : call(HardWired.new(int, String)) && args(p1, p2) { return new HardWiredMock(p1, p2); } }
The output changes as desired:
Mocked: HardWired [id=999, name=My object] Mocking database values 22
You do this once for each class and constructor and in order. To generalize the approach, you will need connection properties and, depending on how far you want to go, maybe a reflection, but it's pretty simple. Enjoy it!