Reading custom annotation for JUNIT - java

I have a custom annotation which I use as config to start off one time set-up for Junit.
#Target(TYPE) #Retention(RUNTIME)
public #interface MyAnnotation{
String host();
int port();
}
Test class:
#MyAnnotation(host="0.0.0.0", port=4567)
public class MyTest extends MyAbstractClass{
#Test
public void myTest(){
//do testy things
}
}
Superclass:
public class MyAbstractClass{
#BeforeAll
public static void start(){
Config cfg = readConfig();
//Use config to do one time set-up
}
private static Config readConfig(){
MyAnnotation ann = MyTest.class.getAnnotation(MyAnnotation.class);
return new Config(ann.host(), ann.port());
}
}
So currently, I hardcode the name of the test class (MyTest) in readConfig(..).
This won't work when I add a second test class.
One way to solve it is:
Add another #BeforeAll method in MyTest which will call the #BeforeAll in super-class and pass the class name as a param.
However, I am curious if I can read the name of the executing subclass in the superclass via some reflexion magic.
Any ideas are most welcome.
Thanks

The presence of the #BeforeAll annotation suggests you are using JUnit 5. In this case, you can use.
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.TestInfo;
public class MyAbstractClass {
#BeforeAll
public static void start(TestInfo ti) {
Config cfg=readConfig(ti.getTestClass().orElseThrow(IllegalStateException::new));
//Use config to do one time set-up
}
private static Config readConfig(Class<?> testClass) {
MyAnnotation ann = testClass.getAnnotation(MyAnnotation.class);
return new Config(ann.host(), ann.port());
}
}
See also the TestInfo API documentation.
This is not “Reflection Magic” but a feature provided by JUnit itself, but it’s also only JUnit which knows that the invocation of a static method annotated with #BeforeAll is associated with a particular test class it is going to process.

Related

JUnit5: access extension field from test class

I need to use extensions to run code before and after all test cases in classes that use it. My test classes need to access a field in my Extension class. Is this possible?
Given:
#ExtendWith(MyExtension.class)
public class MyTestClass {
#Test
public void test() {
// get myField from extension and use it in the test
}
}
and
public class MyExtension implements
BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
private int myField;
public MyExtension() {
myField = someLogic();
}
...
}
How do I access myField from my test class?
You can achieve this via a marker annotation and a BeforeEachCallback extension.
Create a special marker annotation, e.g.
#Documented
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyField {
}
Use the annotation to find and set the values from within the extension:
import org.junit.jupiter.api.extension.BeforeEachCallback;
public class MyExtension implements BeforeEachCallback {
#Override
public void beforeEach(final ExtensionContext context) throws Exception {
// Get the list of test instances (instances of test classes)
final List<Object> testInstances =
context.getRequiredTestInstances().getAllInstances();
// Find all fields annotated with #MyField
// in all testInstances objects.
// You may use a utility library of your choice for this task.
// See for example, https://github.com/ronmamo/reflections
// I've omitted this boilerplate code here.
// Assign the annotated field's value via reflection.
// I've omitted this boilerplate code here.
}
}
Then, in your tests, you annotate the target field and extend the test with your extension:
#ExtendWith(MyExtension.class)
public class MyTestClass {
#MyField
int myField;
#Test
public void test() {
// use myField which has been assigned by the extension before test execution
}
}
Note: you can alternatively extend BeforeAllCallback which is executed once before all test methods of the class, depending on your actual requirements.

TestNG Listener equivalent for #BeforeClass, with access to context

