Adding own annotation to dynamic skip test in TestNG - java

I would like to provide elegant mechanism to skip chosen tests when value of some environmental variable is not admissible. I chose adding my own annotation #RunCondition to define which value are allowed for particular tests. Then I created my own listener for TestNG that marks tests as disabled when value of environmental variable is not within admissible scope defined in annotation parameters.
My code looks as follows:
public class ExampleTest {
private int envVar;
#BeforeClass
public void setUp() {
//set up of some environmental variables which depends on external source
StaticContext.setVar(getValueFromOuterSpace());
}
#RunCondition(envVar=2)
#Test
public void testFoo(){
}
}
public class SkipTestTransformer implements IAnnotationTransformer {
#Override
public void transform(ITestAnnotation iTestAnnotation, Class aClass, Constructor constructor, Method method) {
RunCondition annotation = method.getAnnotation(RunCondition.class);
int[] admissibleValues = annotation.envVar();
for (int val : admissibleValues) {
if (StaticContext.getVar() == val) {
return; // if environmental variable matches one of admissible values then do not skip
}
}
iTestAnnotation.setEnabled(false);
}
}
public #interface RunCondition {
int[] envVar();
}
My code works great, but there is a small problem that transform method is invoked before the setUp which is the #BeforeClass function. Is there any other possibility to run Transformer after all initialization of test? I consider such solution elegant and clear and I don't want any ugly if clauses to reach my goal...
I'm using Java 7 and TestNG v5.11.

Try to implement IMethodInterceptor (An instance of this class will be invoked right before TestNG starts invoking test methods.) instead of annotation transformer. It will allow to manage list of tests which will be executed. It also allows to work with your tests annotations. The restriction is that test methods having dependencies will not be passed to intercept method.

There is a better concept directly supported by the testing frameworks called assumptions. You should not disable the test, but rather skip the execution:
in JUnit you can use assumeThat(boolean) family of methods
in TestNG you can throw SkipException
In that case the method will not disappear, it will be marked as skipped.

You can check your own annotation in a setup method (#BeforeMethod) and throw a SkipException to skip this test.
public class ExampleTest {
private int envVar;
#BeforeClass
public void setUp() {
//set up of some environmental variables which depends on external source
StaticContext.setVar(2);
}
#BeforeMethod
public void checkRunCondition(Method method) {
RunCondition annotation = method.getAnnotation(RunCondition.class);
if (annotation != null) {
int[] admissibleValues = annotation.envVar();
for (int val : admissibleValues) {
if (StaticContext.getVar() == val) {
// if environmental variable matches one of admissible values then do not skip
throw new SkipException("skip because of RunCondition");
}
}
}
}
#RunCondition(envVar = 2)
#Test
public void testFoo() {
}
#Retention(RetentionPolicy.RUNTIME)
public #interface RunCondition {
int[] envVar();
}
}

Related

Mockito - verify a method with Callable<> as parameter [duplicate]

