NullPointerException in super class method's constructor when testing using mockito - java

I have a super class:
public class A extends Fragment{
public A(Context context){
super(context);
}
}
and class B inherits it:
public class B extends A{
public B(Context context){
super(context);
}
}
In my test class where I'm using Robolectric and Mockito, when I instantiate class B like so: B b = new B(context)
I get a nullpointerexception in class A's constructor's super method. Am I doing something wrong?
Here's the test code:
#Mock Context context;
#Before
public void setup() {
initMocks(this);
B b = new B(context);
}

Simple answer: it isn't that easy.
The exception stack trace which you are hiding from us would tell you/us that those NPE happens within some base Android code. Thing is: you are passing a context object into the Fragment constructor.
Guess what: the Fragment code then starts using that context object. It makes a whole series of calls on that object, in order to retrieve other objects, to then make calls on those objects.
Right now, you only mocked a Context object. Any calls to it that are supposed to return a value will return null by default. Mockito doesn't know anything about the classes you are mocking. So, when you want that a certain call returns something else than null, you have to specify that behavior.
So, the "short" answer is: you have to prepare that mock object to be able to be used like this. The long answer ... was already given; and you need to study its details in depth!
Beyond that; you can have a look into another question dealing with the exact same problem.

Related

NullPointerException happens with final member in custom view

