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?
Related
I am trying to create extensions A_Extension and B_Extension, which will get executed only once at start and at the end of all test classes.
Where as, I want to call C_Extension always in each test class.
Here are my extensions and test classes.
public class A_Extension implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {
private static boolean isInitialized = false;
#Override
public void beforeAll(ExtensionContext context) throws Exception {
if (!isInitialized) {
//Do something and return ABC
isInitialized = true;
context.getRoot().getStore(GLOBAL).put("ABC", ABC);
}
}
#Override
public void close() throws Throwable {
ABC = null;
}
}
public class B_Extension implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {
private static boolean isInitialized = false;
#Override
public void beforeAll(ExtensionContext context) throws Exception {
if (!isInitialized) {
//Do something and return PQR
isInitialized = true;
context.getRoot().getStore(GLOBAL).put("PQR", PQR);
}
}
#Override
public void close() throws Throwable {
PQR = null;
}
}
public class C_Extension implements AfterEachCallback, AfterAllCallback, BeforeAllCallback {
//some extension code
}
#ExtendWith({A_Extension.class,B_Extension.class})
class TestClass1 {
#RegisterExtension
static C_Extension cExtension = new C_Extension();
#BeforeAll
static void beforeAllClass() { }
#AfterAll
static void afterAllClass() { }
#Test
void case1() { }
#Test
void case2() { }
}
#ExtendWith({A_Extension.class,B_Extension.class})
class TestClass2 {
#RegisterExtension
static C_Extension cExtension = new C_Extension();
#BeforeAll
static void beforeAllClass() { }
#AfterAll
static void afterAllClass() {}
#Test
void case1() {}
#Test
void case2() {}
}
But A_Extension.class and B_Extension.class are getting called always in each test case.
What can be the reason behind this?
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).
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);
}
}
Say I have this dependency in a Spring #Configuration:
#Bean
public SomeClass someClass(SomeClass1 someClass1, SomeClass2 someClass2, ...) {
return new SomeClass(someClass1, someClass2, ...);
}
Say I want do do something in #PostConstruct that includes someClass dependency:
#PostConstruct
public void init() {
someClass.doSomething();
}
This cannot be injected:
#PostConstruct
public void init(SomeClass someClass) {
someClass.doSomething();
}
causes:
Caused by: java.lang.IllegalStateException: Lifecycle method annotation requires a no-arg method: ...
This cannot be autowired in the same config like this:
#Autowire
private SomeClass someClass;
#Bean
public SomeClass someClass(SomeClass1 someClass1, SomeClass2 someClass2, ...) {
return new SomeClass(someClass1, someClass2, ...);
}
as that leads to:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'globalBus': Requested bean is currently in creation: Is there an unresolvable circular reference?
A config can be split (so #Bean goes to the other config) and #Import-ed by this one and it works OK. Probably other solutoins exist - e.g. creating a separate initialization bean or so.
Is there a way to do this within one #Configuration?
Edit
As requested by #SotiriosDelimanolis, a sscce for the exception when using #Autowired:
public class ConfigPostConstructDependenciesPrb {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
ctx.getBean(Service.class);
ctx.close();
}
public static class Service {
private final Dependency dependency;
public Service(Dependency dependency) {
this.dependency = dependency;
}
public void work() {
System.out.println(dependency.getNum());
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Service [dependency=");
sb.append(dependency);
sb.append("]");
return sb.toString();
}
}
public static class Dependency {
private final int num;
public Dependency(int num) {
this.num = num;
}
public int getNum() {
return this.num;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("SomeClass1 [num=");
sb.append(num);
sb.append("]");
return sb.toString();
}
}
#Configuration
public static class BaseConfig {
#Autowired
private Service service;
#Bean
public Dependency dependency() {
return new Dependency(42);
}
#Bean
public Service service(Dependency dependency) {
return new Service(dependency);
}
#PostConstruct
public void init() {
service.work();
}
}
#Configuration
#Import(BaseConfig.class)
public static class Config {
#Autowired
private Service service;
}
}
(Tested in Spring 4.3.6)
Create a nested class inside your #Configuration and put there declarations of #Autowired service and #PostConstruct init():
#Configuration
public static class BaseConfig {
//...
#Bean
public Service service(Dependency dependency) {
return new Service(dependency);
}
#Configuration
public static class Setup {
#Autowired
private Service service;
#PostConstruct
public void init() {
service.work();
}
}
}
Below is your full example updated accordingly.
Notice that you don't have to add explicit reference to BaseConfig.Setup (look at the #Import annotation before Config class - it only refers to BaseConfig itself).
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import javax.annotation.PostConstruct;
public class ConfigPostConstructDependenciesPrb {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
ctx.getBean(Service.class);
ctx.close();
}
public static class Service {
private final Dependency dependency;
public Service(Dependency dependency) {
this.dependency = dependency;
}
public void work() {
System.out.println(dependency.getNum());
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Service [dependency=");
sb.append(dependency);
sb.append("]");
return sb.toString();
}
}
public static class Dependency {
private final int num;
public Dependency(int num) {
this.num = num;
}
public int getNum() {
return this.num;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("SomeClass1 [num=");
sb.append(num);
sb.append("]");
return sb.toString();
}
}
#Configuration
public static class BaseConfig {
#Bean
public Dependency dependency() {
return new Dependency(42);
}
#Bean
public Service service(Dependency dependency) {
return new Service(dependency);
}
#Configuration
public static class Setup {
#Autowired
private Service service;
#PostConstruct
public void init() {
service.work();
}
}
}
#Configuration
#Import(BaseConfig.class)
public static class Config {
#Autowired
private Service service;
}
}
Try this way:
public class ConfigPostConstructDependenciesPrb {
public static void main(String[] args) {
try {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(BaseConfig.class);
ctx.registerShutdownHook();
ctx.getBean(Service.class);
ctx.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
#Configuration
class BaseConfig {
#Autowired
private Service service;
#Bean
public Dependency dependency() {
return new Dependency(42);
}
#Bean
public Service service(Dependency dependency) {
return new Service(dependency);
}
#PostConstruct
public void init() {
this.service.work();
}
}
class Dependency {
private int num;
public Dependency() {
}
public Dependency(int num) {
this.num = num;
}
public int getNum() {
return this.num;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("SomeClass1 [num=");
sb.append(num);
sb.append("]");
return sb.toString();
}
}
class Service {
private Dependency dependency;
public Service() {
}
public Service(Dependency dependency) {
this.dependency = dependency;
}
public void work() {
System.out.println(dependency.getNum());
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Service [dependency=");
sb.append(dependency);
sb.append("]");
return sb.toString();
}
}
//registry Class
public class Registry
{
private static final Logger logger = LogFactory
.getLogger(Registry.class);
protected Service service = new serviceMLImpl();
private static Registry instance = new Registry();
public static Registry getInstance()
{
return instance;
}
public static void setInstance(Registry instance)
{
Registry.instance = instance;
}
public Helper getHelper()
{
return new Helper();
}
}
//Helper Class
public class Helper
{
protected Service service = ServiceRegistry
.getService();
public Helper()
{
}
public void sendInfo(Info info)
throws Exception
{
service.sendInfo(info);
}
public void sendFacilitySFPListNotification(
List<GcpFacilitySFPInfo> esfpPortList)
{
gfpGcpService.sendFacilitySFPListNotification(esfpPortList);
}
}
//Test Class
public class RegistryTest
{
Registry registry = new Registry();
Helper helper = createMock(Helper.class);
#Before
public void setUp() throws Exception
{
}
#After
public void tearDown() throws Exception
{
registry = null;
}
#Test
public void testGetInstance()
{
replayAll();
registry.getInstance();
verifyAll();
}
#Test
public void testSetInstance()
{
replayAll();
registry.setInstance(registry);
verifyAll();
}
#Test
public void testGetHelper()
{
**replayAll();
new Helper();
registry.getHelper(); // getting InitializationExceptionError here
verifyAll();** //
}
}
Two classes are there, Helper and Registry class. I tried to write the junit for registry class but got InitializationException in the test class.
This is the error i am getting at registry.getHelper():
java.lang.ExceptionInInitializerError at java.lang.Class.forName0(Native Method) at
java.lang.Class.forName(Class.java:169) at
javassist.runtime.Desc.getClassObject(Desc.java:44) at
javassist.runtime.Desc.getClassType(Desc.java:153) at
javassist.runtime.Desc.getType(Desc.java:123) at
javassist.runtime.Desc.getType(Desc.java:79) at
com.att.bbnms.cim.gfpgcp.service.ServiceRegistry.getService(ServiceRegistry.java:41) at
com.att.bbnms.cim.nc.service.link.Helper.(Helper.java:14) at
com.att.bbnms.cim.nc.service.link.Registry.getHelper(Registry.java:26) at
com.att.bbnms.cim.nc.service.link.egistryTest.testGetHelper(RegistryTest.java:63) at
org.junit.internal.runners.TestMethod.invoke(TestMethod.java:66) at
org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:312) at
org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:86) at
org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:94) at
org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296) at
Also while writing the test case for Helper , I am not able to get in the class.
Please tell if you have any solution.