I have a simple scenario in which am trying to verify some behavior when a method is called (i.e. that a certain method was called with given parameter, a function pointer in this scenario). Below are my classes:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
AppBootStrapper bootStrapper = context.getBean(AppBootStrapper.class);
bootStrapper.start();
}
}
#Component
public class AppBootStrapper {
private NetworkScanner networkScanner;
private PacketConsumer packetConsumer;
public AppBootStrapper(NetworkScanner networkScanner, PacketConsumer packetConsumer) {
this.networkScanner = networkScanner;
this.packetConsumer = packetConsumer;
}
public void start() {
networkScanner.addConsumer(packetConsumer::consumePacket);
networkScanner.startScan();
}
}
#Component
public class NetworkScanner {
private List<Consumer<String>> consumers = new ArrayList<>();
public void startScan(){
Executors.newSingleThreadExecutor().submit(() -> {
while(true) {
// do some scanning and get/parse packets
consumers.forEach(consumer -> consumer.accept("Package Data"));
}
});
}
public void addConsumer(Consumer<String> consumer) {
this.consumers.add(consumer);
}
}
#Component
public class PacketConsumer {
public void consumePacket(String packet) {
System.out.println("Packet received: " + packet);
}
}
#RunWith(JUnit4.class)
public class AppBootStrapperTest {
#Test
public void start() throws Exception {
NetworkScanner networkScanner = mock(NetworkScanner.class);
PacketConsumer packetConsumer = mock(PacketConsumer.class);
AppBootStrapper appBootStrapper = new AppBootStrapper(networkScanner, packetConsumer);
appBootStrapper.start();
verify(networkScanner).addConsumer(packetConsumer::consumePacket);
verify(networkScanner, times(1)).startScan();
}
}
I want to verify that bootStrapper did in fact do proper setup by registering the packet consumer(there might be other consumers registered later on, but this one is mandatory) and then called startScan. I get the following error message when I execute the test case:
Argument(s) are different! Wanted:
networkScanner bean.addConsumer(
com.spring.starter.AppBootStrapperTest$$Lambda$8/438123546#282308c3
);
-> at com.spring.starter.AppBootStrapperTest.start(AppBootStrapperTest.java:24)
Actual invocation has different arguments:
networkScanner bean.addConsumer(
com.spring.starter.AppBootStrapper$$Lambda$7/920446957#5dda14d0
);
-> at com.spring.starter.AppBootStrapper.start(AppBootStrapper.java:12)
From the exception, clearly the function pointers aren't the same.
Am I approaching this the right way? Is there something basic I am missing? I played around and had a consumer injected into PacketConsumer just to see if it made a different and that was OK, but I know that's certainly not the right way to go.
Any help, perspectives on this would be greatly appreciated.
Java doesn't have any concept of "function pointers"; when you see:
networkScanner.addConsumer(packetConsumer::consumePacket);
What Java actually compiles is (the equivalent of):
networkScanner.addConsumer(new Consumer<String>() {
#Override void accept(String packet) {
packetConsumer.consumePacket(packet);
}
});
This anonymous inner class happens to be called AppBootStrapper$$Lambda$7. Because it doesn't (and shouldn't) define an equals method, it will never be equal to the anonymous inner class that the compiler generates in your test, which happens to be called AppBootStrapperTest$$Lambda$8. This is regardless of the fact that the method bodies are the same, and are built in the same way from the same method reference.
If you generate the Consumer explicitly in your test and save it as a static final Consumer<String> field, then you can pass that reference in the test and compare it; at that point, reference equality should hold. This should work with a lambda expression or method reference just fine.
A more apt test would probably verify(packetConsumer, atLeastOnce()).consumePacket(...), as the contents of the lambda are an implementation detail and you're really more concerned about how your component collaborates with other components. The abstraction here should be at the consumePacket level, not at the addConsumer level.
See the comments and answer on this SO question.

Junit categories - which category I am in?

I am using JUNIT's #categories and want to check in a method which category I am in.
for example
if (category.name == "sanity")
//do something
Is there any way to do that?
I want to avoid having to pass a parameter to this method because I have over 800 calls to it in the project
I believe you can do that the same way that can be used to determine if any other class has specific annotation and its values - use Java reflection mechanism.
As a quick example for your specific case you can make it like this:
#Category(Sanity.class)
public class MyTest {
#Test
public void testWhatever() {
if (isOfCategory(Sanity.class)) {
// specific actions needed for any tests that falls into Sanity category:
System.out.println("Running Sanity Test");
}
// test whatever you need...
}
private boolean isOfCategory(Class<?> categoryClass) {
Class<? extends MyTest> thisClass = getClass();
if (thisClass.isAnnotationPresent(Category.class)) {
Category category = thisClass.getAnnotation(Category.class);
List<Class<?>> values = Arrays.asList(category.value());
return values.contains(categoryClass);
}
return false;
}
}

How to test OS-specific method with JUnit?

I would like to test the following method with JUnit:
private static boolean systemIsWindows() {
String os = System.getProperty("os.name").toLowerCase();
return os.startsWith("win");
}
Frankly, the only thing I've come up with is to basically copy to same logic to the test. This would, of course, protect against the method being inadvertently broken, but sounds somehow counter-intuitive.
What would be a better way to test this method?
In your Unit tests, you can change the value of the property:
System.setProperty("os.name", "Linux")
After that, you can then test/call your systemIsWindows() method to check that what it returns using asserts.
To make it easier to set a System property and to unset that property on completion of the test (thereby facilitating test isolation, self containment) you could use either of the following JUnit add-ons:
JUnit4: JUnit System Rules
JUnit5: JUnit Extensions
For example:
#Test
#SystemProperty(name = "os.name", value = "Windows")
public void aTest() {
assertThat(systemIsWindows(), is(true));
}
#Test
#SystemProperty(name = "os.name", value = "MacOs")
public void aTest() {
assertThat(systemIsWindows(), is(false));
}
A much better way in JUnit 5 is to use #EnabledOnOs https://junit.org/junit5/docs/5.2.0/api/org/junit/jupiter/api/condition/EnabledOnOs.html
So for example:
#Test
#EnabledOnOs({OS.WINDOWS})
public void aTest() {
assertThat(systemIsWindows(), is(false));
}

Execute order for test suite in junit

