Mocking a class assigns nulls to #NonNull fields(Android) - java

I'm using Android SDK and junit4 + Mockito for unit testing. Say I have a class like this in my app:
public class Container{
#NonNull private Set<String> values = new HashSet<>();
public void addValue(String value) {
values.add(value);
}
#NonNull
public Set<String> getValues() {
return values;
}
}
And I also have a unit test with Mockito that looks like this:
public class ContainerTest {
private Container container;
#Before
public void before() {
container = mock(Container.class);
}
#Test
public void shouldAddValue() {
container.add("test_value");
assertTrue(container.getValues.contains("test_value"));
}
}
This test actually fails on line "container.add("test_value");" because mock(Container.class) creates a class in which values field is actually set to null, so values.add(value) in addValue() method throws an NPE. I could add a null check in addValue() to fix this, but that seems absurd, since values are already declared non null.
Is there any way to make Mockito respect #NonNull annotations and initialize the field properly?

I think you aren't using Mockito in the right way, since you have to define the Mockito behaviors.
For instance, you should have something like this:
#Test
public void shouldAddValue() {
Set<String> mySet = new HashSet<String>();
mySet.put("test_value");
// Mock container getValues() method to return mySet
when(container.getValues()).thenReturn(mySet); // do import static for Mockito.when
assertTrue(container.getValues().contains("test_value"));
}
Mockito works pretty well when you mock responses, but what you want is to let Mockito to initialize classes for you which clearly it isn't Mockito goals.
Therefore, if you want to test your Container object, then you don't have to mock Container itself and you can have something like this:
public class ContainerTest {
private Container container;
#Before
public void before() {
container = new Container(); // Initialize container
}
#Test
public void shouldAddValue() {
container.addValue("test_value");
assertTrue(container.getValues().contains("test_value"));
}
}

Related

Mocking a ListState in Apache Flink 1.4

I am writing some test code for a processElement function in Apache Flink 1.4:
public class ProcessFunctionClass {
public void processElement(Tuple2<String, String> tuple2, Context context, Collector<Tuple2<String, String>> collector) {
// if the state is empty, start a timer
if (listState.get().iterator().hasNext() == false)
context.timerService().registerEventTimeTimer(1000);
listState.add("someStringToBeStored");
// ...
}
}
public class ProcessFunctionClassTest {
private ProcessFunctionClass processFunctionClass;
#Mock
private ListState<String> listState;
#Before
public void setUp() throws Exception {
processFunctionClass = new ProcessFunctionClass();
}
#Test
public void testProcessElement() {
ListState mockListState = mock(ListState.class);
Iterable mockIterable = mock(Iterable.class);
Iterator mockIterator = mock(Iterator.class);
MockitoAnnotations.initMocks(this);
when(tDPListState.get()).thenReturn(mockIterable);
when(tDPListState.get().iterator()).thenReturn(mockIterator);
when(tDPListState.get().iterator().hasNext()).thenReturn(false);
processFunctionClass.processElement(tuple2, context, collector);
// verify(...)
}
}
When I debug using my IDE, just before I step into the processElement() method, listState is not null and appears to have been mocked successfully, but as soon as I get to listState.get().iterator().hasNext(), listState is null and I get a NullPointerException. What am I doing wrong here?
In ProcessFunctionClass you have a private listState variable.
In your test you create a completely unrelated mockListState variable and set some expectations on it.
For your test to work, you must provide a way (constructor or setter) to set ProcessFunctionClass.listState to desired value (your mocked list state)
On top of that, MockitoAnnotations.initMocks(this); seems to do nothing in your example: you haven't shown us any fields annotated with #Mock or #InjectMocks
Update
You are misusing #Mock annotation.
You should place it in the test class, not in class under test.
When placed in the test class, after a call to initMocks, the filed will be initialized with a mock of an appropriate type.
What you should fo instead:
remove MockitoAnnotations.initMocks(this);, you are creating all the mocks manually.
add a constructor in ProcessFunctionClass
public ProcessFunctionClass(ListState<String> listState) {
this.listState = listState
}
use this constructor in your test
var mockListState = mock(ListState.class);
var processFunctionClass = new ProcessFunctionClass();

