I'm trying to mock a nested static class but getting a NullPointerException. Is there any way we can do this.
Sample code:
Parent Class
class Parent {
void record(String str) {
**//Getting a NPE at this line when running test case**
A.B.append(string);
}
}
Nested class
class A {
public static B b;
public static class B {
public void append(String str) {
//perform some task
}
}
}
Test class
#ExtendWith(MockitoExtension.class)
public class ParentTest {
#InjectMock
Parent parent;
#Test
public void dummy_test() {
A.B writer = mock(A.B.class);
doNothing().when(writer).append(any());
parent.record("Text");
}
}
#InjectMocks does not:
inject static fields.
inject fields to collaborating classes A
As your static field A.b is public, you can easily set it in a #BeforeEach method.
#ExtendWith(MockitoExtension.class)
public class ParentTest {
Parent parent = new Parent();
#Mock
A.B writer;
#BeforeEach
void setup() {
A.b = writer;
}
#Test
public void dummy_test() {
parent.record("Text");
}
}
Related
I have the following scenario.
public class A {
//constructors
#Value(${useMethodA:false})
private boolean isUseMethodAEnabled;
public void func() {
if(isUseMethodAEnabled()) {
a();
} else {
b();
}
}
}
//methods a() and b()
public class B {
#Autowired
private A a;
public void funcPrincipal() {
A.a();
System.out.println("Method A.a() was called");
}
}
#RunWith(MockitoJUnitRunner.class)
public class BTest {
#InjectMocks
private B b;
#InjectMocks
#Spy
private A a = new A();
#Test
public void test() {
ReflectionTestUtils.setField(a, "isUseMethodAEnabled", true);
b.funcPrincipal();
// if A is annotated with #Spy -> the "isUseMethodAEnabled" is always false
}
}
See the above comment, please.
Is there any solution to change the "isUseMethodAEnabled" value from false to true using reflection?
If I switch to #Mock instead of #Spy everything works as expected. But for my test cases, I must use #Spy + #InjectMocks.
So my class is:
public final class MyClass {
private static MyObject myObject;
public static void setMyObject(MyObject myObject) {
MyClass.myObject = myObject;
}
private MyClass(MyObject myObject){
setMyObject(myObject);
}
public static Optional<Object2> getObject2(params) {
Optional<Object2> object2 = myObject.execute(params);
return object2;
}
I'm trying to test with Junit
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#Mock
private MyObject myObject;
private MyClass myClass;
#Before
public void initialize() {
MockitoAnnotations.initMocks(this);
}
#Test
public void test1() {
Mockito.doReturn(Optional.empty()).when(myObject).executeQueryWithArgument(any);
myclass = new Myclass(myObject);
}
}
myclass = new Myclass(myObject);
This line fails and says make MyClass constructor package private. Is there any way to do this without doing that?
Add mocked myObject to the MyClass using the set method and write the test, like this:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#Mock
private MyObject myObject;
private MyClass myClass;
#Before
public void setUp() {
MyClass.setMyObject(myObject);
}
#Test
public void shouldDoSomething() {
// Arrange
Mockito.doReturn(Optional.empty()).when(myObject).executeQueryWithArgument(any);
// Act
Optional<Object2> actual = myClass.getObject2(.....);
// Assert
.....
}
}
I have a class to be tested which is like this:
public class MainClass extends BaseClass {
public static int variableToBeAsserted= 0;
MainClass(ConfigClass config) {
super(config);
}
public void myMethod() {
List list = objectOfClass1inSuperClass.operation(objectOfClass2inSuperClass.method())
while(methodInSuperClass()) {
// doing operations with 'list'
variableToBeAsserted++;
}
}
// ..few other methods which I am not going to test.
}
I have suppressed the constructor of my BaseClass and my ConfigClass. Now my test class is like this:
#RunWith(PowerMockRunner.class)
#PrepareForTest(MainClass.class)
public class TestClass {
#Before
public void setUp(){
suppress(constructor(BaseClass.class))
suppress(constructor(ConfigClass.class))
}
#Test
public void testMyMethod(){
MainClass main = new MainClass(new ConfigClass(""));
List list1= new ArrayList();
test1.add("somevalues");
Class1inSuperClass ob1 = PowerMock.createMock(Class1inSuperClass.class);
Class2inSuperClass ob2 = PowerMock.createMock(Class2inSuperClass.class);
EasyMock.expect(ob2.method()).andReturn(getClass());
EasyMock.expect(ob1.operation(getClass())).andReturn(list1);
PowerMock.replayAll();
main.myMethod();
Assert.assertEquals(expectedValue, main.variableToBeAsserted);
}
}
Now I don't know why but my test case fails with a NullPointerException.
It tries to access objectofClass1inSuperClass and fails. I thought this will mock it. But it does not get mocked.
EDIT: I am writing only the test and I cannot change anything in BaseClass. However I have the option to modify the MainClass.
You have two ways to inject mock object to the object under the test.
Manually via WhiteBox
#RunWith(PowerMockRunner.class)
#PrepareForTest(MainClass.class)
public class WhiteBoxApproachTestClass {
#Before
public void setUp() throws Exception {
suppress(constructor(BaseClass.class));
}
#Test
public void testMyMethod() {
MainClass main = new MainClass(createMock(ConfigClass.class));
List<String> list1 = new ArrayList<>();
list1.add("somevalues");
Class1inSuperClass ob1 = createMock(Class1inSuperClass.class);
Class2inSuperClass ob2 = createMock(Class2inSuperClass.class);
expect(ob2.method()).andReturn(getClass());
expect(ob1.operation(getClass())).andReturn(list1);
Whitebox.setInternalState(main, "objectOfClass1inSuperClass", ob1);
Whitebox.setInternalState(main, "objectOfClass2inSuperClass", ob2);
replayAll();
main.myMethod();
assertThat(MainClass.variableToBeAsserted).isEqualTo(5);
}
}
And via #TestSubject (http://easymock.org/user-guide.html#mocking-annotations)
#RunWith(PowerMockRunner.class)
#PrepareForTest(MainClass.class)
public class TestSubjectApproachTestClass {
#Mock(fieldName = "objectOfClass1inSuperClass")
private Class1inSuperClass ob1;
#Mock(fieldName = "objectOfClass2inSuperClass")
private Class2inSuperClass ob2;
#TestSubject
private final MainClass main = new MainClass(createMock(ConfigClass.class));
#BeforeClass
public static void before() throws Exception {
suppress(constructor(BaseClass.class));
}
#Test
public void testMyMethod() {
List<String> list1= new ArrayList<>();
list1.add("somevalues");
expect(ob2.method()).andReturn(getClass());
expect(ob1.operation(getClass())).andReturn(list1);
EasyMockSupport.injectMocks(main);
replayAll();
main.myMethod();
assertThat(MainClass.variableToBeAsserted).isEqualTo(5);
}
}
Full code you may find here.
If I have the following;
public class ClassA {
public void methodA(){
System.out.println("In ClassA, methodA");
}
}
public class ClassB {
public void methodB(){
}
}
and a junit test of;
#Test
public void testMocked(#Mocked final ClassB mockedB) {
System.out.println("Mocked inline");
new MockUp<ClassA>() {
#Mock
public void methodA() {
System.out.println("Inline mockup, mockedB:" + mockedB);
}
};
ClassA a = new ClassA();
a.methodA();
}
Then when I run the test I get;
Mocked inline
Inline mockup, mockedB:jmockitpractice.ClassB#329b0985
Which is as expected, the classB is Mocked, and an instance is available.
But, if I change this to create a helper class for mocking,
public class MockHelper {
#Mocked ClassB classB;
public void setupMocks(){
new MockUp<ClassA>() {
#Mock
public void methodA(){
System.out.println("In setupMocks, classB:"+classB);
}
};
}
}
and the junit becomes;
#Test
public void testMockedInClass() {
System.out.println("Mocked in helper class");
MockHelper mh = new MockHelper();
mh.setupMocks();
ClassA a = new ClassA();
a.methodA();
}
the result I get is;
Mocked in helper class
In setupMocks, classB:null
classB is not initialized by the #Mocked inside MockHelper
I would like to have all the mocking in a helper class, rather than having to declare all the mocking in the test class.
Any ideas why this doesn't work?
Thanks.
Thanks Dom Farr, the answer was inheritance.
public class MockHelper {
#Mocked
ClassB classB;
public void setupMocks() {
new MockUp<ClassA>() {
#Mock
public void methodA() {
System.out.println("In setupMocks, classB:" + classB);
}
};
}
}
and
public class mockTest extends MockHelper {
#Test
public void testMockedInClass() {
System.out.println("Mocked in helper class");
setupMocks();
ClassA a = new ClassA();
a.methodA();
}
}
As the test class extends the helper, it now works;
Mocked in helper class
In setupMocks, classB:jmockitpractice.ClassB#5d54e317
I have a test with 15-20 different test cases, I want to run the same test with twice with two different parameters which are supposed to be passed to the test's BeforeClass method, for instance:
public class TestOne {
private static ClassToTest classToTest;
#BeforeClass
public static void setUp() throws Exception {
classToTest = new ClassToTest("Argument1", "Argument2");
}
#Test
public void testOne() {
........roughly 15 - 20 tests here
}
public class TestTwo {
private static ClassToTest classToTest;
#BeforeClass
public static void setUp() throws Exception {
classToTest = new ClassToTest("Argument3", "Argument4");
}
#Test
public void testOne() {
........roughly 15 - 20 tests here, same as in TestOne
}
As you can see the only difference between these two tests is in the setup method, which passes different values to the constructor of the ClassToTest. I don't want to replicate the test methods in both classes, but would prefer either inheritance or some other intelligent way to achieve this in one class.
This seems like a perfect use case for JUnit4's #Parameters; see https://blogs.oracle.com/jacobc/entry/parameterized_unit_tests_with_junit or http://www.mkyong.com/unittest/junit-4-tutorial-6-parameterized-test/ . That said, you'll have to move the initialization from the setUp method to a constructor for the test class.
For what it's worth, here is how you would do it with TestNG:
public class TestFactory {
#Factory
public Object[] createTests() {
return new Object[] {
new ClassToTest("arg1", "arg2"),
new ClassToTest("arg3", "arg4")
};
}
}
public class ClassToTest {
public ClassToTest(String arg1, String arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
}
#Test
public void testOne() {
// use arg1 and arg2
}
}
Thanks all for your quick replies. This is how I did it finally
public abstract class Base {
final HeavyObject heavy;
protected Base(HeavyObject heavy) {
this.param = param;
}
#Test
public void test() {
param.doSomething();
}
#Test
.............More tests here
}
public class FirstTest extends Base{
private static HeavyObject param;
#BeforeClass
public static void init() {
param = new HeavyObject("arg1", "arg2");
}
public FirstTest() {
super(param);
}
}
public class SecondTest extends Base{
private static HeavyObject param;
#BeforeClass
public static void init() {
param = new HeavyObject("arg3", "arg4");
}
public FirstTest() {
super(param);
}
}
Base is an abstract class which has all the tests and FirstTest and SecondTest create their own objects with different parameters and pass it to the abstract class to use it.
As per the documentation (http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html):
A subclass does not inherit the private members of its parent class.
However, if the superclass has public or protected methods for
accessing its private fields, these can also be used by the subclass.
How about this:
public class TestOne {
private static ClassToTest classToTest1, classToTest2;
#BeforeClass
public static void setUp() throws Exception {
classToTest1 = new ClassToTest("Argument1", "Argument2");
classToTest2 = new ClassToTest("Argument3", "Argument4");
}
#Test
public void testOne() {
testOneImpl(classToTest1);
testOneImpl(classToTest2);
}
public void testOneImpl(ClassToTest classToTest) {
// exact samew as whatever your current testOne() test method is
}
....
}
EDIT:
Or to keep method count down:
public class TestOne {
private static List<ClassToTest> classesToTest;
#BeforeClass
public static void setUp() throws Exception {
classesToTest = new ArrayList<>;
classesToTest.add( new ClassToTest("Argument1", "Argument2"));
classesToTest.add( new ClassToTest("Argument3", "Argument4"));
}
#Test
public void testOne() {
for (ClassToTest classToTest: classesToTest) {
... same test content as before
}
}