I am trying to use a parameterized test and I want to use Mockito.when() the following scenario. I am not in a position where I can modify any code except for the test. I have put a comment on the line that is causing a compile error at the moment. I am having a hard time putting it into words, but basically I want to be able to mock the method without having access to the exact type during compile time.
Test class:
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Map;
import static com.example.demo.Type.A;
import static com.example.demo.Type.B;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.*;
#ExtendWith(MockitoExtension.class)
public class MyTest {
private static final Map<Type, Class<? extends MyStuff>> classByType = Map.of(A, StuffA.class, B, StuffB.class);
#InjectMocks
private Handler handler;
#Mock
private Converter converter;
#Mock
private Sender sender;
#Captor
private ArgumentCaptor<Thing> thingArgumentCaptor;
#ParameterizedTest
#EnumSource(value = Type.class)
void testHandle(Type type) {
MyStuff myStuff = mock(classByType.get(type));
Thing thing = mock(Thing.class);
when(myStuff.getType()).thenReturn(type);
when(converter.convert(classByType.get(type).cast(type))).thenReturn(thing); // This line is causing the compile error
handler.handle(myStuff);
verify(sender).send(thingArgumentCaptor.capture());
assertThat(thingArgumentCaptor.getValue()).isEqualTo(thing);
}
}
Class under test:
public class Handler {
private final Converter converter;
private final Sender sender;
public Handler(Converter converter, Sender sender) {
this.converter = converter;
this.sender = sender;
}
public void handle(MyStuff myStuff) {
Thing thing;
switch (myStuff.getType()) {
case A:
thing = converter.convert((StuffA) myStuff);
break;
case B:
thing = converter.convert((StuffB) myStuff);
break;
default:
throw new RuntimeException();
}
sender.send(thing);
}
}
Domain object:
public abstract class MyStuff {
public abstract Type getType();
}
Converter:
public interface Converter {
Thing convert(StuffA myType);
Thing convert(StuffB myType);
}
Sender:
public interface Sender {
void send(Thing thing);
}
Type:
public enum Type {
A, B
}
Is there any way to solve this without writing two separate test methods?
You can use reflection to call the appropriate method. Try replacing the line with the compilation error with the following two lines:
Method method = Converter.class.getMethod("convert", classByType.get(type));
when(method.invoke(converter, eq(myStuff))).thenReturn(thing);
If the type of the method parameter isn't known at compile time, then reflection is the only way to go.
Related
I am trying to implement a singleton pattern with a caching feature. At first MySingleton was only a POJO and things were simple enough, but then I needed to add a new feature, which also required autowiring a bean. (MyComponent is really an interface to a data repository)
I put the #Component annotation on MySingleton to trigger the autowiring (even though it is always called a static way) and created a private constructor to pass the MyComponent reference to an object created by new. This code seems to work, although I do not fully understand why.
My question: I feel like I'm doing it wrong, but am I?
(would you approve this pull request to your code base?)
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
public class MySingletonTest {
#Test
public void test() {
assertNotNull(MySingleton.getInstance().getMyComponent());
}
}
// ----------------------------------------------------------------------------- //
import java.util.Calendar;
import java.util.concurrent.atomic.AtomicReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class MySingleton {
private static final long CACHE_TIMEOUT = 60 * 60 * 1000; // 1 hour
private static final AtomicReference<MySingleton> INSTANCE = new AtomicReference<MySingleton>();
private final Calendar timestamp; // NOTE: this is NOT static!
#Autowired
private static MyComponent myComponent;
private MySingleton(MyComponent myComponent) {
this.timestamp = Calendar.getInstance();
MySingleton.myComponent = myComponent; // I do not understand why this line is needed
}
private boolean isTimeout() {
return Calendar.getInstance().getTimeInMillis() - timestamp.getTimeInMillis() > CACHE_TIMEOUT;
}
public static synchronized MySingleton getInstance() {
if ( INSTANCE.get() == null || INSTANCE.get().isTimeout() ) {
INSTANCE.set(new MySingleton(myComponent));
}
return INSTANCE.get();
}
public MyComponent getMyComponent() {
return myComponent;
}
}
// ----------------------------------------------------------------------------- //
import org.springframework.stereotype.Component;
#Component
public class MyComponent {
}
I have a ServiceWebClientInterface.java like this
import reactor.core.publisher.Mono;
public interface ServiceWebClientInterface {
Mono<String> apiCall();
}
MyClass.java
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
public class MyClass extends AbstractGatewayFilterFactory<MyClass.Config> {
private final ServiceWebClientInterface serviceWebClientInterface;
MyClass(final ServiceWebClientInterface serviceWebClientInterface) {
this.serviceWebClientInterface = serviceWebClientInterface;
}
#Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
return serviceWebClientInterface.apiCall().flatMap(response -> {
if (!"Valid".equals(response)) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
});
};
}
public static class Config {
// Put the configuration properties
}
}
I'm trying to unit test myMethod using StepVerifier, but I am not able to execute statements inside the inner lambda function of myMethod.
MyClassTest.java
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
class MyClassTest {
#Mock
ServiceWebClientInterface mockServiceWebClientInterface;
#Mock
private ServerWebExchange mockServerWebExchange;
#Mock
private GatewayFilterChain mockGatewayFilterChain;
#Mock
private ServerHttpResponse mockServerHttpResponse;
#BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
void test_apply_forValid() {
when(mockServiceWebClientInterface.apiCall()).thenReturn(Mono.just("Valid"));
MyClass.Config config = new MyClass.Config();
MyClass myClass = new MyClass(mockServiceWebClientInterface);
GatewayFilter gatewayFilter = myClass.apply(config);
Mono<Void> response = gatewayFilter.filter(mockServerWebExchange, mockGatewayFilterChain);
StepVerifier.create(response).expectComplete();
verify(mockServiceWebClientInterface).apiCall();
verify(mockGatewayFilterChain).filter(mockServerWebExchange);
}
#Test
void test_apply_forInValid() {
when(mockServiceWebClientInterface.apiCall()).thenReturn(Mono.just("InValid"));
when(mockServerWebExchange.getResponse()).thenReturn(mockServerHttpResponse);
MyClass.Config config = new MyClass.Config();
MyClass myClass = new MyClass(mockServiceWebClientInterface);
GatewayFilter gatewayFilter = myClass.apply(config);
Mono<Void> response = gatewayFilter.filter(mockServerWebExchange, mockGatewayFilterChain);
StepVerifier.create(response).expectComplete();
verify(mockServiceWebClientInterface).apiCall();
verify(mockServerHttpResponse).setStatusCode(eq(HttpStatus.FORBIDDEN));
verify(mockServerHttpResponse).setComplete();
verify(mockGatewayFilterChain, never()).filter(mockServerWebExchange);
}
}
Please find the complete code above, When I run the tests I observe that the inner lambda function does not get invoked using the step verifier.
I guess you want to test the class that implements MyLambda interface.
For sure you inject there serviceWebClientInterface as mentioned on code snippet.
To unit test that class, you should mock the serviceWebClientInterface.apiCall() and verify if it was called. As an addition to your actual code snippet.
You can use Mockito library for that purpose.
create a mock:
given(serviceWebClientInterface).willReturn(Mono.just("some text"));
then verify if it is called:
verify(serviceWebClientInterface).apiCall()
I was able to fix this issue by using
StepVerifier.create(response).verifyComplete();
and mocking chain.filter(exchange);
I read at least 20 posts but still couldn't find the answer. So, posting this question. May be it is answered in some other post, which I couldn't find.
class OuterService {
InnerService innerService;
#Autowired
public void setInnerService(InnerService innerService){
this.innerService = innerService;
}
public void method() {
List<C> listOfC = new ArrayList<C>();
C c = new C();
c.setUserProfiles(someObject);
c = innerService.invokeMethod(String str1,Map map,
String str2, Object obj1, String str3, String str4,
C c, String str5);
c.setCreatedDate(some String value); // Here c comes null while executing jUnits.
listOfC.add(c);
}
}
Here is my Test class:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import com.pogo.service.DeviceRegistrationService;
#SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT")
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
class ClassOuterServiceTest {
#InjectMocks
OuterService outerService;
#Mock
InnerService innerService;
#Mock C c;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
outerService.setInnerService(innerService);
}
#Test
public void methodTest() {
when(innerService.invokeMethod(Mockito.anyString(),
Mockito.any(Map.class), Mockito.anyString(),Mockito.anyString(),
Mockito.any(PersonSessionToken.class), Mockito.anyString(),Mockito.anyString(), Mockito.anyString(),
Mockito.any(RequestHeader.class),Mockito.any(C.class),
Mockito.anyString() )).thenReturn(c);
doNothing().when(c).invokeCMethod();
outerService.method();
}
}
But I get null inside object c in OuterService.java. Also if I use Matchers.any() or Matchers.anyString() in invokeMethod() then , it shows Matchers exception.
What is the appropriate solution?
You don't need to create the object for OuterService use #InjectMocks annotation and when you use method stubbing use mock objects only. In your program you are creating object for c. Instead of creating object just use #Mock annotation.
When you using Mockito.any() mention the class inside parenthesis. Mockito.any(C.class) like this.
Don't use PowerMockRunner unless you are testing static or final classes.
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
public class ClassOuterServiceTestC {
#Mock
public InnerService innerService;
#InjectMocks
public OuterService outerService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void methodTest() {
C obj = mock(C.class);
when(innerService.invokeMethod(anyString(), anyMap(), anyString(), any(), anyString(), anyString(), any(TestC.class), anyString()))
.thenReturn(obj);
doNothing().when(obj).setUserProfiles(any(Object.class));
doNothing().when(obj).setCreatedDate(anyString());
outerService.method();
}
}
I want to test the following example code:
public class Example {
...
public void doStuff() {
...
Lift lift = new Lift();
lift.call(5);
...
}
...
}
How can I 'intercept' lift.call(5)?
Generally I would use when(lift.call(anyInt()).thenReturn(...), but I have no reference to the Lift object.
You can't do it with mockito alone. The cleanest solution is to refactor your code so you can have access to it. However if that's not an option then "power mockito" is what you want. Grab "powermock-api-mockito"+"powermock-module-junit4" and then something like this will do the trick:
import static org.mockito.Mockito.verify;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(Example.class)
public class ExampleTest {
private Example testSubject;
#Mock
private Lift lift;
#Test
public void testDoStuff() throws Exception {
testSubject.doStuff();
verify(lift).call(5);
}
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.whenNew(Lift.class).withNoArguments().thenReturn(lift);
testSubject = new Example();
}
}
Can you modify the Example class? If yes, the simplest way would be to extract the Lift dependency and provide it via constructor. Like this:
public class Example {
private final Lift lift;
public Example(Lift lift) {
this.lift = lift;
}
public void doStuff() {
this.lift.call(5);
}
}
Then you can stub lift as you want since now you have access to the instance.
I'm wondering how should I initialize fields in Spring Beans? Here is several possible solutions:
1. Initialize fields directly on declaration
import org.springframework.stereotype.Component;
#Component
public class DeclarationInit {
private final int field = Integer.MAX_VALUE;
public int getField() {
return field;
}
}
2. Initialize fields using #Value annotation
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class ValueInit {
#Value("#{T(Integer).MAX_VALUE}")
private int field;
public int getField() {
return field;
}
}
3. Initialize fields using #Autowired annotation
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class AutowiredInit {
private int field;
#Autowired
private void initField() {
field = Integer.MAX_VALUE;
}
public int getField() {
return field;
}
}
4. Initialize fields using #PostConstruct annotation
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
#Component
public class PostConstructInit {
private int field;
#PostConstruct
private void initField() {
field = Integer.MAX_VALUE;
}
public int getField() {
return field;
}
}
All tests succeeds and do not show any difference:
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = SomeTestContextConfiguration.class)
public class FieldInitTest {
#Autowired
private DeclarationInit declarationInit;
#Autowired
private ValueInit valueInit;
#Autowired
private AutowiredInit autowiredInit;
#Autowired
private PostConstructInit postConstructInit;
#Test
public void shouldInitializeFieldOnDeclaration() {
assertThat(declarationInit.getField(), equalTo(Integer.MAX_VALUE));
}
#Test
public void shouldInitializeFieldWithValueAnnotation() {
assertThat(valueInit.getField(), equalTo(Integer.MAX_VALUE));
}
#Test
public void shouldInitializeFieldWithAutowiredSetter() {
assertThat(autowiredInit.getField(), equalTo(Integer.MAX_VALUE));
}
#Test
public void shouldInitializeFieldWithPostConstruct() {
assertThat(postConstructInit.getField(), equalTo(Integer.MAX_VALUE));
}
}
Are this declarations equal to each other or should I use only one of them or neither of them?
Assuming the value is a constant, the first option is the simplest to understand and works without Spring, simplifying unit testing.
The second and fourth option are more complex and introduce an unnecessary dependency on the Spring container without any benefit. The third option is outright bizarre, since you're using #Autowired and not performing dependency injection.
I believe spring offers all those options because you might run into different requirements...
If you want MAX_INT and there's no way on earth anyone needs to initialize it differently, then it's enough to declare int field = Integer.MAX_INT regardless of Spring.
If you do want to allow other initial configurations, then you can initialize it using #Autowired, or through a constructor arg, or setter/getter... it's a matter of taste.
#PostConstruct is more suitable for complex situations, e.g. if your field needs to be calculated based on other injected fields.