I'm creating a custom view, and I write code like this:
public class TestView extends BaseCustomView<ViewTestBinding> {
...
#NonNull
private final Map<Boolean, Drawable> icon = new HashMap<>();
private void onInit(Context context, #Nullable TypedArray attrs) {
assert attrs != null;
// NullPointerException throws at this line
icon.put(true, attrs.getDrawable(R.styleable.TestView_icon));
icon.put(false, attrs.getDrawable(R.styleable.TestView_iconUnselect));
...
}
Method onInit will be called in every constructor, but when I store the key-value pair in the icon, it thows NullPointerException:
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.Map.put(java.lang.Object, java.lang.Object)' on a null object reference
I put the full code on Github. From my understanding, changing icon to null is impossible, so why is NullPointerException thrown? Thank you!
Update (2022/11/18 14:48 GMT +8):
According to the answer of #Cheticamp, I write a minify test case: ExampleUnitTest.java, and it reproduced the error, System.out.println() at line 31 printed null.
public class ExampleUnitTest {
#Test
public void test() {
new ClassB();
}
public static abstract class ClassA {
public ClassA() {
method();
}
protected abstract void method();
}
public static class ClassB extends ClassA {
#SuppressWarnings("FieldCanBeLocal")
private final Integer integer = 114514; // print null
// private final int integer = 114514; // print 114514
#Override
protected void method() {
System.out.println(integer);
}
}
}
But I still can't understand why I got null, the code in this link initializes members in the constructor, and I assign the value directly after defining, Even if I initialize directly, is the actual assignment still performed in the subclass constructor?
If so, when I change the type to int like line 27, it should print the int's initial value of 0, but this time it correctly prints the value I initialized.
I have been caught by this same type of error in the past. This Stack Overflow answer is a decent explanation of what I believe is happening. As stated in the this Stack Overflow reference:
A quote from Effective Java 2nd Edition, Item 17: Design and document for inheritance, or else prohibit it:
There are a few more restrictions that a class must obey to allow inheritance. Constructors must not invoke overridable methods, directly or indirectly. If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will be invoked before the subclass constructor has run. If the overriding method depends on any initialization performed by the subclass constructor, the method will not behave as expected.
If you take a look at the base class BaseCustomView, it calls init() in its constructors. init() then calls onInit() which is overridden in the subclass TestView. Since the constructor for the base class runs before the constructor for the subclass, the class member icon has not yet been initialized.

Android - java set global context to another class

The first problem is i have java class with 500 lines. That will so bad, so i want to split them. I found solution in another stackoverflow thread that, we can pass main class context to another class.
//main class
Public class Main{
#Override
Public void onCreate(Bundle inst)
{
Super.onCreate(inst);
ClassB bclass = new ClassB(this);
bclass.setLayout();
}
}
//B class
Public class ClassB{
Activity act1;
Public ClassB(Activity act)
{
act1 = act;
}
Public void setLayout()
{
this.act1.setContentView(R.layout.lay);
}
}
Okay, thats work. But how to make act1 be our main context? so every time i want call setContentView from ClassB i don't need to use act1 again
Edit 1:
What i hope is, i want to make act1 as default context for ClassB. So when i call setContentView from ClassB, its directly called to Main.setContentView
Because if i just call setContentView from ClassB, i wi get NullPointerException. Its make a sense since ClassB have no context
Not sure why you are doing this. There are ways to reduce lines of a single java class instead of keeping UI references in other classes or rendering UI by other classes. You can encapsulate your business logic(Not UI) in other classes which can improve the readability of your code or in your case, if you have multiple Activities which have similar behavior, you can create a base abstract activity class and put all the base code to it.

Java - Inject bean only in super constructor without field #Autowired

I am dealing with a design situation and I am not sure how to solve it or if I am doing anything wrong with my micro design.
public abstract class A implements SomeFrameworkInterface {
private final Param1 a1;
#Inject
private Param2 a2;
protected A(Param1 a1) {
this.a1 = a1;
}
#Override
public someFrameworkInterfaceMethod() {
//code
doMyStuff();
}
protected abstract void doMyStuff();
}
#Named
#SomeCustomAnnotation(property="someProperty")
public class B extends A {
#Inject
public B(Param1 b1) { //b1 is different for every implementation of class A, through some annotations
super(b1);
}
#Override
protected void doMyStuff() {
//code
}
}
I need to change "#Inject Param2 a2" from class A to be injected
through the constructor, so the code would be testable and I will
clean a code smell associated with this.
I don't want to send it from B constructor, because I would have
to add one more argument in B constructor, in A constructor and
for each constructor from all implementations of A... but the
child class never needs to know about Param2 instance.
Param2 may be static, but needs to be injected because it's
component (#Named), so I didn't find a way to static inject.
Restrictions are the following:
3rd party framework with custom annotations to be used and interface
to be implemented
code must be testable, so #Inject/#Autowired on fields is not allowed
we are restricted from using Spring which is "encapsulated" in the
custom framework mentioned, so we can use only pure Java (JSR-330).
(if any solution in Spring, would be nice to know about it too)
It's impossible to pass any parameter to A's constructor without going through B's, because you are actually constructing an instance of B. A's constructor is only delegated to in order to construct the "A part" of the B instance.
The most you can do is pass some sort of "typeless" parameter to B's constructor, so that at least B won't know about Param2, but that cure already sounds worse than the disease even as I was writing it down...
I don't see injecting Param2 post-construction making the code any less testable. OTOH, using a static field would most certainly do.
I find the solution to test this, but it's look a little weird. I mean I do not understand why or how is this happening, but it does the job in a clean way.
Test class looks like:
#RunWith(MockitoJUnitRunner.class)
public BTest {
#Mock
private Param1 mockParam1;
#Mock
private Param2 mockParam2;
#InjectedMocks
private A b = new B(null);
#Test
public someFrameworkInterfaceMethodTest() {
when(...).thenReturn(...)
b.someFrameworkInterfaceMethod();
verify(...).someCall(any());
}
}
I had a hard time to figure it out why Param1 a1 remained null and tried to find out different solutions. But it seems Mockito injects fields again post construct (after "new B(null)" is called - if fields are null or not sure). Param2 a2 was mocked by mockito while Param1 a1 remained null and this happened because of the "final" keyword on Param1 a1 in B class.

Mockito says "Wanted but not invoked" but debugging test jumps into that method

I have to deal with a legacy application that has no tests. So before I begin refactoring I want to make sure everything works as it is.
Now imagine the following situation:
public SomeObject doSomething(final OtherObject x, final String something) {
if(x != null) {
final String = someOtherMethod(x, something);
}
}
protected String someOtherMethod(final OtherObject x, final String something) {
....
}
Now I want to make sure that protected method is called as well
So I did this
#InjectMocks // Yes there is more going on here
private MyTestObject myTestObject;
private MyTestObject spy;
private static final OtherObject VALID_OTHER_OBJECT = new OtherObject();
#Before
public void setup() {
this.spy = Mockito.spy(myTestObject);
}
#Test
public void ifOtherObjectIsNotNullExpectSubMethodToBeCalled() {
myTestObject.doSomething(VALID_OTHER_OBJECT, null);
verify(spy).someOtherMethod(VALID_OTHER_OBJECT, null);
}
I get a failing test and "Wanted but not invoked..." for someOtherMethod().
I jumped right into debug mode and checked. That method is called!
What could be the cause of this? Am I misusing the spy here?
Edit: I want to stretch that I know this is not what you typically test for, especially since someOtherMethod(...) has a non-void return-value here. But imagine the return value was void...
Basically I just want to understand why the spy fails here.
As per its Javadoc, Mockito.spy(object) creates a copy of the passed in object. Calling methods on the original passed in object then does not register on the spy, because the spy instance is not the same object.
Change myTestObject.doSomething(...) to spy.doSomething(...) and it should work.
Alternative (different way to achieve the same thing):
Consider using the #Spy annotation on your myTestObject.
Be sure to add MockitoAnnotations.initMocks(this); to your initialization method (in your junit test).
The #Before and #Mock annotations are useful as well.
I had one object creating another and that other object making calls. So I needed to make that internal object be using the spied reference instead. I used reflection and updated the reference using Whitebox.
TestAddressFragment fragment = spy(new TestAddressFragment());
AddressPresenter presenter = fragment.getPresenter();
Whitebox.setInternalState(presenter, "view", fragment );
Now my fragment could check if its method was called.
verify( fragment ).showAddress(any(), anyBoolean());

Can I mock a superclass's constructor with Mockito/Powermock?

Is it possible using Mockito and optionally Powermock to mock a superclass S such that any calls to the superclass to S (including calls to the S() constructor) are mocked? So using the below example, if I replace S with MockS using Mockito, will the call to super() use the constructor in MockS?
class S {
S() {
// Format user's hard drive, call 911, and initiate self-destruct
}
}
class T extends S {
T() {
super();
}
}
class Test {
#Mock private S mockS;
new T(); // T's call to super() should call the mock, not the destructive S.
}
I've seen questions about mocking individual methods in S or mocking only calls to super(), and read that this is unsupported, but it's not clear whether or not I can mock the entire superclass.
With my current tests, when I try to mock S, T's call to super() calls the real implementation, not the mock.
To work around this apparent limitation, I refactored my code, replacing inheritance with delegation, and I think I've ended up with a better design anyhow since the inheritance wasn't really necessary.
The new code looks like this. Mind you the code for the question was simplified, so the real classes have much more functionality.
class S {
S() {
// Format user's hard drive, call 911, and initiate self-destruct
}
}
class T {
T(S s) {} // Now T "has an S" instead of "is an S"
}
class Test {
#Mock private S mockS;
new T(s); // T's call to super() should call the mock, not the destructive S.
}
For those interested, using Guice and Android, the test looks more like this:
class T {
T(Activity activity, S s) {}
}
class Test {
#Mock Activity activity;
#Mock S mockS;
injector = Guice.createInjector(new AbstractModule() {
#Override protected void configure() {
bind(Activity.class).toInstance(activity);
bind(S.class).toInstance(mockS);
}}
);
T t = injector.getInstance(T.class);
}
I think this is possible with PowerMock only if the method on the child is different from the method on the superclass (i.e., you cannot mock the parent method if the child overrides that method). For a little more detail, you can look at the relevant bug report.
For PowerMock, check out Suppressing Unwanted Behavior page to see if it will be enough for your needs.
After much digging around, I ended up using JMockit for these tricky cases. Before I moved on to JMockit, I tried stubbing out all the places exceptions were thrown using suppression. In the end, I needed to override some methods, and not just suppress them, so I ended up abandoning it.
Example usage for Android case:
First, you mock out your superclass using the #MockClass annotation:
#MockClass(realClass = Activity.class, instantiation = PerMockedInstance)
public class FakeActivity {
public Bundle mSavedInstanceState;
#Mock
public void $init() {}
#Mock
public void onCreate(Bundle savedInstanceState) {
mSavedInstanceState = savedInstanceState;
}
}
When activated, this class will replace the default constructor of Activity with $init(), and replace the onCreate method with the one above. WIth android, the unit under test is derived from Activity (in my sample code, it is HelloTestActivity). The test class looks like this:
public class HelloTestActivityTest3 extends AndroidTest {
#Tested
HelloTestActivity activity;
FakeActivity fakeActivity = new FakeActivity();
#Before
public void setupMocks()
{
Mockit.setUpMock(fakeActivity);
}
#Test
public void onCreate_bundle(#Mocked Bundle savedInstanceState)
{
// Try to access out-of-band information from the fake
activity.onCreate(savedInstanceState);
assertSame(savedInstanceState, fakeActivity.mSavedInstanceState);
}
}
The call Mockit.setupMock(fakeActivity) replaces the super class with my instance of the fake. With this usage, you can access internal state of your fake class as well. If you don't need to override any methods with custom functionality, you can use other methods available from Mockit class.
As rogerio pointed out in the comments below, mocking the Activity class is the bare minimum. The following code demonstrates this.
public class HelloTestActivityTest4 {
#Tested
HelloTestActivity activity;
#Mocked
Activity base;
#Test
public void testOnCreate() throws Exception {
// Just make sure "Stub!" exception is not thrown.
activity.onCreate(null);
}
}
The declaration #Mocked Activity base; causes all methods (excepting static initializers) of Activity class and its superclasses to be mocked in the tests defined in HelloActivityTest4.
What you can do is extract the 'dangerous' code in your superclass constructor into a non-private method, then use Mockito spy on your class T and override the behaviour in that extracted method.
This would of course violate encapsulation. Guava offers the VisibleForTesting annotation for such cases.

Categories