Set system property in jmockit unit test

Using a combination of testng and jmockit to do some unit testing. In a method I am testing, it tries to access a system property that I set using a JBoss deploy script, therefore my unit test doesn't have access to the entire JBoss environment to access the properties, so it returns null when testing that method. Tried mocking and setting the system variable directly in my test, but the system property is still returning null in the class that I am testing.
The class being tested:
//returns the correct value in the application but returns null for the test
public static final String webdavrootDir = System.getProperty("property.name");
public String getFileUrl(){
StringBuilder sb = new StringBuilder();
return sb.append(webdavrootDir)
.append(intervalDir)
.append(fileName)
.toString();
}
The test:
#Test
public void getUrl(#Mocked System system){
system.setProperty("property.name", "https://justatest.com/dav/bulk");
String fileUrl = csvConfig.getFileUrl();
assertEquals(fileUrl, "https://justatest.com/dav/bulk/otherstuff");
}
The test finds the value null/otherstuff but expects https://justatest.com/dav/bulk/otherstuff
I have also tried setting the system property in a testng #BeforeMethod method without any success.
Make sure you call System.setProperty("property.name", "https://justatest.com/dav/bulk"); before the class being tested is instantiated, otherwise the static field will always be null.
Consider using a #BeforeClass setup method for this:
#BeforeClass
public static void setup() {
System.setProperty("property.name", "https://justatest.com/dav/bulk");
// Instantiate your CsvConfig instance here if applicable.
}
and then
#Test
public void getUrl(){
System.setProperty("property.name", "https://justatest.com/dav/bulk");
String fileUrl = csvConfig.getFileUrl();
assertEquals(fileUrl, "https://justatest.com/dav/bulk/otherstuff");
}
If you want to use jMocki, but you have to make sure it is done, before your class-under-test is beeing loaded (because of the static field).
#BeforeClass
public static void fakeSystemProperty() {
new MockUp<System>() {
#Mock
public String getProperty(String key) {
return "https://justatest.com/dav/bulk";
}
};
}
Another way would be to modify the class-under-test and partially mock this one, e.g.:
public class CsvConfig {
private static final String webdavrootDir = System.getProperty("property.name");
public static String getProperty() {
return webdavrootDir;
}
}
The test:
#BeforeClass
public static void fakeSystemProperty() {
new MockUp<CsvConfig>() {
#Mock
public String getProperty() {
return "https://justatest.com/dav/bulk";
}
};
}

How to provide object inside public method to access another method within it using PowerMock?

