Suppose I have a program that looks like this:
#Component
public class MainAction {
public void doTheAction() {
System.out.println("Now doing the action");
}
}
#Aspect
#Component
public class BeforeAspect {
#Autowired
private Logger logger;
#Before("execution(* thepackagename.MainAction.*(..))")
public void doBefore() {
logger.log("The #Before advice has run");
}
}
#Component
public class Logger {
public void log(String s) {
System.out.println(s);
}
}
This is working fine if I run it through Eclipse (the main method esentially calls mainAction.doTheAction() after mainAction is created by Spring).
Now I want to write a test that ensures that the log method is called correctly when doTheAction is called. We're using JMockit for our testing. (This is a very simplified case of a problem I'm actually facing; a more complex logger is being called via an AOP aspect, and the wrong value of something is being logged. Before working on a fix, I'm trying write a test to ensure the logged value is correct.)
This is what my (simplified) test currently looks like:
#RunWith(JMockit.class)
#ContextConfiguration(locations = {"classpath:Beans.xml"})
public class MainActionTest {
#Tested
private MainAction mainAction;
#Test
public void testThatLoggerIsCalled(#Injectable Logger logger) {
new Expectations() { {
logger.log(anyString);
} };
mainAction.doTheAction();
}
}
The #ContextConfiguration may be useless. Earlier I had tried #RunWith(SpringJunit4ClassRunner.class), which is why #ContextConfiguration is there, but none of the mocking stuff was handled. Also, I'm using #Tested and #Injectable instead of #Autowired and #Mocked, following the suggestion in this question; without that, mainAction remained null. So now the test runs, and Now doing the action appears in the output. But The #Before advice has run doesn't appear (and doesn't appear even if I don't mock the Logger), and the expectation fails.
How can I use JMockit and AOP together?
Edit: As requested, I added something to print the classpath property. Here it is (with unimportant parts of some path names removed):
Eclipse workspaces\springtest8\target\test-classes
Eclipse workspaces\springtest8\target\classes
C:\eclipse\plugins\org.junit_4.11.0.v201303080030\junit.jar
C:\eclipse\plugins\org.hamcrest.core_1.3.0.v201303031735.jar
.m2\repository\org\jmockit\jmockit\1.18\jmockit-1.18.jar
.m2\repository\junit\junit\4.11\junit-4.11.jar
.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar
.m2\repository\org\springframework\spring-context\4.2.0.RELEASE\spring-context-4.2.0.RELEASE.jar
.m2\repository\org\springframework\spring-aop\4.2.0.RELEASE\spring-aop-4.2.0.RELEASE.jar
.m2\repository\aopalliance\aopalliance\1.0\aopalliance-1.0.jar
.m2\repository\org\springframework\spring-beans\4.2.0.RELEASE\spring-beans-4.2.0.RELEASE.jar
.m2\repository\org\springframework\spring-core\4.2.0.RELEASE\spring-core-4.2.0.RELEASE.jar
.m2\repository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar
.m2\repository\org\springframework\spring-expression\4.2.0.RELEASE\spring-expression-4.2.0.RELEASE.jar
.m2\repository\org\aspectj\aspectjrt\1.8.6\aspectjrt-1.8.6.jar
.m2\repository\org\aspectj\aspectjweaver\1.8.6\aspectjweaver-1.8.6.jar
.m2\repository\org\springframework\spring-test\4.2.0.RELEASE\spring-test-4.2.0.RELEASE.jar
.m2\repository\javax\inject\javax.inject\1\javax.inject-1.jar
/C:/eclipse/configuration/org.eclipse.osgi/bundles/201/1/.cp/
/C:/eclipse/configuration/org.eclipse.osgi/bundles/200/1/.cp/
Edit 2: I got things to work by removing JUnit4 from the Libraries tab in Configure Build Path.
The following test works fine, using Spring 3.0 or newer (tested with Spring 3.0.7, 4.0.5, and 4.2.0):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:beans.xml")
public class MainActionTest
{
#Inject MainAction mainAction;
#Test
public void testThatLoggerIsCalled(#Mocked final Logger logger)
{
mainAction.doTheAction();
new Verifications() {{ logger.log(anyString); }};
}
}
I never had to annotate JUnit tests with RunWith to use JMockit. From the documentation you need to make sure the jmockit jar is loaded before junit's or add the javaagent jvm parameter. That way you'll be able to run the tests with Spring's Junit Runner and have JMockit as the mock framework.
Related
We have a Spring 5 application using JUnit 4 as our test harness (w/ SpringRunner). We're experiencing an issue where a private helper method that's not marked with a #Test annotation is being run as a test. This happens in both IntelliJ and Maven.
The method signature is:
private Optional<TestSuiteJsonObject> createTestSuite(String name, TestType type) throws IOException, LicenseException {
And the test class itself looks like:
public class TestSuitesControllerTest extends ControllerTest
There are no annotations on either. The ControllerTest looks like:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = CompanyTestApplication.class)
#AutoConfigureMockMvc
#Ignore
public class ControllerTest {
...
}
The behavior is that these test methods are run with null arguments. We don't want them to be run at all. The rest of the methods in this class are appropriately marked with the #Test annotation.
I wondered if the fact that the word test is in the class/method name could be causing JUnit to identify it as runnable, but changing the names of both the class and method has no effect.
Adding #Ignore to the private method also has no effect!
What are we doing wrong? Did we step into an alternate dimension where test harnesses are actually testing stress responses of the engineers?
It was a silly mistake, but I'm leaving it for any future folk who find themselves in the same situation.
I had two methods with the same name. The test method:
#Test
#WithMockUser(username = "admin", roles = "ADMIN")
public void createSuite() throws Exception { ... }
And the helper method:
private static Optional<TestSuiteJsonObject> createSuite(String name, TestType type) { ... }
And we somehow glossed over this. 🤦♂️
I have a test case which has an #Autowired field. I would like to have one method for setting up the test case, as it has many #Test-annotated methods that will rely on the same generated data, (for which I need the autowired class).
What's a good way to achieve this?
If I have the #BeforeClass, then I need to make the method static, which breaks the autowiring.
1st solution
Use TestNG instead.
#Before* annotations behave this way in TestNG.
No method annotated with #Before* has to be static.
#org.testng.annotations.BeforeClass
public void setUpOnce() {
//I'm not static!
}
2nd solution
And if you don't want to do that, you can use an execution listener from Spring (AbstractTestExecutionListener).
You will have to annotate your test class like this:
#TestExecutionListeners({CustomTestExecutionListener.class})
public class Test {
//Some methods with #Test annotation.
}
And then implement CustomTestExecutionListener with this method:
public void beforeTestClass(TestContext testContext) throws Exception {
//Your before goes here.
}
Self-contained in one file that would look like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"commonContext.xml" })
#TestExecutionListeners({SimpleTest.class})
public class SimpleTest extends AbstractTestExecutionListener {
#Override
public void beforeTestClass(TestContext testContext) {
System.out.println("In beforeTestClass.");
}
#Test
public void test() {
System.out.println("In test.");
}
}
I came up with the solution of creating a separate initialization method (not setUp) annotated with #PostConstruct. This is not really an elegant solution, but it makes sure that the autowired/injected fields are properly initialized by Spring before using them, (which was the initial problem with the statically #BeforeClass annotated method).
Is it possible to run a JUnit #Test method in a class that has a method annotated with #Before, but to ignore the #Before method only for this test?
Edit: I am interested if JUnit supports this functionality, not workarounds. I am aware of workarounds like moving the test(s) in another class or removing the annotation and manually calling setUp() in each test method.
Suppose in a class there are 30 tests, and for 29 of them #Before really simplifies the testing initialization, but for one (or more than one) of them is useless/it complicates things.
public class MyTestClass {
#Before
public void setUp() {
//setup logic
}
#Test
public void test1() {
//[...]
}
#Test
public void test2() {
//[...]
}
//more tests here
#Test(ignoreBefore = true, ignoreAfter = true //false by default)
//something equivalent to this
public void test20() {
//[...]
}
}
You can do this with a TestRule. See my answer to Exclude individual test from 'before' method in JUnit. Basically, implement ExternalResource, and in the apply method, check if there is a specific annotation on the method, and if there is, don't run the before/after method. You'll have to specifically call the before/after from your rule though.
If it useless it should not be a problem - does it harm to run the setUp once more?
However I don't think it's possible and looks for me as a cripple feature.
Another approach - move that test to a separate test-class.
With JUnit 5 You can have nested tests using #Nested annotation :
public class MainClass {
#Nested
class InnerClass1 {
#BeforeEach
void setup(){}
#Test
void test1(){}
}
#Nested
class InnerClass2 {
// No setup
#Test
void test2(){}
}
}
Just to add to this, something that I found out on a recent visit to this area.
Junit/Mockito seems to give preference to mocking behavior defined in a test, over a #Before annotated method.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Using Guice, is it a good practice to get a new injector in each JUnit test class, as each test class should be independant?
In case anyone stumbles upon this question and wants to see how to get Guice annotations working from unit tests, extend your tests from a base class like the one below and call injector.injectMembers(this);
public class TestBase {
protected Injector injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
bind(HelloService.class);
}
});
#Before
public void setup () {
injector.injectMembers(this);
}
}
Then your test can get an injected HelloService like this
public class HelloServiceTest extends TestBase {
#Inject
HelloService service;
#Test
public void testService() throws Exception {
//Do testing here
}
}
You should really avoid using Guice in unit tests as each test should be small enough that manual DI is manageable. By using Guice (or any DI) in unit tests you are hiding away a warning that your class is getting too big and taking on too many responsibilities.
For testing the bootstrapper code and integration tests then yes create a different injector for each test.
I think using DI will make unit test code more simple, I always Use DI for unit test and also for integration test.
Without DI everything feels hard to code. Either using Guice Inject or Spring Autowired. like my test code bellow:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "/application-context.xml")
public class When_inexists_user_disabled {
#Autowired
IRegistrationService registrationService;
private int userId;
#Before
public void setUp() {
Logger.getRootLogger().setLevel(Level.INFO);
Logger.getLogger("org.springframework").setLevel(Level.WARN);
BasicConfigurator.configure();
userId = 999;
}
#Test(expected=UserNotFoundException.class)
public void user_should_have_disabled() throws UserNotFoundException {
registrationService.disable(userId);
}
}
This depends on which version of JUnit is being used. Our teams have used Junit4 successfully and are now looking into JUnit5.
In Junit5 we use extensions.
public class InjectionPoint implements BeforeTestExecutionCallback {
#Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
List<Module> modules = Lists.newArrayList(new ConfigurationModule());
Optional<Object> test = context.getTestInstance();
if (test.isPresent()) {
RequiresInjection requiresInjection = test.get().getClass().getAnnotation(RequiresInjection.class);
if (requiresInjection != null) {
for (Class c : requiresInjection.values()) {
modules.add((Module) c.newInstance());
}
}
Module aggregate = Modules.combine(modules);
Injector injector = Guice.createInjector(aggregate);
injector.injectMembers(test.get());
getStore(context).put(injector.getClass(), injector);
}
}
private Store getStore(ExtensionContext context) {
return context.getStore(Namespace.create(getClass()));
}
}
Then each test uses the RequiresInjection annotation, which can accept an array of inner modules to aggregate, or none to use the default.
#RequiresInjection
public class Junit5InjectWithoutModuleTest {
#Inject
private TestEnvironment environment;
#Test
public void shouldAccessFromOuterModule() {
assertThat(environment).isNotNull();
}
}
And here's the annotation:
#ExtendWith(InjectionPoint.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
public #interface RequiresInjection {
Class<? extends Module>[] values() default {};
}
JUnit5 is still new to me, so I may be looking into templates, but so far the Extensions seem to do the trick.
With JUnit4 we use a similar approach, except that the injection takes place within the createTest method of our custom test runner, and then each test implements a RequiresInjection interface that has a "getModule" method.
I should probably give a shout out to TestNG as well, as Guice support is built right in. Usage is as simple as this:
#Guice({SomeObjectModule.class})
public class MyTest {
#Inject
SomeObject someObject;
}
Take a look at Guice Berry.
I won't recommend using it now (documentation is really terrible), but looking at their approach can make you think clear about how DI should be done in jUnit.
I found AtUnit to be an excellent complement to Guice (it even deals with mock framework integration).
This makes the Unit Test classes extremely clear and concise (never see an Injector there) and, where appropriate, also lets you exercise your production bindings as part of your unit tests.
I suggest this framework I have recently written Guice-Behave.
It is very simple, with two annotations you can run the test in the same context of your application.
You can define your mocks inside the Guice module and in this way it is very easy to re-use them.
OK, so the #Ignore annotation is good for marking that a test case shouldn't be run.
However, sometimes I want to ignore a test based on runtime information. An example might be if I have a concurrency test that needs to be run on a machine with a certain number of cores. If this test were run on a uniprocessor machine, I don't think it would be correct to just pass the test (since it hasn't been run), and it certainly wouldn't be right to fail the test and break the build.
So I want to be able to ignore tests at runtime, as this seems like the right outcome (since the test framework will allow the build to pass but record that the tests weren't run). I'm fairly sure that the annotation won't give me this flexibility, and suspect that I'll need to manually create the test suite for the class in question. However, the documentation doesn't mention anything about this and looking through the API it's also not clear how this would be done programmatically (i.e. how do I programatically create an instance of Test or similar that is equivalent to that created by the #Ignore annotation?).
If anyone has done something similar in the past, or has a bright idea of how else I could go about this, I'd be happy to hear about it.
The JUnit way is to do this at run-time is org.junit.Assume.
#Before
public void beforeMethod() {
org.junit.Assume.assumeTrue(someCondition());
// rest of setup.
}
You can do it in a #Before method or in the test itself, but not in an #After method. If you do it in the test itself, your #Before method will get run. You can also do it within #BeforeClass to prevent class initialization.
An assumption failure causes the test to be ignored.
Edit: To compare with the #RunIf annotation from junit-ext, their sample code would look like this:
#Test
public void calculateTotalSalary() {
assumeThat(Database.connect(), is(notNull()));
//test code below.
}
Not to mention that it is much easier to capture and use the connection from the Database.connect() method this way.
You should checkout Junit-ext project. They have RunIf annotation that performs conditional tests, like:
#Test
#RunIf(DatabaseIsConnected.class)
public void calculateTotalSalary() {
//your code there
}
class DatabaseIsConnected implements Checker {
public boolean satisify() {
return Database.connect() != null;
}
}
[Code sample taken from their tutorial]
In JUnit 4, another option for you may be to create an annotation to denote that the test needs to meet your custom criteria, then extend the default runner with your own and using reflection, base your decision on the custom criteria. It may look something like this:
public class CustomRunner extends BlockJUnit4ClassRunner {
public CTRunner(Class<?> klass) throws initializationError {
super(klass);
}
#Override
protected boolean isIgnored(FrameworkMethod child) {
if(shouldIgnore()) {
return true;
}
return super.isIgnored(child);
}
private boolean shouldIgnore(class) {
/* some custom criteria */
}
}
Additionally to the answer of #tkruse and #Yishai:
I do this way to conditionally skip test methods especially for Parameterized tests, if a test method should only run for some test data records.
public class MyTest {
// get current test method
#Rule public TestName testName = new TestName();
#Before
public void setUp() {
org.junit.Assume.assumeTrue(new Function<String, Boolean>() {
#Override
public Boolean apply(String testMethod) {
if (testMethod.startsWith("testMyMethod")) {
return <some condition>;
}
return true;
}
}.apply(testName.getMethodName()));
... continue setup ...
}
}
A quick note: Assume.assumeTrue(condition) ignores rest of the steps but passes the test.
To fail the test, use org.junit.Assert.fail() inside the conditional statement. Works same like Assume.assumeTrue() but fails the test.