I am having a test suite which is having the following structure
TestClass1
- testmethod1()
- testmethod2()
- testmethod3()
- testmethod4()
TestClass2
- testmethod11()
- testmethod22()
- testmethod33()
- testmethod44()
In the above structure i want to execute the testmethod4() as the final one. ie) executed at last.
There is a annotation #FixMethodOrder which executes a method in order not the testclass. Is there any mechanism to maintain order in test class and testmethod together. With the #FixMethodOrder i can execute the method by renaming the name of the test method but i can't instruct junit to execute the test class as the final one(last one).
Though quoting #Andy again -
You shouldn't care about test ordering. If it's important, you've got
interdependencies between tests, so you're testing behaviour +
interdependencies, not simply behaviour. Your tests should work
identically when executed in any order.
But if the need be to do so, you can try out Suite
#RunWith(Suite.class)
#Suite.SuiteClasses({
TestClass2.class,
TestClass1.class
})
public class JunitSuiteTest {
}
where you can either specify
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestClass1 {
#AfterClass
public void testMethod4() {
and then take care to name your method testMethod4 as such to be executed at the end OR you can also use #AfterClass which could soon be replaced by #AfterAll in Junit5.
Do take a look at Controlling the Order of the JUnit test by Alan Harder
#shiriam as #Andy Turner already pointed out, the order of your tests shouldn't come in question when running the tests.
If you have a routine that you want executed before doing any tests, you could use a static block of code in one of the classes.
Think of something like this:
class TestBootstrap {
// singleton instance
private static final instance;
private boolean initialized;
private TestBootstrap(){
this.initialized = false;
}
public static TestBootstrap getInstance(){
if (instance == null){
instance = new TestBootstrap()
}
}
public void init(){
// make the method idempotent
if (!initialzed){
// do init stuff
initialized = true;
}
}
public boolean isInitialized(){
return initialized;
}
}
Then in your tests use something like this:
class TestClass1{
#BeforeClass
public void setup(){
TestBootstrap.getInstance().init();
}
#Test
public void testmethod1(){
// assertions
}
// ....
}
class TestClass2{
#BeforeClass
public void setup(){
TestBootstrap.getInstance().init();
}
#Test
public void testmethod11(){
// assertions
}
// ...
}
By using the singleton instance for doing the setup for the tests you ensure that you perform the initialization of your test environment only once, independently of the order in which the test classes are executed.

Passing JUnit data between tests

I just discovered when creating some CRUD tests that you can't set data in one test and have it read in another test (data is set back to its initialization between each test).
All I'm trying to do is (C)reate an object with one test, and (R)ead it with the next. Does JUnit have a way to do this, or is it ideologically coded such that tests are not allowed to depend on each other?
Well, for unit tests your aim should be to test the smallest isolated piece of code, usually method by method.
So testCreate() is a test case and testRead() is another. However, there is nothing that stops you from creating a testCreateAndRead() to test the two functions together. But then if the test fails, which code unit does the test fail at? You don't know. Those kind of tests are more like integration test, which should be treated differently.
If you really want to do it, you can create a static class variable to store the object created by testCreate(), then use it in testRead().
As I have no idea what version of Junit you talking about, I just pick up the ancient one Junit 3.8:
Utterly ugly but works:
public class Test extends TestCase{
static String stuff;
public void testCreate(){
stuff = "abc";
}
public void testRead(){
assertEquals(stuff, "abc");
}
}
JUnit promotes independent tests. One option would be to put the two logical tests into one #Test method.
TestNG was partly created to allow these kinds of dependencies among tests. It enforces local declarations of test dependencies -- it runs tests in a valid order, and does not run tests that depend on a failed test. See http://testng.org/doc/documentation-main.html#dependent-methods for examples.
JUnit is independent test. But, If you have no ways, you can use "static" instance to store it.
static String storage;
#Test
public void method1() {
storage = "Hello"
}
#Test
public void method2() {
Assert.assertThat(something, is(storage));
}
How much processing time do these tests take? If not a lot, then why sweat it. Sure you will create some object unnecessarily, but how much does this cost you?
#Test
void testCreateObject() {
Object obj = unit.createObject();
}
#Test
void testReadObject() {
Object obj = null;
try {
obj = unit.createObject(); // this duplicates tests aleady done
} catch (Exception cause) {
assumeNoException(cause);
}
unit.readObject(obj);
}
in this basic example, the variable is changed in the test A, and can be used in the test B
public class BasicTest extends ActivityInstrumentationTestCase2 {
public BasicTest() throws ClassNotFoundException {
super(TARGET_PACKAGE_ID, launcherActivityClass);
}
public static class MyClass {
public static String myvar = null;
public void set(String s) {
myvar = s;
}
public String get() {
return myvar;
}
}
private MyClass sharedVar;
#Override
protected void setUp() throws Exception {
sharedVar = new MyClass();
}
public void test_A() {
Log.d(S,"run A");
sharedVar.set("blah");
}
public void test_B() {
Log.d(S,"run B");
Log.i(S,"sharedVar is: " + sharedVar.get());
}
}
output result is:
run A
run B
sharedVar is: blah

Categories