I am writing unit test case for a Class
public class CurrentMoreInfoDataProvider implements CurrentMoreInfoInterface.presenterToModel{
private CurrentMoreInfoInterface.modelToPresenter modelToPresenter;
public CurrentMoreInfoDataProvider(CurrentMoreInfoInterface.modelToPresenter modelToPresenter) {
this.modelToPresenter = modelToPresenter;
}
#Override
public void provideData() {
WeatherApiResponsePojo apiWeatherData = WeatherDataSingleton.getInstance().getApiWeatherData();
if(null != apiWeatherData.getCurrently()){
CurrentlyPojo currently = apiWeatherData.getCurrently();
if(null != currently){
populateWeatherData(currently);
}
}
}
public void populateWeatherData(CurrentlyPojo currently) {....}
I want to just use verify method of power mock to test whether populateWeatherData get executed or not. Below is my test case so far.
#RunWith(PowerMockRunner.class)
#PrepareForTest(CurrentMoreInfoDataProvider.class)
public class TestCurrentMoreInfoDataProvider {
private CurrentMoreInfoDataProvider dataProvider;
#Mock
CurrentMoreInfoInterface.modelToPresenter modelToPresenter;
private CurrentlyPojo currentlyPojo = new CurrentlyPojo();
#Test
public void testPopulateWeatherData(){
dataProvider = PowerMockito.spy(new CurrentMoreInfoDataProvider(modelToPresenter));
dataProvider.provideData();
Mockito.verify(dataProvider).populateWeatherData(currentlyPojo);
}
}
If I run this I get null pointer exception in provideData method at
if(null != apiWeatherData.getCurrently()){
How should I provide apiWeatherData to provideData method in that class?
You have to mock WeatherDataSingleton.getInstance().getApiWeatherData() too.
This would be much easier if you would not use static access in general and the Singelton pattern in particular.
I tried mocking it, but how should i provide that mock object to provideData() ?
create a mock of WeatherDataSingleton.
Configure your Test so that this mock is used (by properly using dependency injection or by surrendering to your bad design using Powermock).
configure the mock to return the data:
doReturn(currentlyPojo).when(weatherDataSingletonMock).getApiWeatherData();
This resolves the NPE.
I dont think you need to go for PowerMockito if you apply a simple refactor to your production code:
public class CurrentMoreInfoDataProvider{
#Override
public void provideData() {
WeatherApiResponsePojo apiWeatherData = getApiWeatherData();
if(null != apiWeatherData.getCurrently()){
CurrentlyPojo currently = apiWeatherData.getCurrently();
if(null != currently){
populateWeatherData(currently);
}
}
}
WeatherApiResponsePojo getApiWeatherData(){
return WeatherDataSingleton.getInstance().getApiWeatherData();
}
then in your test expect that new method to return certain object:
#RunWith(MockitoJUnitRunner.class)
public class TestCurrentMoreInfoDataProvider {
private CurrentMoreInfoDataProvider dataProvider;
#Mock
CurrentMoreInfoInterface.modelToPresenter modelToPresenter;
#Mock
WeatherApiResponsePojo apiWeatherDataMock;
private CurrentlyPojo currentlyPojo = new CurrentlyPojo();
#Test
public void testPopulateWeatherData(){
dataProvider = PowerMockito.spy(new CurrentMoreInfoDataProvider(modelToPresenter));
doReturn(apiWeatherDataMock).when(dataProvider).getApiWeatherData();
dataProvider.provideData();
Mockito.verify(dataProvider).populateWeatherData(currentlyPojo);
}
}

Unit testing Amazon SWF child workflows

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.

How to improve mockito default behaviour for Guava Optional?

I started to use Guava Optional as a part of the null object pattern and would like to improve the use in Mockito, where null is the default return value for mocked objects. To behave correctly one needs to explicitly tell Mockito to use Optional.absent() instead:
import org.mockito.*;
import org.testng.*;
import org.testng.annotations.*;
import com.google.common.base.Optional;
public class Example {
#Mock
private MyObject underTest;
#Test
public void testExample() {
// fails
// assertNotNull(underTest.get());
Mockito.when(underTest.get()).thenReturn(Optional.absent());
Assert.assertNotNull(underTest.get());
}
public class MyObject {
public Optional<Object> get() {
return Optional.absent();
}
}
#BeforeClass
public void beforeClass() {
MockitoAnnotations.initMocks(this);
}
}
Is there a easy way to improve mockito to automatically return Optional.absent() instead of null if the actual type is Optional?
I tried to solve it with reflection in #Before annotated method, however, I didn't manage to get it working.
Solution you found out can be improved by creating your own static factory that creates mocks with an OptionalAnswer and use it instead of default Mockito factory:
class MockitoOptional{
public static <T> T mock(Class<T> classToMock) {
return Mockito.mock(classToMock, new OptionalAnswer());
}
}
Next step will be to extend a test runner that will use this factory to inject mocks into #Mock annotated fields. Search for custom JUnit test runners if you haven't heard of them yet.
I got a first shot with the linked answer for strings.
public class OptionalAnswer extends ReturnsEmptyValues {
#Override
public Object answer(InvocationOnMock invocation) {
Object answer = super.answer(invocation);
if (answer != null) {
return answer;
}
Class<?> returnType = invocation.getMethod().getReturnType();
if (returnType == Optional.class) {
return Optional.absent();
}
return null;
}
}
#Test
public void testExample() {
MyObject test = mock(MyObject.class, new OptionalAnswer());
Assert.assertNotNull(test.get());
}
Won't get much easier, right?
Mockito.when(underTest.get()).thenReturn(Optional.<Object>absent());
This is all you need to do. Add the type returned from underTest.get() to your absent() call. Yes it is supposed to be on that side of the period.

Categories