how to unit test methods with dependencies - java

I need to create some unit testing for my application but I am not sure how to proceed and I couldn't find anything of help online. What I want to know is how to test an app where there are many methods dependent on a few others.
e.g.
public class foo(){
public void doIt() {
boz();
bar();
biz.baz(); //from another class
}
public void bar(){
...
}
public int boz(){
...
}
}
so in a scenario like the one presented above one would think that unit testing the doIt method would be sufficient since it is going to fail anyway if something is wrong with the methods called inside it.
Although, I am not sure if we can consider this to be unit testing as this tests the functionality of more than just an entity. In addition, if the test on the doIt method fails it is going to be really hard to tell where the error occurred especially in a case with many dependencies - doesn't that defy the meaning of unit testing?
So far the only approach I have thought is to start by testing the dependencies first (i.e. bar boz baz) and then the doIt method. That way, if the test suite gets to doIt with no errors and fails means that there is something wrong with the code implemented inside that method and not inside its dependencies.
BUT, is this the right way of doing it?

To test doIt() method without having to invoke the real implementations of bar() and baz(), use a Spy in Mockito:
Mockito spy documentation
If bar() is really void, you can do something like this:
foo f = spy(new foo());
doNothing().when(foo).bar();
Better is if you can inject your dependencies (ex: biz) as mock objects and test your foo class directly.

Related

JUnit5 mocking a class inside mocked class

I have a following situation :
Class A (test)
.
-> Autowired B
.
-> Autowired C
A.method {
b.method2();
}
B.method2 {
c.method3();
}
Now I have to test A.method() ->
#InjectsMock
A a;
#Mock
B b;
a.method();
when b.method2() is called, it obviously doesn't do anything but I actually want it to go inside b.method2() to eventually call c.method3(). Now c.method3() is what I'd want to use Mockito.when(c.method3()).return(1);
Is this possible? If so, how do I achieve this?
When using unit testing, you should think that the only piece of software that is running is this method. The objective is to isolate a part to analyze the behavior.
To test any class, a developer must make sure that the class's dependencies will not interfere with his unit tests.
Think about this situation: you call a real b.method2 and it throws an exception you don't expect. How to evaluate this case if your objective was to test this a.method?
Unit tests verify the behavior of the units. Think of a class as being a unit. Classes most often have external dependencies. Tests for such classes should not use their actual dependencies because if the dependencies are faulty, the tests will fail, even though the code inside the class may be perfectly fine.

JUnit test methods can't return a value