I've spent several days looking for a way to move one of my #BeforeClass methods to listener class I can reference in xml where I define content os test suite.
Problem I'm facing is that I'm using Spring for DI, and in #BeforeClass method I add some attributes to testng context, so I can use them in other places (other listeners).
I tried using onStart(final ITestContext context) from ITestListener. But that method seems to be invoked before spring manages to create beans, and I cannot perform my operations, because all my beans are nulls.
I tried using onBeforeClass(ITestClass testClass) from IClassListener. But that method only provides ITestClass, which does not give me access to context, so I can't set my attributes.
Now I'm experimenting with onConfigurationSuccess(final ITestResult itr) from IConfigurationListener, but that requires using if statement to run my code only if configuration method name is equal to springTestContextPrepareTestInstance.
Does anyone know a better way of doing this?
[EDIT] code sample
#Component
public class CleanupHelper {
private static SomeBean someBean;
#Autowired
public CleanupHelper(SomeBean someBean){
CleanupHelper.someBean = someBean;
}
public static Object getSomething(){
return someBean.getSomething();
}
}
public class ExcludedGroupsListener implements IConfigurationListener {
#Override
public void onConfigurationSuccess(final ITestResult itr) {
if (itr.getName().contains("springTestContextPrepareTestInstance")) {
var something = CleanupHelper.getSomething();
if (something != null && someOtherCondition) {
itr.setAttribute("someObject", something);
}
}
}
}
#ContextConfiguration(classes = TestConfig.class)
public class SomeTests extends AbstractTestNGSpringContextTests {
#Test
public void someTest(){
// doSomething
}
}
#Configuration
#ComponentScan(basePackages = "com.some",
excludeFilters = #Filter(type = FilterType.REGEX, pattern = "com.some.else..*"))
public class TestConfig {
}
Above code works... unfortunately onConfigurationSuccess method is invoked after each configuration method.
Try with Annotation Transformers.
You can add it in your testng.xml like any other listener.
And in there you can do things like:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.IAnnotationTransformer;
import org.testng.annotations.ITestAnnotation;
public class TestAnnotationTransformer implements IAnnotationTransformer {
#SuppressWarnings("rawtypes")
#Override
public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
if (testMethod.getName().equals("MyTest1"))
annotation.setGroups( new String[] {"GroupA" });
if(ignoreTestDependencies)
annotation.setIgnoreMissingDependencies(true);
}
}
Just an example, but you have many things there to play with.
Just bear in mind that, as I stated in the comments, this runs before runtime, so you won't be able to change things on the go like you would do with a normal listener.

Make ApplicationContext dirty before and after test class

I have a particular class (let's say MyTest) in my Spring integration tests that is using PowerMock #PrepareForTest annotation on a Spring component: #PrepareForTest(MyComponent.class). This means that PowerMock will load this class with some modifications. The problem is, my #ContextConfiguration is defined on the superclass which is extended by MyTest, and the ApplicationContext is cached between different test classes. Now, if MyTest is run first, it will have the correct PowerMock version of MyComponent, but if not - the test will fail since the context will be loaded for another test (without #PrepareForTest).
So what I want to do is to reload my context before MyTest. I can do that via
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
But what if I also want to reload context after this test is done? So I will have clean MyComponent again without PowerMock modifications. Is there a way to do both BEFORE_CLASS and AFTER_CLASS?
For now I did it with the following hack:
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
on MyTest and then
/**
* Stub test to reload ApplicationContext before execution of real test methods of this class.
*/
#DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD)
#Test
public void aa() {
}
/**
* Stub test to reload ApplicationContext after execution of real test methods of this class.
*/
#DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
#Test
public void zz() {
}
I am wondering if there is a prettier way to do that?
As a side question, is it possible to reload only certain bean and not full context?
Is there a way to do both BEFORE_CLASS and AFTER_CLASS?
No, that is unfortunately not supported via #DirtiesContext.
However, what you're really saying is that you want a new ApplicationContext for MyTest that is identical to the context for the parent test class but only lives as long as MyTest. And... you don't want to affect the context cached for the parent test class.
So with that in mind, the following trick should do the job.
#RunWith(SpringJUnit4ClassRunner.class)
// Inherit config from parent and combine with local
// static Config class to create a new context
#ContextConfiguration
#DirtiesContext
public class MyTest extends BaseTests {
#Configuration
static class Config {
// No need to define any actual #Bean methods.
// We only need to add an additional #Configuration
// class so that we get a new ApplicationContext.
}
}
Alternative to #DirtiesContext
If you want to have a context dirtied both before and after a test class, you can implement a custom TestExecutionListener that does exactly that. For example, the following will do the trick.
import org.springframework.core.Ordered;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
public class DirtyContextBeforeAndAfterClassTestExecutionListener
extends AbstractTestExecutionListener {
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
}
#Override
public void afterTestClass(TestContext testContext) throws Exception {
testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
}
}
You can then use the custom listener in MyTest as follows.
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.TestExecutionListeners.MergeMode;
#TestExecutionListeners(
listeners = DirtyContextBeforeAndAfterClassTestExecutionListener.class,
mergeMode = MergeMode.MERGE_WITH_DEFAULTS
)
public class MyTest extends BaseTest { /* ... */ }
As a side question, is it possible to reload only certain bean and not full context?
No, that is also not possible.
Regards,
Sam (author of the Spring TestContext Framework)

