How do you unit test main method in Java? - java

This is my class with main function. Here I initialize a spring bean which has camel route in it. I do not want to test any other classes being referred in this code but I just want to increate code coverage of this main class. How do I mock and test this class?
import org.apache.camel.main.Main;
public class ABC{
public static void main(String[] args) {
Main main = new Main();
MyCamelRoute myCamelRoute = SpringUtil.getBean(MyCamelRoute.class);
main.addRouteBuilder(myCamelRoute);
Thread t = new Thread(() -> {
try {
main.run();
} catch (Exception e) {
_logger.error("Unable to add route", e);
}
}, "started route");
t.start();
}
}

As you're writing of "mocks" I assume you intend to write a unit test.
ONE: Either you test a class or you mock it. You use mocks to make your test independent of the behaviour (and thus possible bugs) of other units (the "dependencies" of your "system under test" (SUT)).
TWO: You do not write tests to increase code coverage. You write tests to enforce requirements of the API contract.
THREE: To test your main method: call it! You can put in arguments and see if the return value matches your expectations.
FOUR: The problem here might be that you've got static dependencies you cannot control. Spring allows you to configure mocks for bean injection. Can't tell you details right now but I am sure you can find out, it should be something like #Configuration annotated classes or test specific versions of them.
But: your test has no control whatsoever over the main object. And sincerely, I would guess that actually you intend to test the Mainclass. Also you might want to inject the Main instance via Spring means.
FIVE: I am not sure wether it is a good idea to involve multi threading in unit tests as it means your test cannot control the environment of your sut. If you do not know where your test starts, you cannot decide if where it ends up is correct or not.

Related

Equivalent to #DirtiesContext(...) for Surefire + JUnit?

I'm using the maven-surefire-plugin with junit 4.1.4. I have a unit test which relies on a 3rd party class that internally uses static { ... } code block to initiate some variables. For one test, I need to change one of these variables, but only for certain tests. I'd like this block to be re-executed between tests, since it picks up a value the first time it runs.
When testing, it seems like surefire instantiates the test class once, so the static { ... } code block is never processed again.
This means my unit tests that change values required for testing are ignored, the static class has already been instantiated.
💭 Note: The static class uses System.loadLibrary(...), from what I've found, it can't be rewritten to be instantiated, static is the (rare, but) proper usage.
I found a similar solution for Spring Framework which uses #DirtiesContext(...) annotation, allowing the programmer to mark classes or methods as "Dirty" so that a new class (or in many cases, the JVM) is initialized between tests.
How do you do the same thing as #DirtiesContext(...), but with maven-surefire-plugin?
public class MyTests {
#Test
public void test1() {
assertThat(MyClass.THE_VALUE, is("something-default"));
}
#Test
public void test2() {
System.setProperty("foo.bar", "something-else");
assertThat(MyClass.THE_VALUE, is("something-else"));
// ^-- this assert fails
// value still "something-default"
}
}
public class MyClass {
static {
String value;
if(System.getProperty("foo.bar") != null) {
value = System.getProperty("foo.bar"); // set to "something-else"
} else {
value = "something-default";
}
}
public static String THE_VALUE = value;
}
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>4.1.2</version>
</plugin>
static initialization blocks in java are something that can't be easily handled by JUnit. In general static stuff doesn't play nicely with unit testing concepts.
So, assuming you can't touch this code, your options are:
Option 1:
Spawn a new JVM for each test - well, this will work, but might be an overkill because it will aggravate the performance
If you'll follow this path, you might need to configure surefire plugin with:
forkCount=1
reuseForks=false
According to the surefire plugin documentation this combination will execute each test class in its own JVM process.
Option 2:
Create a class with a different class loader for every test.
Basically in Java if class com.foo.A is created by ClassLoader M is totally different than the same class com.foo.A created by ClassLoaded N.
This is somewhat hacky but should work.
The overhead is much smaller than in option 1. However you'll have to understand how to "incorporate" new class loaders into the testing infrastructure.
For more information about the creation of the custom class loader read for example this tutorial

JUnit without inversion of control

Currently the JUnit5 Framework works with Inversion of Control. I.e. you annotate a test method with #Test and then JUnit scans your classpath (in the simplest case)
Now is there a way for me to be in charge of calling the test cases through JUnit APIs? Maybe by hooking my test implementations to some test registry provided by JUnit?
I'm pretty new to JUnit - how did older versions go about this?
The reason I'm asking is that normally to execute my test cases, I'd have to run something along the lines of
java -jar junit-platform-standalone.jar --class-path target --scan-class-path
on the command line. My situation requires me to run the test cases through by executing one of my own classes, like that e.g.
java /com/example/MyTestCassesLauncher
EDIT: to clarify, I need one of my own classes to be hosting/launching my test cases, something like this:
// Maybe this needs to extend one of JUnit's launchers?
public class MyTestCassesLauncher {
public static void main(String[] args) {
JUnitLauncher.launchTests(new MyTestClass());
}
}
where JUnitLauncher.launchTests is some kind of API provided by the platform. I'm not looking for a method with that exact same signature but a mechanism that would allow me to ultimately call my own MyTestClassesLauncher class to run the tests.
Thanks in advance.
Not sure what you arÄ™ actually trying to achieve but in Junit5 to change behaviour of your tests you can use Extensions mechanism, similar to Junit4 RunWith but more powerful
Such custom extension can provide some additional logic like in this logging example
public class LoggingExtension implements
TestInstancePostProcessor {
#Override
public void postProcessTestInstance(Object testInstance,
ExtensionContext context) throws Exception {
Logger logger = LogManager.getLogger(testInstance.getClass());
testInstance.getClass()
.getMethod("setLogger", Logger.class)
.invoke(testInstance, logger);
}
}
The way Junit controls it's flow is Junit problem - you should not modify framework but extend it

