4

I'm very new to unit testing, I'm wondering if there is a way to test a method without initializing the class. The reason I'm asking is because there are lot of object passing in the constructor meaning a lot of mocking stubbing while from a thorough check methodToTest seems not to use any object attribute. It's not my code otherwise the method could be converted to static.

class ExampleClass {
   public ExampleClass(FirstClass fc, SecondClass sc, ThirdClass tc) {}

   public void methodToTest(FourthClass foc) {}
}
giannis christofakis
  • 8,201
  • 4
  • 54
  • 65
  • 1
    I think, you shouldn’t put any assumptions about what a method does internally into your unit test. After all, a unit test is supposed to check whether a method does its job, regardless of how it does it. – Holger Jul 23 '15 at 12:56
  • 1
    Dont you have an option to create the non-parameterized constructor? like `public ExampleClass() {} ` or make the method `static` – Sashi Kant Jul 23 '15 at 12:56
  • 1
    If your question boils down to your wanting to create an object without calling a constructor (whatever that means), then look at http://stackoverflow.com/questions/3488097/is-it-possible-to-create-an-instance-of-an-object-in-java-without-calling-the-co – Bathsheba Jul 23 '15 at 12:58
  • Why you cannot create a object via calling Constructor and pass null values in the parameters and call methodToTest and pass object FourthClass? – Ankush soni Jul 23 '15 at 13:46
  • @Ankushsoni I guess because I want to avoid a `NullPointerException`. – giannis christofakis Jul 24 '15 at 08:14
  • @Ankushsoni Let's continue our conversation in a chat room if you don't mind http://chat.stackoverflow.com/rooms/84150/junit-mockito – giannis christofakis Jul 24 '15 at 08:48

6 Answers6

1

You have some options here:

  • Make the method static so you don't need a reference to an actual object. This will only work if the method does not need any state of ExampleClass (i.e. everything it needs is passed in the method arguments).
  • Extract this method to another class (perhaps using the method object pattern) that's easier to test on its own. This refactoring is called replace method with method object.
prgmtc
  • 765
  • 3
  • 11
  • 1
    Your first solution, despite being a solution, is bad. It basically proposes changing the behaviour of the class for testing purposes, which goes against good practices. Your second solution would be a better approach, but then again would only make sense if the method can be easily decoupled from the class. – Tavo Jul 23 '15 at 13:16
  • 1
    A rule of thumb I like to apply in these scenarios: if something is hard to test there might be something wrong with the design of the code under test. Making something static can help solve the problem in this specific example. It wouldn't be "just for testing", as this reveals some extra information about the method: it's not dependent upon any instance variables of ExampleClass. The only downside I see to this approach is that stubbing/mocking this static method becomes harder. Separating responsibilities might result in a cleaner design but requires a lot more extra work. – prgmtc Jul 23 '15 at 14:01
1

Ok, it seems I found a way. Since the method is irrelevant to the state of the object, I could mock the object and order the MockRunner to use the real method when it is called. It is named partial mocking. The way of doing it is

    ExampleClass = PowerMockito.mock(ExampleClass.class);
    when(ExampleClass.methodToTest(foc)).thenCallRealMethod();
giannis christofakis
  • 8,201
  • 4
  • 54
  • 65
1

Usually, having to many parameters in constructors is a hint on bad conception. You'd better rethink you Objects and classes to reduce argument to give to the constructor.

If you don't want to, you can still use some kind of a "TestUtil" wich instantiate class for you.


Example :

public class MyTestUtils {
    public createValidFirstClass() {
        return new FirstClass(/* some arguments here */);
    }
    public createValidSecondClass() {
        return new SecondClass(/* Some arguments here */);
    }
    public createValidThridClass() {
        return new ThridClass(/* Some arguments here */);
    }
    public createValidExampleClass() {
        return new ExampleClass(createValidFirstClass(), createValidSecondClass(), createValidThridClass());
    }
}

This class MUST be in your test packages and not in your project, and should not be used outside of the tests, it would be a really bad practice here, use Factory or Builder for your real projects.



Anyway, i think that the best solution is to rethink you Classes.
Example :

public class People {
    public People(String firstName, String lastName, Date birth, Date death) {
    }
}

As you can see this is a pain in the ass to control that all given parameter was correctly formatted and not null.

This number of argument passed to a method can be reduced this way.

public class People {
    public People(PeopleNames names, Period period) {
    }
}
public class PeopleNames {
    public People(String firstName, String lastName) {
    }
}
public class PeopleNames {
    public People(Date begin, Date end) {
    }
}
Anthony Raymond
  • 7,434
  • 6
  • 42
  • 59
0

As far as I know, you cannot conduct a Test without initializing the class.

The three steps of Test are Arrange,Act and Assert. The class has to be initialized in the arrange part for fetching the required methods in the particular class you are testing.

Rachit Ahuja
  • 371
  • 2
  • 15
0

I don't know what your method does but if it's possible for your application you could just make methodToTest static, which would allow you call it without an instance of the class.

Alternatively, you could avoid too much instantiation by creating one instance in a @BeforeClass method to be used for all tests, but again I don't know what you method does so that might not be desirable.

adamconkey
  • 4,104
  • 5
  • 32
  • 61
0

Use suppressions from PowerMockito.

import org.powermock.api.support.membermodification.MemberModifier; import org.powermock.api.support.membermodification.MemberMatcher; suppress(MemberMatcher.constructor(ExampleClass.class))

kswaughs
  • 2,967
  • 1
  • 17
  • 21