Why JUnit test methods can't return a value?
Documentation says (emphasis mine):
Test methods and lifecycle methods may be declared locally within the current test class, inherited from superclasses, or inherited from interfaces (see Test Interfaces and Default Methods). In addition, test methods and lifecycle methods must not be abstract and must not return a value.
Why is it enforced like this?
Is this purely a design choice? Is it a best practice?
One thing I haven't yet seen mentioned is that one of the reasons behind this design decision is historical.
Before test cases where marked by annotations, they were analysed by JUnit via reflection - all public void methods, the name of which started with test, and that didn't have any arguments, were considered test cases that were run by the framework.
Had these methods allowed to also return a value, you could not have created a public helper method starting with test that calculated a value commonly used in some tests, or the framework runner would have tried to run it.
You should have made them private anyway I guess.
Additionally, this was also the high time of "a method name that starts with a verb should do something" (rather than calculate something).
What is the reasoning behind this?
Think of each test method as being the logical analog of a public static void main(String[]) method in a classic Java application. There needs to be a well known method signature so that the JUnit framework is able to identify the test methods, and call them in a standard way.
As to the specific design points you are querying:
The reason that the methods cannot be abstract is that they need to be callable. You can't call an abstract method.
The reason that methods cannot return values is that the test framework would not be able to do anything useful with a returned value. (It would not be impossible to discard any result, but what is the point of a testcase result that can't be used?)
I was thinking maybe we could reuse one test method within another.
If you wanted to reuse code in unit tests, you would just use ordinary methods and other OO features of Java; e.g. base classes, helper classes, etcetera. There are few situations where you would want a free-standing test method and a "component" (for want of a better word) of another test method. And if you did you could write something like this.
public void test_xxx() {
real_test_xxx();
}
public int real_test_xxx() {
...
}
public void test_yyy() {
...
res = real_test_xxx()
...
}
Is this purely a design choice? Is it a best practice?
There are no best practices!
These are design choices that are guided by the general requirements and practicalities of unit testing1. Particularly, the requirement that unit test authors should NOT need to build configurations to tell the test framework what to do.
Note that TestNG takes a similar approach. If a TestNG #Test method is declared as returning a value, it is not treated as a test method; see Can a test method return a value in TestNG
Either way, unless you are proposing to implement yet another Java test framework, this is all moot.
1 - If there were strong practical reasons for needing abstract test methods or test methods that returned values, someone would have done something about it. The masses (of unit test writers) would rise up and overthrow the tyranical regime (of test framework developers). (Reprise of the "We won't be fooled again" by The Who.)
Is this purely a design choice?
Yes. In theory, nothing prevents the Junit runner (component thats runs tests of a test class) from accepting as valid a test method with a return value.
Is it a best practice?
Indeed. More you restrict the ways of specifying a unit test method, more you ensure that no misuse happens.
For example look at that simple example that would suppose a test method to return a value.
A addFoo() test method that asserts that the method does what it is expected and that also returns the Foo fixture created :
#Test
public Foo addFoo(){
Foo foo = new Foo(...);
// call the api ...
sut.addFoo(foo);
// assertion
assertEquals(...);
// return the foo fixture
return foo;
}
And here a addFooBar() test method that retrieves the Foo fixture created by the previous method and then asserts that the method addFooBar() does what it is expected :
#Test
public void addFooBar(){
Foo foo = addFoo();
Bar bar = new Bar(..., foo);
// call the api ...
sut.addFooBar(bar);
// assertion
assertEquals(...);
}
That lays multiple issues :
Does the method addFoo() should be executed multiple times ? It may hurt the test speed execution and make test reports unclear.
The addFoo() test result may make the addFooBar() test result to fail in spit of a correct implementation.
How to diagnostic the issue origin ? We added useless complexity.
The addFoo() test result may make the addFooBar() test result to succeed in spit of a incorrect implementation. Indeed, an incorrect implementation in addFoo() may compensate an incorrect implementation in addFooBar()
Test methods should be executed independently
If the tests can not run independently then they are not unit tests.
A unit test should not rely on any external state
If test method will return a value, it may applies that test results should be used/relevant to other tests
Same best practice in TestNG
Since TestNG follows best practices for unit testing, which means a unit test method should not have a return value
you can write some private methods and reuse them like
#Test
public void test1() {
privateTest();
}
#Test
public void test2() {
privateTest();
// more Logic
}
private int privateTest() {
return 0;
}

Mocking a dependent class using Mockito in JAVA

I am quite new to Mockito. I apologise if my question sounds sill. I have a standalone JAVA application for which I have to write Unit test cases using JUnit and Mockito. The important thing is that I cannot change the code as it has been written by someone else and Integration testing has been completed. I have tried to search for similar questions, but couldnt find anything. Solutions suggested in somewhat similar questions, didnt work :(
I have attached the flow control diagram.I want to mock the dependent classes. For example when I am Unit testing 'Class 1 --> Method 1', I want to mock the output of 'Method 2 in Class 2' WITHOUT CALLING it. I have tried to use Mockito.when and Mockito.doReturn. Both call the dependent methods.
Can someone please suggest me some ideas to achieve this ?
//Pseudocode of Class 1
public class Class1 {
public boolean method1() {
Class2 c2 = new Class2();
boolean b1 = c2.method2();
}
}
//Pseudocode of Class 2
public class Class2 {
public boolean method2() {
Class3 c3 = new Class3();
boolean b2 = c3.method3();
}
}
... Likewise same for Class 3, 4 and 5
What you're being asked to do is write unit tests for logic which was written by someone who knows absolutely nothing about writing code for test-ability. Probably a developer who's been writing code for a very long time, does things in an "old school" way and thinks he's way too important to write unit tests. Whoever wrote the logic you're testing needs to go back to school and learn some new tricks.
Anyway that doesn't help you, so you can still unit test this logic, it's just more of a pain. Mockito alone can't do it, you need "PowerMockito" which will let you mock the construction of Class2.
First things first you need to add 2 new test dependencies to your project "powermock-api-mockito"+"powermock-module-junit4".
A test class for you case would look something like:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Class1.class)
public class Class1Test {
private Class1 testSubject;
#Mock
private Class2 class2;
#Test
public void testMethod1() throws Exception {
testSubject.method1();
verify(class2).method2();
}
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.whenNew(Class2.class).withNoArguments().thenReturn(class2);
testSubject = new Class1();
}
}
So as you can see PowerMockito lets you mock out construction on new Class2 instances by using PowerMockito.whenNew(), this will only work if you've "prepared" Class1 using the annotation #PrepareForTest(Class1.class) otherwise Class1 can't be injected with the mock Class2 instance. Hopefully that points you in the right direction? On a side note, if you're a junior developer being asked to write unit tests for a more senior developer get out now, your development team is rotten!
Sometimes, code is not written to be testable.
Especially calling constructors inside methods or other constructors is a big issue for unit testing and mocking.
If you do not use factories or dependency inversion / dependency injection in any way, you will have a very hard time testing the code. This is one of the reasons why CDI is so popular.
Anyways, being asked to write Unit Tests after Integration Tests are already in place is kind of a bad smell. You should have written the unit tests first. If you follow Test Driven Development (TDD), you should have written your test even before you actually wrote your class. This way, it would be impossible to write classes that are hard to test.
But what to do on your already messed up situation?
I recommend to refactor your code. Instead of calling a constructor inside your methods, pass an instance into your method, or provide a field in the class in order to be able to mock it.
Reconsider the scope of your unit test. It should only test a single class. Everything else, all the dependencies should be mocked.

