Created
January 10, 2024 04:25
-
-
Save ondrej-kvasnovsky/c57bdfae8220a06f8354cef7678cb98b to your computer and use it in GitHub Desktop.
Mocker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example; | |
import java.lang.reflect.InvocationHandler; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Proxy; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
public class Mocker implements InvocationHandler { | |
private static final Mocker handler = new Mocker(); | |
public static <T> T createMock(Class<T> clazz) { | |
Class<?> proxyClass = Proxy.newProxyInstance( | |
clazz.getClassLoader(), | |
new Class[]{clazz}, | |
handler | |
) | |
.getClass(); | |
try { | |
return (T) proxyClass | |
.getConstructor(new Class[]{InvocationHandler.class}) | |
.newInstance(new Object[]{handler}); | |
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | | |
NoSuchMethodException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
private static final ThreadLocal<Method> methodThreadLocal = new ThreadLocal<>(); | |
private static final Map<String, Object> methodToReturnValue = new HashMap<>(); | |
public Object invoke(Object proxy, Method method, Object[] args) { | |
// when they call when(obj.getSomething)), we register it as a mocked method we are mocking at this point | |
methodThreadLocal.set(method); | |
if (methodToReturnValue.containsKey(method.getName())) { | |
return methodToReturnValue.get(method.getName()); | |
} | |
return null; | |
} | |
public static <T> OngoingMocking<T> when(T methodCall) { | |
return new OngoingMocking<>(); | |
} | |
public static class OngoingMocking<T> { | |
public void thenReturn(T value) { | |
Method method = methodThreadLocal.get(); | |
methodThreadLocal.remove(); | |
methodToReturnValue.put(method.getName(), value); | |
} | |
} | |
public static void main(String[] args) { | |
ItemService userServiceMock = Mocker.createMock(ItemService.class); | |
when(userServiceMock.fetch(1)) | |
.thenReturn(new Item("An item returned from mocked service")); | |
when(userServiceMock.fetchAll()) | |
.thenReturn(List.of( | |
new Item("Item 1"), | |
new Item("Item 2") | |
)); | |
Controller controller = new Controller(userServiceMock); | |
Item item = controller.fetch(1); | |
System.out.println("A mocked item: " + item); | |
List<Item> items = controller.fetchAll(); | |
System.out.println("Mocked items: " + items); | |
} | |
} | |
record Item(String name) { | |
} | |
interface ItemService { | |
Item fetch(int id); | |
List<Item> fetchAll(); | |
} | |
class Controller { | |
private final ItemService itemService; | |
public Controller(ItemService itemService) { | |
this.itemService = itemService; | |
} | |
public Item fetch(int id) { | |
return itemService.fetch(id); | |
} | |
public List<Item> fetchAll() { | |
return itemService.fetchAll(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment