I am following instructions in https://www.docs4dev.com/javadoc/en/org/springframework/boot/spring-boot-test/2.2.2.RELEASE/org/springframework/boot/test/system/OutputCaptureRule.html
I am using Maven with spring-boot-starter-parent version 2.2.2.
My test is very simple:
#SpringBootTest(classes = MyApplication.class) // this loads Springboot context
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
#ContextConfiguration(classes = {MyTestConfig.class, AnotherTestConfig.class})
public class MyTest {
#Rule
public OutputCaptureRule output = new OutputCaptureRule();
#Test
public void theTest() {
assertThat(output).contains("something");
}
}
However, when I put a breakpoint in the assert line and try to evaluate output.getOut(), the result is:
Method threw 'java.lang.IllegalStateException' exception. The details message is:
No system captures found. Please check your output capture registration.
It seems like the feature is not working out of the box. Any idea what I am missing?
Most probably it is because you are using JUnit 5 as SpringBoot 2.2 provides JUnit 5 by default , but the OutputCaptureRule is the JUnit 4 's TestRule stuff and hence it cannot be activated under JUnit 5.
You should use the equivalent OutputCaptureExtension in JUnit 5 instead :
#SpringBootTest(classes = MyApplication.class)
#ExtendWith(OutputCaptureExtension.class)
public class MyTest {
#Test
public void theTest(CapturedOutput output) {
assertThat(output).contains("something");
}
}
Related
I'm trying to set up some basic instrumented tests which I am running with AndroidStudio on my physical device.
The test is fairly straightforward:
#RunWith(AndroidJUnit4.class)
public class MyActivityTest {
private ActivityScenarioRule<MyActivity> scenarioRule = new ActivityScenarioRule<>(MyActivity.class);
#Test
public void onFoo() {
ActivityScenario scenario = scenarioRule.getScenario();
}
}
When it runs, this happens:
java.lang.NullPointerException: throw with null exception
at androidx.test.internal.util.Checks.checkNotNull(Checks.java:34)
at androidx.test.ext.junit.rules.ActivityScenarioRule.getScenario(ActivityScenarioRule.java:118)
at com.me.sample.activities.MyActivityTest.onFoo(MyActivityTest.java:...)
According to the documents, NullPointerException is thrown if you use getScenario() when no test is running. I am running these tests with the AndroidStudio UI and the tests are appearing in the test running window, so it doesn't make sense to me that it's throwing that exception.
Do I need to add a before() method to set up the scenarioRule? Is there some setting I'm missing? Or could this be caused by a dependency or something in MyActivity?
I was missing annotations. This is the version which works:
#RunWith(AndroidJUnit4.class)
#SmallTest
public class MyActivityTest {
#Rule
public ActivityScenarioRule<MyActivity> activityRule = new ActivityScenarioRule<>(MyActivity.class);
#Test
public void foo() {
ActivityScenarioRule<MyActivity> scenario = activityRule.getScenario()
}
}
Sample Controller
public class SampleController {
public void sampleMethod() {
ClassAbc classAbc = new ClassAbc();
classAbc.abcMethod();
//doStuff
}
}
ClassAbc
public class ClassAbc {
public void abcMethod() {
//doStuff
}
}
Junit for SampleController
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#PrepareForTest({SampleController.class})
public class SampleControllerTest {
#Autowired
SampleController sampleController;
public void setUp() {
ClassAbc classAbc = PowerMockito.mock(ClassAbc.class);
PowerMockito.whenNew(ClassAbc.class).withAnyArguments()
.thenReturn(classAbc);
doNothing().when(classAbc).abcMethod();
}
#Test
public void testsampleMethod() throws Exception {
sampleController.sampleMethod();
}
}
When Iam adding the "SampleController.class" in #PrepareForTest ie:
#PrepareForTest({SampleController.class})
Iam getting the following violation when I run Sonar job.
14 more branches need to be covered by unit tests to reach the minimum threshold of 65.0% branch coverage.
That is Iam getting zero coverage for that class.
I would like to like if it is necessary to put the class where the constructor is called into the #PrepareForTest annotation instead of the class which is being constructed.
Or can someone find me a solution to fix that Sonar violation.
Thanks in advance
As stated in PowerMockito wiki:
... right now there is NO WAY TO USE PowerMock with JaCoCo On-the-fly instrumentation.
... to get code coverage with JaCoCo - use offline Instrumentation ...
You may find example of using PowerMock with JaCoCo Offline Instrumentation and Maven in our repository: jacoco-offline example.
I am using cucumber tests to test my spring boot app with spring security enabled .Things work fine except when I run my test suite with cucumber tests some tests using spring security eg.
#WithMockUser(username = "BROWSER", roles =
{"BROWSER","ADMIN"})
fail .These tests do work if I do run them in seclusion as simple junit tests but fail when run with cucumber test steps.
The issue looks like the spring security test mock behaviour isnt getting applied when I run the same with cucumber tests.
My cucumber test run class is as below
#RunWith(Cucumber.class)
#CucumberOptions(features = "src/test/resources", monochrome = true, format =
{"pretty", "html:src/main/resources/static/cucumber"})
public class CucumberTests
{
}
Also I noticed the same works when run via Maven with <reuseForks>false</reuseForks> .Also maven triggered test case run also fails if this option is not checked .
UPDATE
AbstractIntegrationTest class all tests extend
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Services.class,loader = SpringApplicationContextLoader.class)
//#IntegrationTest
#WebIntegrationTest(randomPort = true)
public abstract class AbstractIntegrationTest {
Another use case which does not work is using theses annotation is cucumber feature conditions like below
#When("^I apply a GET on (.*)$")
#WithMockUser(username = "BROWSER", roles = { "BROWSER", "ADMIN" })
public void i_search_with_rsql(String query) throws Throwable {
result = mvc.perform(get(uri, query));
}
any help or workaround on this.
WithMockUser does not work with Cucumber. Use cucumber hooks instead.
WithMockUser relies on TestExecutionListener#beforeTestMethod from Spring's test context support, but they are not invoked when running with Cucumber runner. This is because Cucumber runs scenarios composed of steps rather than the standard JUnit test methods.
Option 1 - Security context hooks. You can setup security context with hooks, for example:
#ActiveProfiles("test")
#SpringBootTest(classes = MyServer.class)
#AutoConfigureMockMvc
#AutoConfigureRestDocs
#AutoConfigureCache
public class MyServerContextHooks {
#Before
public void onBeforeScenario(final Scenario scenario) {
// This method does nothing - context setup is done with annotations
}
}
Example annotation on scenarios:
#WithAdminUser
Scenario: Run action as admin
...
Example hook to use annotation on scenarios:
public class TestUserHooks {
#Before("#WithAdminUser")
public void setupAdminUser() {
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(
"admin",
"N/A",
createAuthorityList("admin")));
}
}
Option 2 - Authentication steps. Another way is to use special steps for providing user into mockMvc:
Scenario: Run action as admin
Given I am logged in as admin
...
Stepdef example:
public class SecurityTestSteps {
#Autowired
private MockMvcGlue mockMvc;
#Autowired
private OAuth2Mocks oauth2Mocks;
#Autowired
private TestUsers users;
/**
* Provides a one of predefined role-based authentications for the current request.
*/
#Given("^I am logged in as (admin|editor|user)$")
public void given_UserIsAuthenticatedWithRole(final String role) {
switch (role) {
case "admin":
mockMvc.request().with(authentication(oauth2Mocks.auth(users.admin())));
break;
case "editor":
mockMvc.request().with(authentication(oauth2Mocks.auth(users.edtior())));
break;
default:
throw new CucumberException("Unsupported role <" + role + ">");
}
}
}
Base upon your comments you need to ensure to apply Spring Security. You can find an example of this in the Setting Up MockMvc and Spring Security section of the reference documentation:
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
// ...
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity()) // ADD THIS!
.build();
}
In Response to Rob Winch's answer, mine worked using his method minus the line
".apply(springSecurity())"
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.
I am trying to report error using JUnit error collector. Although my assertion is failing, error is not reported in JUnit. But I am getting the "error" message in console.
#Rule
public ErrorCollector errcol = new ErrorCollector();
#Then("^Business alert message on the screen$")
public void Business_alert_message_on_the_screen(Result_Update) throws Throwable {
if (userType.equals("Admin")) {
try {
Assert.assertEquals("Update button is not present for the admin user", true, Result_Update);
} catch (Throwable t) {
errcol.addError(t);
System.out.println("Error");
}
}
}
tl;dr : Make sure your test class doesn't extend TestCase.
I had a similar problem when I was using JUnit 4 with IntelliJ IDEA. I naïvely selected a base class of TestCase in the dialog, which was the default for JUnit 3, because I figured "it'd be nice to have those handy this#assert* methods" (the default for JUnit 4 is null). The bad code (which didn't work) is below:
public class SassCompilerTest extends TestCase {
#Rule
public ErrorCollector collector = new ErrorCollector();
#Test
public void testCompiler() throws IOException {
collector.checkThat(true, CoreMatchers.equalTo(false));
}
}
However, in JUnit 4, that prevented a lot of features from working. Removing the parent class fixed the test:
public class SassCompilerTest {
#Rule
public ErrorCollector collector = new ErrorCollector();
#Test
public void testCompiler() throws IOException {
collector.checkThat(true, CoreMatchers.equalTo(false));
}
}
The solution was suggested to me by a comment in the issue with Cucumber mentioned by #StefanBirkner in another answer. After reading that, I tried extending ErrorCollector to make the ErrorCollector#verify public and call it from an #After method, but the #After method wasn't getting called, which made me realize something was either wrong with the TestRunner (which was IntelliJ's default) or the Test itself.
According to JUnit:
The ErrorCollector rule allows execution of a test to continue after
the first problem is found
errcol.addError(t);//only adds the error to the ErrorCollector
This means that the test continues after collecting the error.
You should add:
errcol.checkThat(...); //will pass/fail the test
See examples:
https://junit.org/junit4/javadoc/4.12/org/junit/rules/ErrorCollector.html (Updated)
https://gist.github.com/cb372/2419626
The Cucumber runner does not support #Rule because it extends ParentRunner and not BlockJUnit4ClassRunner (see source code of the runner). There is already an issue for supporting ErrorCollector.