I made my own JUnit-Runner by implementing org.junit.runner.Runner, so that I can run my UnitTests with them using the #RunWith-Annotation.
It lookes somewhat like this:
public class MyRunner extends Runner {
private Context myContext;
myContext.init();
private final BlockJUnit4ClassRunner runner;
public MyRunner(final Class<?> clazz) throws InitializationError {
myContext = new Context();
runner = new BlockJUnit4ClassRunner(clazz);
}
#Override
public void run(final RunNotifier notifier) {
runner.run(notifier);
}
#Override
public Description getDescription() {
return runner.getDescription();
}
public void filter(final Filter filter) throws NoTestsRemainException {
runner.filter(filter);
}
}
To clean up resources, I have to shut down MyContext by calling MyContext.close(). Where should I invoke this so that my resources are cleand up after the tests have run?
I'm not sure what you're trying to achive but have you already had a look at JUnit's Rules?
public class MyContextRule extends ExternalResource {
private final Context myContext;
public MyContextRule() {
myContext = new Context();
}
#Override
protected void before() throws Throwable {
myContext.init();
}
#Override
protected void after() {
myContext.close();
}
}
Usage:
public class MyTest {
#ClassRule
public static MyContextRule contextRule = new MyContextRule();
//...
}
JUnit Rules advantage over Runners is that you can have multiple of them, while you only can have one runner.
So, your custom Rule could be used with any runner that may be introduced by a random testframework that you may come across in the future...
Where should I invoke this so that my resources are cleand up after
the tests have run ?
UPDATED MY ANSWER, you can use org.junit.runner.notification.RunListener as shown below:
(1) Create your own RunListener class:
public class MyRunnerListener extends RunListener {
private Context context;
public MyRunnerListener(Context context) {
this.context = context;
}
void testRunFinished(Result result) {
context.close();
}
}
(2) Use the MyRunnerListener inside MyRunner :
public class MyRunner extends Runner {
private Context myContext;
MyRunnerListener runnerListener;
private final BlockJUnit4ClassRunner runner;
public MyRunner(final Class<?> clazz) throws InitializationError {
myContext = new Context();
myContext.init();
runnerListener = new MyRunnerListener(myContext);
runner = new BlockJUnit4ClassRunner(clazz);
}
#Override
public void run(final RunNotifier notifier) {
notifier.addListener(runnerListener);
runner.run(notifier);
}
#Override
public Description getDescription() {
return runner.getDescription();
}
public void filter(final Filter filter) throws NoTestsRemainException {
runner.filter(filter);
}
}
P.S.: If you don't want to use the Runner, then you can follow the answer from Markus (which uses TestRule, NOT TestRunner).
Related
when i use PowerMockito to test a method,the code is
public class RedisLockUtil {
private static final StringRedisTemplate STRING_REDIS_TEMPLATE = SpringContextUtil.getBean(StringRedisTemplate.class);
public static boolean getLock(String lockId, long second) {
log.info("lock by key:[{}],last :[{}] s", lockId, second);
Boolean success = STRING_REDIS_TEMPLATE.opsForValue().setIfAbsent(lockId, "lock",
second, TimeUnit.SECONDS);
return success != null && success;
}
}
#Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
#RunWith(PowerMockRunner.class)
#PrepareForTest({RedisLockUtil.class})
public class Mock {
#Before
public void setUp() {
PowerMockito.mockStatic(RedisLockUtil.class);
MockitoAnnotations.initMocks(this);
}
#Test
public void test(){
RedisLockUtil.getLock("test",1L);
}
}
i need to test the createUserLockKey method,but the STRING_REDIS_TEMPLATE need to be initialized。but i dont konw how to set ApplicationContext,so the method-getApplicationContext() will return null,and when RedisLockUtil invoke the getBean,it will throw NullPointException.
so how can i make the ApplicationContext not null?
I am writing a new Android lint rule which need to visit all method invocations and then it analyzes annotation of each method to decide handling. However, I am getting trouble which very first step: visit all method invocations.
In conclusion, all I want is that when I have the code below, the 2 final lines (test.testMethod(); and testMethod();) are highlighted as error by Android Studio.
public class MyClass {
class TestClass {
void testMethod() {}
}
void testMethod() {}
void testCall() {
TestClass test = new TestClass();
test.testMethod(); // expect highlight
testMethod(); // expect highlight
}
}
In the old version of lint framework I think I can achieve this by:
public class MyDetector extends Detector implements Detector.JavaScanner {
#Override
public
List<Class<? extends Node>> getApplicableNodeTypes() {
//noinspection unchecked
return Arrays.<Class<? extends Node>>asList(MethodInvocation.class);
}
#Nullable
#Override
public AstVisitor createJavaVisitor(#NonNull JavaContext context) {
return new CallVisitor(context);
}
private class CallVisitor extends ForwardingAstVisitor {
private final JavaContext mContext;
public CallVisitor(JavaContext context) {
mContext = context;
}
#Override
public boolean visitMethodInvocation(#NonNull MethodInvocation call) {
String message = "Nguyen Ha Quang";
mContext.report(THREAD, call, mContext.getLocation(call), message);
return false;
}
}
}
This code is referenced from https://cs.android.com/android/platform/superproject/+/master:tools/base/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java;bpv=0 (line 1516 -> end).
Unfortunately, since AndroidX was released, this code is no longer work. Now, I must try to do something like this:
public class ChatWorkerDetector extends Detector implements Detector.UastScanner {
#Nullable
#Override
public List<Class<? extends UElement>> getApplicableUastTypes() {
return Arrays.<Class<? extends UElement>>asList(
UCallableReferenceExpression.class
);
}
#Override
public UElementHandler createUastHandler(JavaContext context) {
return new CustomElementHandler(context);
}
public class CustomElementHandler extends UElementHandler {
JavaContext mContext;
public CustomElementHandler(JavaContext mContext) {
this.mContext = mContext;
}
#Override
public void visitCallableReferenceExpression(UCallableReferenceExpression node) {
String message = "Nguyen Ha Quang";
mContext.report(THREAD, node, mContext.getLocation(node), message);
}
}
}
This code still does not work as I expect and I also have tried: visitCallableReferenceExpression, visitMethod, visitCallExpression, but none of them work.
Is any visitABC function available in UElementHandler class which can do my job?
UElementHandler source code: https://github.com/ouyangpeng/android-lint-checks-studio3.0/blob/master/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UElementHandler.java
Thank you very much.
In unit custom runner I want to perform action before and after running the test action,
so I came around with this solution.
how solid is doing it that way, is there a more clean way to achieve that?
public class SomeCustomRunner extends BlockJUnit4ClassRunner {
private int m_testMethodIndex = 0;
private int m_testMethodsCount = 0;
private boolean m_sessionSetup = false;
#Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
if(m_sessionSetup == false) {
m_sessionSetup = true;
beforeTestClass(); //->> DO MY STUFF HERE
}
super.runChild(method, notifier);
m_testMethodIndex++;
if(m_testMethodIndex == m_testMethodsCount) {
afterTestClass(); //->> DO MY STUFF HERE
}
}
#Override
protected List<FrameworkMethod> getChildren() {
List<FrameworkMethod> methods = super.getChildren();
m_testMethodsCount = methods.size();
return methods;
}
}
Instead of creating a separate test runner, you can define the actions to perform before and after in the test class itself in methods annotated with #BeforeClass resp. #AfterClass.
To reuse them in more than one test you can easily inherit them from a base class.
the easiest way is override the run method as below:
public class LifecycleRunner extends BlockJUnit4ClassRunner {
public LifecycleRunner(Class<?> klass) throws InitializationError {
super(klass);
}
#Override
public void run(RunNotifier notifier) {
beforeRunTests();
try {
super.run(notifier);
} finally {
afterRunTests();
}
}
private void afterRunTests() {
trace();
}
private void beforeRunTests() {
trace();
}
private void trace() {
System.out.println(Thread.currentThread().getStackTrace()[2]);
}
}
To gain full control over test execution, install a proper RunListener in your runner.
#Override
public void run(final RunNotifier notifier)
{
final RunListener listener = new RunListener()
{
#Override
public void testStarted(final Description description) throws Exception
{
// do something before each (non-ignored) Test method is executed
}
#Override
public void testFinished(final Description description) throws Exception
{
// do something after each Test method was performed (failed or successful)
}
// check further listener methods yourself!
};
// install your listener
notifier.addListener(listener);
super.run(notifier);
// and remove it again
notifier.removeListener(listener);
}
I'm converting from JUnit to TestNG and facing a issue moving away from the #Rule annotation in JUnit.
In my superclass there is a #Rule, when I change this to #BeforeMethod it simply does not go into that method. (using that Println to confirm)
Also, when i do simply change #Rule or #ClassRule to #BeforeMethod, Eclipse complains that it is 'disallowed at this location'.
(Note I've removed some methods in the below to make the code length a little shorter/easier to view).
public abstract class AbstractTests
{
protected static boolean oneTimeSetUpComplete;
private static Logger log;
private static WebSessionFactory sessionFactory;
private static WcConfigManager config;
private WebSession session;
TestLogger testLogger = new TestLogger(getConfig(), getLog());
private StringBuilder errors = new StringBuilder();
protected WteDataProvider data;
#ClassRule
public static ExternalResource mainConfiguration = new ExternalResource() {
protected void before() throws Throwable {
setUpTests();
};
};
public Verifier errorCollector = new Verifier(){
#Override
protected void verify() throws Throwable {
failTestIfThereAreErrors();
};
};
public TestLogger getTestLogger() {
return testLogger;
}
#Rule
public RuleChain executionOrder() {
return RuleChain.outerRule(getTestLogger()).around(errorCollector);
}
#Rule
public TestRule dataReader = new TestRule() {
public Statement apply(final Statement base,
final Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
System.out.println("PRINT IF YOU GO IN HERE");
DataProvider dataProvider = description.getAnnotation(DataProvider.class);
if (dataProvider == null) {
dataProvider = description.getTestClass().getAnnotation(DataProvider.class);
}
base.evaluate();
};
};
};
};
protected AbstractArgosTests(
Logger logger,
WcConfigManager configManager,
WebSessionFactory factory)
{
setUpTests(logger, configManager, factory);
}
}
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
}
}