Mocking constructor using powermock on the class which needs to be tested

I am able to mock a constructor call using powermock from inside a class which I want to test. This works when I add the class I want to test in #PrepareForTest. But once I add my class there, even when the test cases pass, the coverage is being shown as 0 in the coverage plugin.
When I remove my class from #PrepareForTest, of course, coverage starts showing up for other test cases but the test case in which I have to mock constructor call fails. Not sure what to do about this.
Class A
{
MyObject o;
A(){
//some other code
o = new MyObject();
//some other code
}
public void process(){
//some code
}
#RunWith(PowerMockRunner.class)
#PrepareForTest(A.class)
Class TestA{
#Test
public void test1()
{
MyObject mocked = Mockito.mock(MyObject.class);
PowerMockito.whenNew(MyObject.class).withNoArguments().thenReturn(mocked);
A a = new A();
a.process();
//Assert as per test case
}
}
In coverage tool, coverage shows as 0 however, unit test passes and I checked in debug mode that it was covering all the statements of class A.
In coverage tool, coverage shows as 0 however, unit test passes and I checked in debug mode that it was covering all the statements of class A.
Coverage tools rely on manipulating the executed byte code.
So does PowerMock, when you mock static/new.
This can quickly lead to all sorts of problems. For JaCoCo, there seems to be a solution around offline instrumentation. Where, I also remember: some other person asked about that some time back, and in the end gave up, because he couldn't get "offline instrumentation" to work either.
For any other framework, I have to repeat old advice: consider to invest your time to learn how to write easy-to-test code. Because if you would do that, you would not need to use PowerMock(ito) in order to test it.
Your code is hard-to-test because of that new() statement in the constructor. Simply don't do that. Either use dependency injection via #InjectMocks, or have a test-only constructor that takes the required object.
Long story sort: when you write your own, new code, and you think you need PowerMock to test it, then you are doing something wrong.
I think you can do without Powermock here. If you Spy on class A and mock the getter you should end up with the same result and most likely have your coverage correct:
#Test
public void test1(){
MyObject mocked = Mockito.mock(MyObject.class);
A spyA = Mockito.spy(new A());
doReturn(mocked).when(spyA).getMyObject();
...
}

Is it a right case for Mockito spy?

Let's say I have a class
class SomeClass
{
public void methodA()
{}
public void methodB()
{}
public void someMethod()
{
methodA();
methodB();
}
}
I would like to test behavior of someMethod() with Mockito.
The only way I could think of is using spy();
Something like
SomeClass someClass = spy(new SomeClass());
someClass.someMethod();
InOrder inOrder = inOrder(someClass);
inOrder.verify(someClass).methodA();
inOrder.verify(someClass).methodB();
I'm new to the mockito and documentation says
"Real spies should be used carefully and occasionally, for example when dealing with legacy code."
So maybe I'm missing something and there is better (right) way to verify that methodA and methodB were called without explicitly calling them in the test case.
Thanks.
Yes, spy() is fit for your purpose. The warning is due to the fact that real methods are invoked, and hence you can get unexpected results (for example - real money being withdrawn from a bank account)
If your code needs spy for unit testing - something wrong.
Spy is a first sign of a code smell.
You have two options to avoid it in your example:
You can avoid mocking one of the method and test the whole someMethod.
If methodA and methodB is really needs to be mocked - you can move them to seperate class etc.

Categories