Providing context for #Value fields from a unit test

I have a class like this:
#Service("someClient")
public class SomeClient {
#Value{some.value}
private String someValue;
public void someMethod() {
return someValue;
}
}
And a test like this:
#ContextConfiguration(locations = "classpath:/some/where/testApplicationContext.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class SomeClientTest extends TestCase {
#Value{some.value}
private String someValueTest;
#Test
public void shouldWork() {
...
someClient.someMethod()
...
}
}
When the wider application is running, the field someValue inside the SomeClient class is populated from a properties file referenced from testApplicationContext.xml. When I run the test in debug mode I can see that someValueTest is populated in the test, but when the test calls the class under test, the value is not populated.
I could use some advice! Obviously I can change the visibility of the field in the class, or provide a setter, however I would like to avoid that if possible. If it isn't, please advise.
In order to populate fields with #Value annotation in your test you need to configure PropertySourcesPlaceholderConfigurer.
Add the following to your test:
#Configuration
public static class Config {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
To read the values from test property file you can add
#TestPropertySource(locations="classpath:test.properties") to your Test class declaration
You can use ReflectionTestUtils from org.springframework.test.util.ReflectionTestUtils package to mock any variable, including the ones that access the properties file.
#RunWith(SpringJUnit4ClassRunner.class)
public class SomeClientTest extends TestCase {
private SomeClient someClient;
#Test
public void shouldWork() {
//Initialize someClient
someClient = new SomeClient();
ReflectionTestUtils.setField(someClient, "variable name", "the variable value");
someClient.someMethod()
...
}
}

Injecting #Autowired private field during testing

I have a component setup that is essentially a launcher for an application. It is configured like so:
#Component
public class MyLauncher {
#Autowired
MyService myService;
//other methods
}
MyService is annotated with the #Service Spring annotation and is autowired into my launcher class without any issues.
I would like to write some jUnit test cases for MyLauncher, to do so I started a class like this:
public class MyLauncherTest
private MyLauncher myLauncher = new MyLauncher();
#Test
public void someTest() {
}
}
Can I create a Mock object for MyService and inject it into myLauncher in my test class? I currently don't have a getter or setter in myLauncher as Spring is handling the autowiring. If possible, I'd like to not have to add getters and setters. Can I tell the test case to inject a mock object into the autowired variable using an #Before init method?
If I'm going about this completely wrong, feel free to say that. I'm still new to this. My main goal is to just have some Java code or annotation that puts a mock object in that #Autowired variable without me having to write a setter method or having to use an applicationContext-test.xml file. I would much rather maintain everything for the test cases in the .java file instead of having to maintain a separate application content just for my tests.
I am hoping to use Mockito for the mock objects. In the past I have done this by using org.mockito.Mockito and creating my objects with Mockito.mock(MyClass.class).
You can absolutely inject mocks on MyLauncher in your test. I am sure if you show what mocking framework you are using someone would be quick to provide an answer. With mockito I would look into using #RunWith(MockitoJUnitRunner.class) and using annotations for myLauncher. It would look something like what is below.
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher = new MyLauncher();
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
The accepted answer (use MockitoJUnitRunner and #InjectMocks) is great. But if you want something a little more lightweight (no special JUnit runner), and less "magical" (more transparent) especially for occasional use, you could just set the private fields directly using introspection.
If you use Spring, you already have a utility class for this : org.springframework.test.util.ReflectionTestUtils
The use is quite straightforward :
ReflectionTestUtils.setField(myLauncher, "myService", myService);
The first argument is your target bean, the second is the name of the (usually private) field, and the last is the value to inject.
If you don't use Spring, it is quite trivial to implement such a utility method. Here is the code I used before I found this Spring class :
public static void setPrivateField(Object target, String fieldName, Object value){
try{
Field privateField = target.getClass().getDeclaredField(fieldName);
privateField.setAccessible(true);
privateField.set(target, value);
}catch(Exception e){
throw new RuntimeException(e);
}
}
Sometimes you can refactor your #Component to use constructor or setter based injection to setup your testcase (you can and still rely on #Autowired). Now, you can create your test entirely without a mocking framework by implementing test stubs instead (e.g. Martin Fowler's MailServiceStub):
#Component
public class MyLauncher {
private MyService myService;
#Autowired
MyLauncher(MyService myService) {
this.myService = myService;
}
// other methods
}
public class MyServiceStub implements MyService {
// ...
}
public class MyLauncherTest
private MyLauncher myLauncher;
private MyServiceStub myServiceStub;
#Before
public void setUp() {
myServiceStub = new MyServiceStub();
myLauncher = new MyLauncher(myServiceStub);
}
#Test
public void someTest() {
}
}
This technique especially useful if the test and the class under test is located in the same package because then you can use the default, package-private access modifier to prevent other classes from accessing it. Note that you can still have your production code in src/main/java but your tests in src/main/test directories.
If you like Mockito then you will appreciate the MockitoJUnitRunner. It allows you to do "magic" things like #Manuel showed you:
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher; // no need to call the constructor
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
Alternatively, you can use the default JUnit runner and call the MockitoAnnotations.initMocks() in a setUp() method to let Mockito initialize the annotated values. You can find more information in the javadoc of #InjectMocks and in a blog post that I have written.
I believe in order to have auto-wiring work on your MyLauncher class (for myService), you will need to let Spring initialize it instead of calling the constructor, by auto-wiring myLauncher. Once that is being auto-wired (and myService is also getting auto-wired), Spring (1.4.0 and up) provides a #MockBean annotation you can put in your test. This will replace a matching single beans in context with a mock of that type. You can then further define what mocking you want, in a #Before method.
public class MyLauncherTest
#MockBean
private MyService myService;
#Autowired
private MyLauncher myLauncher;
#Before
private void setupMockBean() {
doNothing().when(myService).someVoidMethod();
doReturn("Some Value").when(myService).someStringMethod();
}
#Test
public void someTest() {
myLauncher.doSomething();
}
}
Your MyLauncher class can then remain unmodified, and your MyService bean will be a mock whose methods return values as you defined:
#Component
public class MyLauncher {
#Autowired
MyService myService;
public void doSomething() {
myService.someVoidMethod();
myService.someMethodThatCallsSomeStringMethod();
}
//other methods
}
A couple advantages of this over other methods mentioned is that:
You don't need to manually inject myService.
You don't need use the Mockito runner or rules.
I'm a new user for Spring. I found a different solution for this. Using reflection and making public necessary fields and assign mock objects.
This is my auth controller and it has some Autowired private properties.
#RestController
public class AuthController {
#Autowired
private UsersDAOInterface usersDao;
#Autowired
private TokensDAOInterface tokensDao;
#RequestMapping(path = "/auth/getToken", method = RequestMethod.POST)
public #ResponseBody Object getToken(#RequestParam String username,
#RequestParam String password) {
User user = usersDao.getLoginUser(username, password);
if (user == null)
return new ErrorResult("Kullanıcıadı veya şifre hatalı");
Token token = new Token();
token.setTokenId("aergaerg");
token.setUserId(1);
token.setInsertDatetime(new Date());
return token;
}
}
And this is my Junit test for AuthController. I'm making public needed private properties and assign mock objects to them and rock :)
public class AuthControllerTest {
#Test
public void getToken() {
try {
UsersDAO mockUsersDao = mock(UsersDAO.class);
TokensDAO mockTokensDao = mock(TokensDAO.class);
User dummyUser = new User();
dummyUser.setId(10);
dummyUser.setUsername("nixarsoft");
dummyUser.setTopId(0);
when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) //
.thenReturn(dummyUser);
AuthController ctrl = new AuthController();
Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao");
usersDaoField.setAccessible(true);
usersDaoField.set(ctrl, mockUsersDao);
Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao");
tokensDaoField.setAccessible(true);
tokensDaoField.set(ctrl, mockTokensDao);
Token t = (Token) ctrl.getToken("test", "aergaeg");
Assert.assertNotNull(t);
} catch (Exception ex) {
System.out.println(ex);
}
}
}
I don't know advantages and disadvantages for this way but this is working. This technic has a little bit more code but these codes can be seperated by different methods etc. There are more good answers for this question but I want to point to different solution. Sorry for my bad english. Have a good java to everybody :)
Look at this link
Then write your test case as
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"/applicationContext.xml"})
public class MyLauncherTest{
#Resource
private MyLauncher myLauncher ;
#Test
public void someTest() {
//test code
}
}

Categories