How does all annotations work in TestNg without main() method

I have a doubt in TestNG with Java. I am completly new to TestNG. My doubt is, How all the test cases are executing using TestNG in java without having main() method? Please suggest me if you have any ideas. Following code is the example of a sample test case using TestNG in java. But if you notice, you can find one thing that there is no main() method in the code. Then, how does the testcases are executing?
I have another doubt. Is main() method needed for selenium Webdriver and TestNG combination to execute a script? Or can we execute testcases without main() method? If we can execute testcases without main(), then how does it is possible?
package com.first.example;
import org.testng.annotations.Test;
public class demoOne {
#Test
public void firstTestCase()
{
System.out.println("im in first test case from demoOne Class");
}
#Test
public void secondTestCase()
{
System.out.println("im in second test case from demoOne Class");
}
}
This is a valid doubt many testers have. Because the main() method is needed to run the Java program and while writing tests in TestNg we don't use main() method, and we use Annotations instead.
Annotations in TestNG are lines of code that can control how the method below them will be executed. So, in short you don't need to write main() method, TestNg do that by itself. Refer the code at the end in Annotations documentation to get the idea how it happens.
As rightly pointed out in this answer: https://stackoverflow.com/a/1918154/3619412
Annotations are meta-meta-objects which can be used to describe other
meta-objects. Meta-objects are classes, fields and methods. Asking an
object for its meta-object (e.g. anObj.getClass() ) is called
introspection. The introspection can go further and we can ask a
meta-object what are its annotations (e.g. aClass.getAnnotations).
Introspection and annotations belong to what is called reflection and
meta-programming.
Also, it's not necessary to have main() method in your tests, but you can use main() method to run the TestNg tests if you want. Refer this.
to run script from cmd prompt we use below statement,
java org.testng.TestNG testng1.xml
main method in TestNG.java class how accept the command line argument,
public static void main(String[] argv) {
TestNG testng = privateMain(argv, null);
System.exit(testng.getStatus());
}
You saw it right. Test-cases get executed through testng, the testing framework which was inspired from junit without having the main() method but extensively uses annotations.
Annotations
As per the documentation in Annotations majority of the APIs require a huge amount of boilerplate code. To write a web service you need to provide a paired interface and implementation. This boilerplate could be automatically generated by a tool if the program can be decorated with annotations indicating which methods were remotely accessible. Annotations doesn't affects the program semantics directly but they do affect the way programs are treated by tools and libraries, which can in turn affect the semantics of the running program.
TestNG
TestNG is a simple annotation-based test framework which uses a marker annotation type to indicate that a method is a test method and should be run by the testing tool. As an example:
import org.testng.annotations.Test;
#Test
public void foo() {
System.out.println("With in foo test");
}
The testing tool which is being used is as follows:
import java.lang.reflect.*;
public class RunTests {
public static void main(String[] args) throws Exception {
int passed = 0, failed = 0;
for (Method m : Class.forName(args[0]).getMethods()) {
if (m.isAnnotationPresent(Test.class)) {
try {
m.invoke(null);
passed++;
} catch (Throwable ex) {
System.out.printf("Test %s failed: %s %n", m, ex.getCause());
failed++;
}
}
}
System.out.printf("Passed: %d, Failed %d%n", passed, failed);
}
}

Testing with jUnit

