Is it possible to inject a spy for two dependencies into the tested class?
I have this class:
#Service
#Singleton
#MessageReceiver
public class LinkHitCallbackEventHandler {
public static final Logger logger = LogManager.getLogger();
#Inject
private CallbackInvocationBuilder callbackInvocationBuilder;
#Inject
private CallbackLogBuilder callbackLogBuilder;
#MessageReceiver
public void handle(#SubscribeTo LinkHitEvent event) {
Entity<Form> entity = EntityFormFactory.createFromEvent(event);
this.callbackLogBuilder.build(entity);
Response response = this.callbackInvocationBuilder.post(entity);
}
}
Importantly, it's a simple class with two injected dependencies: callbackInvocationBuilder and callbackLogBuilder.
I test calls to these dependencies in the following way:
#Test
public void send_callback_after_event_is_published() {
target("/testY")
.property("jersey.config.client.followRedirects", false)
.request()
.header("User-Agent", UserAgentMother.allowedUserAagent())
.get();
verify(callbackInvocationBuilder).post(anyObject());
// verify(callbackLogBuilder).build(anyObject());
}
This arrangement the test passes, because I first call callbackLogBuilder.build(entity).
If I swap calls and call them first callbackInvocationBuilder.post(entity), the test will fail.
Both callbackLogBuilder and callbackInvocationBuilder are spies. I configure them in configure () from JerseyTest. In exactly the same way.
One of the method to inject spy to use whitebox and use verify
like this:
Whitebox.setInternalState(ClassToTest.class, "Object", spy1);
, So the test method ill be like this:
#Test
public void send_callback_after_event_is_published() {
Whitebox.setInternalState(LinkHitCallbackEventHandler.class, "callbackInvocationBuilder", spy1);
Whitebox.setInternalState(LinkHitCallbackEventHandler.class, "callbackInvocationBuilder", spy2);
target("/testY")
.property("jersey.config.client.followRedirects", false)
.request()
.header("User-Agent", UserAgentMother.allowedUserAagent())
.get();
verify(callbackInvocationBuilder).post(anyObject());
verify(callbackLogBuilder).build(anyObject());
}
, If you want to capture the argument
ArgumentCaptor<Entity> captor = ArgumentCaptor.forClass(Entity.class);
verify(callbackInvocationBuilder, times(1)).post(captor.capture());
Entity actualEntity = captor.getValue();
assertEquals(expected, actualEntity);
Related
I'm trying to create a unittest for the method below (myHostClient), but I'm having some problems with it:
MyClass.java
import com.abc.def.ServiceBuilder
public class MyClass {
#Value("${materialLocation}")
private String materialValue
private static final SERVICEBUILDER = new ServiceBuilder()
#Bean public MyHostServiceClient myHostClient(
#Value(${qualifier_one}) final String qualiferOne,
#Value(${use_service}) final boolean useService) {
if(useService) {
return SERVICEBUILDER
.remote(MyHostServiceClient.class)
.withConfig(qualifierOne)
.withCall(new CallerAttach(Caller.retro(defaultStrategy())), // Error Line2 Here
new SigningVisitor(new CredentialsProvider(materialValue))),
call -> call.doSomeStuff(StuffObject.getStuffInstance()))
.makeClient();
}
#Bean DefaultStrategy<Object> defaultStrategy() {
final int valueA = 1;
final int valueB = 2;
return new DoSomeThingsBuilder()
.retry(valueA)
.doSomethingElse(valueB)
.create();
}
}
And here is my latest unsuccessful attempt at writing a unittest for it:
MyClassTest.java
import org.mockito.Mock
import static org.mockito.Mockito.times
public class MyClassTest {
#Mock
private SERVICEBUILDER serviceBuilder;
private MyClass myClass;
private String qualifierOne = "pass"
#BeforeEach
void setUp() {
myClass = new MyClass();
}
#Test
public void test_myHostClient() {
boolean useService = true;
final MyHostServiceClient result = myclass.myHostClient(qualifierOne, useService); // Error Line1 here
verify(serviceBuilder, times(1));
}
}
I have been trying to mock SERVICEBUILDER and verify that the mocked object is called one time but no luck so far. Right now I'm getting this error:
IllegalArgumentException: Material Name cannot be null
And it points to these lines in my code.
In the Test:
final MyHostServiceClient result = myclass.myHostClient(qualifierOne, useService);
Which points to this line in the module:
.withCall(new CallerAttach(Caller.retro(defaultStrategy())),
Anyone know how I can fix my unittest or write a working one from scratch?
I would say the design of MyClass is quite wrong because it looks like a Spring configuration but apparently it's not. If it is really supposed to be a configuration then I wouldn't even test it like this because it would rather be an integration test. Of course, even in integration tests you can mock dependencies. But the test itself would run differently and you would have to spin up a suitable Spring context, etc.
So given the above, I would rather make MyClass some sort of MyHostServiceClientFactory with removing all of the Spring annotations and then fix the following problems in your code.
SERVICEBUILDER is hardcoded.
SERVICEBUILDER is static final and its value is hardcoded into MyClass. You will not be able to reassign that field with the mocked version. It can still be final but not static then and it's better to use dependency injection here by passing the value through the MyClass constructor.
SERVICEBUILDER will still be not mocked even if you fix the above.
To really mock SERVICEBUILDER by using the #Mock annotation in the test you should enable Mockito annotations.
If you are using JUnit5 then you should annotate your test class like this:
#ExtendWith(MockitoExtension.class)
public class MyClassTest {
...
}
If you are stuck with JUnit4 then you should use another combination:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
...
}
Once you've done that the SERVICEBUILDER will be mocked but now you will have to configure the behaviour of that mock, like what is going to be returned by the SERVICEBUILDER methods. I can see 4 methods in total, namely remote, withConfig, withCall, and makeClient. You will have to do Mockito's when/thenReturn configurations.
MyClass.materialValue is null.
But even when your mock will be properly configured you will still encounter the original IllegalArgumentException: Material Name cannot be null. This is because MyClass.materialValue will still be null and looks like CredentialsProvider cannot accept that. As I can see, that field is supposed to be injected by Spring using the #Value annotation, but remember this class no longer contains anything from Spring. As in problem 1, you have to pass the value through the MyClass constructor.
Once all of these problems are solved you can introduce a thin Spring configuration like MyHostServiceClientConfiguration (or whatever name suits you) that would serve as a provider of necessary properties/dependencies for MyHostServiceClientFactory (existing MyClass) and then this factory can provide you with a MyHostServiceClient bean through a method like MyHostServiceClientConfiguration#myHostServiceClient annotated with #Bean.
Conceptually your MyHostServiceClientFactory will look like this:
public class MyHostServiceClientFactory {
private final String materialValue;
private final ServiceBuilder serviceBuilder;
public MyHostServiceClientFactory(String materialValue, ServiceBuilder serviceBuilder) {
this.materialValue = materialValue;
this.serviceBuilder = serviceBuilder;
}
public MyHostServiceClient myHostClient(String qualiferOne, boolean useService) {
if(useService) {
return serviceBuilder
.remote(MyHostServiceClient.class)
.withConfig(qualifierOne)
.withCall(new CallerAttach(Caller.retro(defaultStrategy())), // Error Line2 Here
new SigningVisitor(new CredentialsProvider(materialValue))),
call -> call.doSomeStuff(StuffObject.getStuffInstance()))
.makeClient();
}
// can also be injected as a dependency rather than being hardcoded
DefaultStrategy<Object> defaultStrategy() {
final int valueA = 1;
final int valueB = 2;
return new DoSomeThingsBuilder()
.retry(valueA)
.doSomethingElse(valueB)
.create();
}
}
#Autowired
private Publisher Publisher;
private int Id = 12345;
private BClient bClient = new BClient(Id);
private Map<Integer, Boolean> defaultBooleanValueMap;
private LoadCache<Integer, Boolean> booleanCache = CacheBuilder.newBuilder()
.refreshAfterWrite(refreshRate, TimeUnit.SECONDS)
.build(
new CacheLoader<Integer, Boolean>() {
#Override
public Boolean load(Integer id) throws Exception {
return fetchBooleanValue(id);
}
}
);
private boolean fetchBooleanValue(int id) {
long fetchStart = System.currentTimeMillis();
boolean val = bClient.getBoolean(id, defaultBooleanValueMap.get(id));
publisher.publish(
publisher.getDistributionMetric("fetchtime.bool", System.currentTimeMillis() - fetchStart));
return val;
}
public boolean getBoolean(int id) {
return booleanCache.getUnchecked(id);
}
//Trying to test getBoolean(int id) function. I'm mocking bClient, Publisher. Not sure how to properly test it
// Could anyone help me understand how to test it
//testing with
SomeClass someClass = new SomeClass();
#Mock
Publisher publisher;
#Mock
BClient bClient;
#Test
public void testGetBoolean(){
bClient = new BClient(12345);
Map<Integer,Boolean> defaultBooleanValueMap = null;
defaultBooleanValueMap.put(123, false);
when(bClient.getBoolean(123,
defaultBooleanBregValueMap.get(123))).thenReturn(false);
boolean b = someClass.getBoolean(123);
assertFalse(b);
}
// i'm don't know if i'm doing it right
Are you using Mockito?
It's good practice to not start a field name with a capital (Publisher for instance)
Personally i think it will be better to make all these methods protected instead of private, so that you can test each of them separately.
however this would be an example of a unit test for your code.
You can use Mockito to check if certain method calls are fired the amount of time you expect them to be fired.
I did not include all but you can just add if you need more tests.
Further i recommend to read about Mockito as it has some really powerful unit test tools
#Test
public void testGetBoolean () {
xxx.getBoolean
//the following line can only be done if you spy your service
Mockito.verify(xxx, times(1)).fetchBooleanValue(any());
//this line can be done if you mock bClient
Mockito.verify(bClient , times(1)).getBoolean(any(), any()); //Mockito.any() or you can fill in the real values if you really want.
//this line can be done if you mock Publisher
Mockito.verify(publisher, times(1)).publish(any); //again any or the real value you want to pass
}
I just now saw your unit tests, you can inject the mocks in you class with the following anotatation:
#InjectMocks
SomeClass someClass;
when mocking a class you don't manually have to create it again.
You don't have to mock the Bclient as you already create it with "new Bclient" instead of autowiring it.
I feel the #InjectMocks is not working because you didn't tell Spring that your class is a service component.
#Service
public class SomeClass {
//insert code here
}
This is the service I have :
#Service
public class UserInfoService {
#Autowired
private UserInfoServiceClient UserInfoServiceClient; // Call another Rest API
public ResponseEntity<ResponseUserInfoData> sendUserInfo(String UserId) throws RuntimeException {
ResponseUserInfoData responseUserInfoData = new ResponseUserInfoData();
//Get the body from the User service client
UserServiceDTO UserServiceDTO = UserInfoServiceClient.sendResponse(UserId).getBody();
//Set the values of responseUserInfoData
Optional<UserServiceDTO> UserServiceDTOOptional = Optional.ofNullable(UserServiceDTO);
if (UserServiceDTOOptional.isPresent()) {
UserServiceDTOOptional.map(UserServiceDTO::getId).ifPresent(responseUserInfoData::setid);
}
else return ResponseEntity.noContent().build();
}
}
I have to test it. I'm new to JUnit testing. I want to test the following points:
To check if the service return the response entity
To check if the get and set method works
This is what I started?
#RunWith(MockitoJUnitRunner.class)
public class ServiceTests {
#InjectMocks
private UserInfoService UserInfoService;
#Mock
private UserInfoServiceClient UserInfoServiceClient;
#Mock
private UserServiceDTO UserServiceDTO;
#Test
public void shouldReturnUserInfoData() throws IOException{
UserInfoService.sendUserInfo("ABC");
}
}
Any help is appreciated?
Mockito is useful to mock the dependencies of the service so that you can test all the code path in you service. In your case you will want to stub the call to serInfoServiceClient.sendResponse(UserId) and have it return a specific UserServiceDTO for each test case.
The test file looks like it is set up correctly, you only need to mock the method to give you the result you need for the particular test, for example
#RunWith(MockitoJUnitRunner.class)
public class ServiceTests {
#InjectMocks
private UserInfoService UserInfoService;
#Mock
private UserInfoServiceClient UserInfoServiceClient;
#Test
public void shouldReturnUserInfoData() throws IOException{
final String userId = "123";
// The mocked return value should be set up per test scenario
final UserServiceDto dto = new UserServiceDto();
final ResponseEntity<UserServiceDTO> mockedResp = new ResponseEntity<>(dto, HttpStatus.OK);
// set up the mock service to return your response
when(UserInfoServiceClient.sendResponse(userId)).thenReturn(mockedResp);
// Call your service
ResponseEntity<ResponseUserInfoData> resp = UserInfoService.sendUserInfo(userId);
// Test the result
Assert.isNotNull(resp);
}
}
There are also other ways to mock the dependencies using Mockito. I suggest going through the quick start of https://site.mockito.org/
I have a parent workflow (ParentWorkflow) calling a child workflow (ChildWorkflow) and I'm trying to test out the call.
The parent code looks something like this:
public class ParentWorkflow {
private final ChildWorkflowClientFactory childWorkflowClientFactory =
new ChildWorkflowClientFactoryImpl();
public void runWorkflow() {
new TryCatch() {
#Override
protected void doTry() throws Throwable {
Promise<Void> workflowFinished = childWorkflowClient.childWorkflow(x);
...
}
...
}
}
I want to mock out the
childWorkflowClient.childWorkflow(x)
call, however when I am hooking up the unit test I don't appear to have the option to inject the client factory, the unit test code looks like this:
#Rule
public WorkflowTest workflowTest = new WorkflowTest();
#Mock
private Activities mockActivities;
private ParentWorkflowClientFactory workflowFactory
= new ParentWorkflowClientFactoryImpl();
#Before
public void setUp() throws Exception {
// set up mocks
initMocks(this);
workflowTest.addActivitiesImplementation(mockActivities);
workflowTest.addWorkflowImplementationType(ParentWorkflowImpl.class);
workflowTest.addWorkflowImplementationType(ChildWorkflowImpl.class);
I don't appear to be able to pass anything into the workflow implementation classes, is there another way I can mock the child workflow out?
You can test workflow code directly mocking its dependencies without using workflowTest:
/**
* Rule is still needed to initialize asynchronous framework.
*/
#Rule
public WorkflowTest workflowTest = new WorkflowTest();
#Mock
private ActivitiesClient mockActivities;
#Mock
private BWorkflowClientFactory workflowFactory;
#Before
public void setUp() throws Exception {
// set up mocks
initMocks(this);
}
#Test
public void myTest() {
AWorkflowImpl w = new AWorkflowImpl(workflowFactory);
w.execute(); // whatever execute method of the workflow
}
This approach allows testing parts of the workflow encapsulated in other objects instead of the entire workflow.
If for whatever reason (for example you are using other testing framework than JUnit) you don't want to rely on WorkflowTest #Rule asynchronous code can be always executed using AsyncScope:
#Test
public void asyncTest() {
AsyncScope scope = new AsyncScope() {
protected void doAsync() {
// Any asynchronous code
AWorkflowImpl w = new AWorkflowImpl(workflowFactory);
w.execute(); // whatever execute method of the workflow
}
};
scope.eventLoop();
}
EDIT: The below only applies to SpringWorkflowTest; WorkflowTest doesn't have addWorkflowImplementation for some reason.
The correct way to use the WorkflowTest would be to add a mock implementation for the child workflow rather than adding the actual type:
#Rule
public SpringWorkflowTest workflowTest = new SpringWorkflowTest();
#Mock
private Activities mockActivities;
#Mock
private ChildWorkflow childWorkflowMock;
private ParentWorkflowClientFactory workflowFactory
= new ParentWorkflowClientFactoryImpl();
#Before
public void setUp() throws Exception {
// set up mocks
initMocks(this);
workflowTest.addActivitiesImplementation(mockActivities);
workflowTest.addWorkflowImplementationType(ParentWorkflowImpl.class);
workflowTest.addWorkflowImplementation(childWorkflowMock);
...
}
The framework will then call this mock instead of the actual implementation when you use the factory.
I have an interface, ex:
interface MyService {
void createObj(int id)
void createObjects()
}
I want to test an implementation of the createObjects method, which has body like:
void createObjects() {
...
for (...) {
createObj(someId);
}
}
I have already tested createObj(id):
#Test public void testCreate() {
//given
int id = 123;
DAO mock = mock(DAO.class);
MyService service = new MyServiceImpl(mock);
//when
service.createObj(id);
//verify
verify(mock).create(eq(id));
}
So I don't want to repeat all test cases for it in the test for createObjects.
How can I make sure that another method of the real object was called besides the one I am testing?
Use a spy:
MyService myService = new MyServiceImpl()
MyService spy = spy(myService);
doNothing().when(spy).createObj(anyInt());
// now call spy.createObjects() and verify that spy.createObj() has been called
This is described, like everything else, in the api doc.