I have a class called Menu.java which is used as the interface for my program.
In this Menu.java class, I have a switch/case block which acts as my menu Options.
Basically, I want to use jUnit to test the output of each case in that switch/case block, but I'm struggling to find the best way to do it.
Is it best to have a seperate jUnit TestCase for each menu operation? and then use a single TestUnit to run all cases? Or is there a better way this can be done?
Many thanks.
In general, each class has a corresponding test class. So you would have a MenuTest.java to match your Menu.java. This allows you to find quickly the tests that are associated with a particular file, because of the naming convention.
Then, ideally, each test would have one test method associated with it. So if your switch has 10 cases, you would end up with 10 test methods, one for each case. This allows you to isolate quickly the option that is failing, because you get feedback for each test individually.
Note that TestCase is JUnit 3. If possible, use JUnit 4 tests (org.junit.*), these are annotated with a #Test annotation.
I would use a single test case for each possible work flow. Ideally you would have one assertion per test case, which is probably easier following that guideline. In general you want to keep your unit tests small and concise.
Then I would place all the test cases in the same test class as long as they belong to the same tested class.
I'd create a single class for Menu.java (MenuTest.java). I'd write a test case for each menu option. If you have GUI stuff, separate it from the logic.
No need to test the GUI or its plumbing.
If the method you are testing is receiving different parameters for this switch, consider using a parameterized test case. The advantage is that it's easier to keep track of what need to be changed if your switch changes:
Here is how it works with TestNG (look up "parameterized test case" for JUnit)
// This method will provide data to any test method that declares that
// its Data Provider is named "test1"
#DataProvider(name = "test1")
public Object[][] createData1() {
return new Object[][] {
{ "Foo", new Integer(36) },
{ "Bar", new Integer(37)},
};
}
// This test method declares that its data should be supplied by the
// Data Provider named "test1"
#Test(dataProvider = "test1")
public void verifyData1(String n1, Integer n2) {
System.out.println(n1 + " " + n2);
}
In your case I'd use single test case for all options. You need something like this:
import org.junit.Test;
import org.junit.Assert;
import your.project.Menu;
public class MenuTest {
#Test
public void testCase() {
Menu menu = new Menu();
assertEquals("1",menu.runCase("bar"));
assertEquals("2",menu.runCase("foo"));
//etc
}
}

mockito : how to unmock a method?

I have a JUnit class with different methods to perform different tests.
I use Mockito to create a spy on real instance, and then override some method which is not relevant to the actual test I perform.
Is there a way, just for the sake of cleaning up after me in case some other tests that run after my tests also use the same instances and might execute a mocked method they didn't ask to mock, to un-mock a method?
say I have a spy object called 'wareHouseSpy'
say I overriden the method isSomethingMissing :
doReturn(false).when(wareHouseSpy).isSomethingMissing()
What will be the right way to un-override, and bring things back to normal on the spy i.e make the next invokation of isSomethingMissing to run the real method?
something like
doReturn(Mockito.RETURN_REAL_METHOD).when(wareHouseSpy).isSomethingSpy()
or maybe
Mockito.unmock(wareHouseSpy)
Who knows? I couldn't find nothing in that area
Thanks!
Assaf
I think
Mockito.reset(wareHouseSpy)
would do it.
Let's say most of your tests use the stubbed response. Then you would have a setUp() method that looks like this:
#Before
public void setUp() {
wareHouseSpy = spy(realWarehouse);
doReturn(false).when(wareHouseSpy).isSomethingMissing();
}
Now let's say you want to undo the stubbed response and use the real implementation in one test:
#Test
public void isSomethingMissing_useRealImplementation() {
// Setup
when(wareHouseSpy.isSomethingMissing()).thenCallRealMethod();
// Test - Uses real implementation
boolean result = wareHouseSpy.isSomethingMissing();
}
It depends whether you are testing with TestNG or JUnit.
JUnit creates a new instance of itself for each test method. You basically don't have to worry about reseting mocks.
With TestNG, you have to reset the mock(s) with Mockito.reset(mockA, mockB, ...) in either an #BeforeMethod or an #AfterMethod
The "normal" way is to re-instantiate things in your "setUp" method. However, if you have a real object that is expensive to construct for some reason, you could do something like this:
public class MyTests {
private static MyBigWarehouse realWarehouse = new MyBigWarehouse();
private MyBigWarehouse warehouseSpy;
#Before
public void setUp() {
warehouseSpy = spy(realWarehouse); // same real object - brand new spy!
doReturn(false).when(wareHouseSpy).isSomethingMissing();
}
#Test
...
#Test
...
#Test
...
}
Maybe I am not following but when you have a real object real:
Object mySpy = spy(real);
Then to "unspy" mySpy... just use real.
As per the documentation, we have
reset(mock);
//at this point the mock forgot any interactions & stubbing
The documentation specifies further
Normally, you don't need to reset your mocks, just create new mocks
for each test method. Instead of #reset() please consider writing
simple, small and focused test methods over lengthy, over-specified
tests.
Here's an example from their github repo which tests this behavior and uses it:
#Test
public void shouldRemoveAllInteractions() throws Exception {
mock.simpleMethod(1);
reset(mock);
verifyZeroInteractions(mock);
}
reference : ResetTest.java
Addressing this piece specifically:
Is there a way, just for the sake of cleaning up after me in case some other tests that run after my tests also use the same instances and might execute a mocked method they didn't ask to mock, to un-mock a method?
If you are using JUnit, the cleanest way to do this is to use #Before and #After (other frameworks have equivalents) and recreate the instance and the spy so that no test depends on or is impacted by whatever you have done on any other test. Then you can do the test-specific configuration of the spy/mock inside of each test. If for some reason you don't want to recreate the object, you can recreate the spy. Either way, everyone starts with a fresh spy each time.

Categories

Resources