I have to test a method with testNG framework:
public class PkceUtil {
private PkceUtil(){
super();
}
/**
* External application use this method for generating a Base64 URL encoded alphanumeric string of characters
* with a minimum length of 43 characters and a maximum length of 128 characters (code verifier).
* #return code verifier
*/
public static String generateCodeVerifier(){
SecureRandom secureRandom = new SecureRandom();
byte[] codeVerifier = new byte[32];
secureRandom.nextBytes(codeVerifier);
return Base64.getUrlEncoder().withoutPadding().encodeToString(codeVerifier);
}
..
But i get a Nullpointer in when(Base64.getUrlEncoder()...
import org.mockito.MockedStatic;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
import org.testng.annotations.Test;
import java.util.Base64;
import static org.mockito.Mockito.*;
import static org.testng.Assert.assertEquals;
#PrepareForTest(Base64.class)
public class PkceUtilTest extends PowerMockTestCase {
#Test
public void testGenerateCodeVerifier() {
MockedStatic<Base64> mockedStaticBase64 = mockStatic(Base64.class);
String codeVerifier= "";
23: -->when(Base64.getUrlEncoder().withoutPadding().encodeToString(any())).thenReturn(codeVerifier);
final String codeVerifierReturned = PkceUtil.generateCodeVerifier();
assertEquals(codeVerifierReturned, codeVerifier);
verify(Base64.getUrlEncoder().withoutPadding().encodeToString(any()));
mockedStaticBase64.close();
}
}
java.lang.NullPointerException
at com.cadit.oidc.sdk.pkce.PkceUtilTest.testGenerateCodeVerifier(PkceUtilTest.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:132)
at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:599)
at org.testng.internal.TestInvoker.invokeTestMethod(TestInvoker.java:174)
at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:46)
at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:822)
at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:147)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at org.testng.TestRunner.privateRun(TestRunner.java:764)
at org.testng.TestRunner.run(TestRunner.java:585)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:384)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:378)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:337)
at org.testng.SuiteRunner.run(SuiteRunner.java:286)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng</artifactId>
<version>2.0.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.5.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.5.13</version>
<scope>test</scope>
</dependency>
What's wrong with my test code?
thanks
You have to ask yourself: what is your test actually testing? I'd say: nothing. Everything important is mocked out and the method simply returns what you tell the mocked object to return. So in effect, you are testing that Mockito/PowerMock does its job properly.
More useful tests could be:
Calling the method and verifying that the result is a non-empty string
Calling the method and verifying that the result is a valid base64 string
Calling the method two times and asserting that the result is different for both calls (real randomness allows the method to return the same output two times in a row, but the chances are really, really slim for 32 bytes of random data).
NB Do not create a new instance of SecureRandom every time your method is called; create it once when the class is loaded and reuse that instance.
Or even better: inject the random instance, so you can replace it with a test double when testing. That way you have full control over the generated random value and can thus assert the method's output.
Ignoring that, back to your question:
Base64.getUrlEncoder() is never set up to return anything but the default. The default for non-setup'ed methods is … null mostly (with some exceptions, such as empty collections).
Either manually stub every call in your chain, or specify Answers.RETURNS_DEEP_STUBS (so every call would return another call).
Also, if your test throws an exception, your mockStatic resource is never closed. It's better to use the try-with-resources pattern when working with AutoCloseable instances: it handles all the error conditions properly and closes your resource under any circumstances:
try (MockedStatic<Base64> mockedStaticBase64 = mockStatic(Base64.class)) {
String codeVerifier= "";
when(Base64.getUrlEncoder().withoutPadding().encodeToString(any())).thenReturn(codeVerifier);
final String codeVerifierReturned = PkceUtil.generateCodeVerifier();
assertEquals(codeVerifierReturned, codeVerifier);
verify(Base64.getUrlEncoder().withoutPadding().encodeToString(any()));
}
One question though: why do you want to mock the UrlEncoder to always return an empty string? If it is passed an empty string, it should already return one. What do you gain by mocking it (and then verifying that the mock has been called)?
PS. Everytime a mock returns a mock a fairy dies
Related
public void fetchTwilioReport(){
System.out.println("fetchTwilioReport called");
Twilio.init(ACCOUNT_SID,AUTH_TOKEN);
ResourceSet<Call> calls = Call.reader().limit(20).read();
for(Call record : calls) {
System.out.println(record.getSid());
}
}
I am trying to get a list of calls, but the above code returns a blank. I am using twilio 8.11.0.
Curious. The code you have posted does fetch a list of calls for me. For clarity, here is a self-contained example of code which prints out 20 call SIDs:
package com.example;
import com.twilio.Twilio;
import com.twilio.base.ResourceSet;
import com.twilio.rest.api.v2010.account.Call;
public class CallReaderDemo {
public static void main(String[] args) {
Twilio.init(
System.getenv("TWILIO_ACCOUNT_SID"),
System.getenv("TWILIO_AUTH_TOKEN"));
ResourceSet<Call> calls = Call.reader().limit(20).read();
for (Call record : calls) {
System.out.println(record.getSid());
}
}
}
As this is in a Maven project, I have imported the Twilio Helper library in pom.xml with:
<dependencies>
<dependency>
<groupId>com.twilio.sdk</groupId>
<artifactId>twilio</artifactId>
<version>8.11.0</version>
</dependency>
</dependencies>
If this code still returns no calls for you, then the next thing I would check is whether there actually are any calls in the account which those credentials access. Possibly "no calls" is the correct answer? You can fetch call data from the API using the same URL that the Java library does:
https://${TWILIO_ACCOUNT_SID}:${TWILIO_AUTH_TOKEN}#api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Calls.json
Replace ${TWILIO_ACCOUNT_SID} and ${TWILIO_AUTH_TOKEN} with your account credentials in that URL and you can load it with curl or in a web browser. The JSON that is returned will have a key calls whose value is a list of objects full of call data.
Then there are two possibilities. If the API is returning calls and the Java Helper isn't giving them to you then we need to dig further into the Java side of things. If there are no calls and you think there definitely should be then we can look more into your account setup and take it from there. Let me know how you get on.
I am using Mockito/JUnit on a Maven project for a console-based application with a DAO design pattern. My IDE is Spring Toools Suite 3.
The problem is that I am getting an UnfinishedStubbingException every time I run this particular test on my particular JUnit test, and I don't know why that is since the syntax looks correct. I am very new to unit testing and Mockito, but I assume that this is occurring because the level of abstraction from one layer to the next is confusing Mockito for some reason. Therefore, I initially tried to use a Spy versus a Mock on the service object in the test case (but then the NotAMockException is being thrown instead).
Any advice and/or recommendations how to resolve this issue will be appreciated.
Here is the stack trace:
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.revature.testing.BankAccountEvaluationService.testMakeDeposit_ValidUserId(BankAccountEvaluationService.java:91)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
at com.revature.testing.BankAccountEvaluationService.testMakeDeposit_ValidUserId(BankAccountEvaluationService.java:91)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
Here is the sample code:
BankAccountEvaluationTest class:
#InjectMocks
private AccountServiceImpl service;
#Mock
private AccountDaoImpl daoMock;
#Before
public void setUp() {
service = Mockito.spy(new AccountServiceImpl());
MockitoAnnotations.initMocks(this);
}
#Test
public void testMakeDeposit_ValidUserId() {
//setup
Account account = Mockito.mock(Account.class);
account.setAccountId(1);
double amount = 20.50;
//gives UnfinishedStubbingException -> Mockito doesn't like this because it's mocking within a mocking object
//doNothing().when(daoMock).updateAccountBalance(account.getBalance() + amount, accountNumber); //Solution?
//run
service.makeDeposit(amount, account.getAccountId());
//verify
verify(service, times(1)).makeDeposit(amount, account.getAccountId());
}
AccountServiceImpl class:
package com.revature.serviceimpl;
import java.util.List;
import org.apache.log4j.Logger;
import com.revature.dao.AccountDao;
import com.revature.daoimpl.AccountDaoImpl;
import com.revature.model.Account;
import com.revature.service.AccountService;
public class AccountServiceImpl implements AccountService {
private static Logger logger = Logger.getLogger(AccountServiceImpl.class);
private AccountDao accountDao = new AccountDaoImpl();
//other overridden methods from AccountService interface
#Override
public void makeDeposit(double addedCash, int id) {
logger.info("Sending deposit request to the database.");
// find the account
Account account = accountDao.selectAccountByAccountId(id);
System.out.println(account);
// set new balance
account.setBalance(account.getBalance() + addedCash);
double myNewBalance = account.getBalance();
logger.info("New balance: " + account.getBalance());
logger.info("Updating account balance to account number " + id);
// update the database
accountDao.updateAccountBalance(myNewBalance, id);
}
}
AccountDaoImpl class:
package com.revature.daoimpl;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import com.revature.dao.AccountDao;
import com.revature.model.Account;
import com.revature.model.AccountStatus;
import com.revature.model.AccountType;
public class AccountDaoImpl implements AccountDao {
private static Logger logger = Logger.getLogger(UserDaoImpl.class);
private static String url = MY_URL;
private static String dbUsername = MY_DATABASE_NAME;
private static String dbPassword = MY_DATABASE_PASSWORD;
//other overridden methods from AccountDao interface
#Override
public void updateAccountBalance(double balance, int id) {
try (Connection conn = DriverManager.getConnection(url, dbUsername, dbPassword)) {
String sql = "UPDATE accounts SET account_balance = ? WHERE account_id = ?;";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setDouble(1, balance);
ps.setInt(2, id);
ps.executeUpdate();
logger.info("new balance is now set");
} catch (SQLException e) {
logger.warn("Error in SQL execution to update balance. Stack Trace: ", e);
}
}
}
It seems that there is a some confusion about mocking going on here.
With the code you have, you would look to write a test for your AccountServiceImpl but mocking out the AccountDao. Instead of using the real DAO which talks to a database, you use a mock DAO which you can set up to return certain values when certain methods are called. You can also query the mock to find out how many times it was called and with what values.
Mocking domain objects
In your test it seems you've also chosen to mock out the Account class. You haven't included your Account class in your question but I'm guessing it just has getters and setters. If so, I wouldn't bother mocking it out. Instead of using a mock account, use a real one. Replace the line
Account account = Mockito.mock(Account.class);
with
Account account = new Account();
That way you can call the getters and setters on account and they will behave as you expect. If you insist on using a mock account, you would have to use Mockito to implement the getters and setters:
when(account.getId()).thenReturn(...)
when(account.getBalance()).thenReturn(...)
// ...
You also call account.setId() on your mock account. This will do nothing because you haven't told Mockito what to do when you call .setId() on a mock account. Calling account.setId() on a real account will set the ID.
You don't have to mock out every single class other than the one being tested. Whether you do depends on what these other classes do. If another class has some complicated logic, or communicates with databases, filesystems, networks, etc., then it can be useful to mock it out, so that your test doesn't have to worry about either the complicated logic or the communication with external systems. In this case, however, let's not bother mocking Account because it doesn't do anything complicated.
Mock setup
When creating a mock, all void methods will do nothing and all other methods will return false, zero or null as appropriate. If you want them to do something else, you need to set them up.
Your service uses your DAO to load an account from the database. You need to set up the mock accountDao to return account when given its ID. Do this by adding the following line to your test before the line that calls service.makeDeposit(...):
when(daoMock.selectAccountByAccountId(1)).thenReturn(account);
But what about the updateAccountBalance() method? By default, that will do nothing, and you appear to be attempting to set it up to do nothing, which it already does. You can delete the line that attempts to set up this method, as it would achieve nothing. Later on, we will look at verifying this method, i.e. asserting that it was called.
Mocking inside of mocking
You were getting an error with this line:
doNothing().when(daoMock).updateAccountBalance(account.getBalance() + amount, accountNumber)
When setting up one mock, you can't call a method on another mock. Why would you need to? If it's a mock then you should already have set up the mock method to return a certain value, so just use that value instead. In other words, instead of writing
// if account is a mock...
when(account.getBalance()).thenReturn(10.00);
doNothing().when(daoMock).updateAccountBalance(account.getBalance() + amount, accountNumber)
just write
// if account is a mock...
when(account.getBalance()).thenReturn(10.00);
doNothing().when(daoMock).updateAccountBalance(10.00 + amount, accountNumber)
In this line, you are attempting to set up daoMock and are calling account.getBalance(). If account is also mock, this will cause the problem.
The reason this causes a problem is due to the way Mockito works. Mockito can't see your source code, all it sees is calls to its own static methods and calls to mocks. The line
doNothing().when(daoMock).updateAccountBalance(account.getBalance() + amount, accountNumber)
causes the following sequence of interactions:
Static Mockito method doNothing() called,
when() method of whatever object doNothing() called,
account.getBalance() method called,
updateAccountBalance() method of the mock DAO called. (We can't call a method until we've evaluated all of the arguments.)
To Mockito, the first three steps of this are indistinguishable from the following:
doNothing().when(daoMock);
when(account.getBalance()).thenReturn(...);
In this case, it is clear that we haven't finished setting up daoMock. We would expect an exception in this case.
Mockito's syntax leads to clear and expressive mocking, but if care isn't taken you can sometimes end up in situations like this.
We've already decided to get rid of the line that caused this problem, so this section is more for information and understanding than anything else.
Verification
Next, we look at the following lines:
//run
service.makeDeposit(amount, account.getAccountId());
//verify
verify(service, times(1)).makeDeposit(amount, account.getAccountId());
What does this do? You call the makeDeposit method, and then you verify that you called the makeDeposit method. There's really no need to make this verification: you can plainly see that this method is called three lines above.
Generally, you don't verify methods on the class being tested. Instead, you verify methods on the mocks that your class called. What you want to do instead is to verify that the relevant method on the mock DAO got called with the expected values:
verify(daoMock, times(1)).updateAccountBalance(account.getBalance() + amount, account.getAccountId());
You can also get rid of the call to Mockito.spy(...). I've never used spies myself, and I don't see the need for them here. Replace the line
service = Mockito.spy(new AccountServiceImpl());
with
service = new AccountServiceImpl();
In conclusion
There's a fair amount here, and hopefully at least some of it makes sense and gives you a better understanding of what's going on. I made the changes above to your test and I got it to pass.
It's quite a while since I've used Mockito the last time, but could it be that instead of
doNothing().when(daoMock).updateAccountBalance(account.getBalance() + amount, accountNumber);
You should use
doNothing().when(daoMock).updateAccountBalance(Mockito.any(), Mockito.any());
... maybe you can also try
when(daoMock.updateAccountBalance(Mockito.any(), Mockito.any())).doNothing();
I think (but I'm not sure) that you are just mocking the exact call with the parameters (account.getBalance() + amount, accountNumber)
so e.g. if your accountnumber at the time you set the mock is 5 than you are just mocking the one single call with accountnumber = 5
Scenario: As a user, I want to login to the system
Given I am on my website
When I enter valid credentials
Then I am taken to the home page
The scenario name can be retrieved using the getName() function. Is there a way to also get the step being executed (in Java)? We foresee the use of this in logging and reporting.
So, for the scenario above, I am on my website would be returned while the corresponding step definition is being executed.
I think the CucumberWithSerenity register a Listener which stores the current Step Name.
Try this in your Test-Runner:
//import net.serenitybdd.cucumber.CucumberWithSerenity;
#RunWith(CucumberWithSerenity.class)
#CucumberOptions(...
And then in in your Step:
//import net.thucydides.core.model.TestStep;
//import net.thucydides.core.steps.StepEventBus;
if (!StepEventBus.getEventBus().isBaseStepListenerRegistered()) {
return "Unknown"; // CucumberWithSerenity is required.
}
String currentStepDescr = StepEventBus.getEventBus().getCurrentStep()
.transform(TestStep::getDescription)
.get();
Dependency:
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-core</artifactId>
<version>${serenity.version}</version>
</dependency>
I solved it using #BeforeStep & #AfterStep. It is a bit hacky, so use it only if you are know what you are doing.
public class StepDefBeginEndLogger {
private int currentStepDefIndex = 0;
#BeforeStep
public void doSomethingBeforeStep(Scenario scenario) throws Exception {
Field f = scenario.getClass().getDeclaredField("testCase");
f.setAccessible(true);
TestCase r = (TestCase) f.get(scenario);
//You need to filter out before/after hooks
List<PickleStepTestStep> stepDefs = r.getTestSteps()
.stream()
.filter(x -> x instanceof PickleStepTestStep)
.map(x -> (PickleStepTestStep) x)
.collect(Collectors.toList());
//This object now holds the information about the current step definition
//If you are using pico container
//just store it somewhere in your world state object
//and to make it available in your step definitions.
PickleStepTestStep currentStepDef = stepDefs
.get(currentStepDefIndex);
}
#AfterStep
public void doSomethingAfterStep(Scenario scenario) {
currentStepDefIndex += 1;
}
}
Here's an update to handle the framework changes. The "testCase" field is hidden under the "delegate". I got this working with io.cucumber.java version 5.7.0
public String getStepText(io.cucumber.java.Scenario scenario){
String currentStepDescr = null;
//value currentStepDefIndex is tracked in the another class
int currentStepDefIndex = OtherClass.getStepIndex();
Field f = scenario.getClass().getDeclaredField("delegate");
f.setAccessible(true);
TestCaseState tcs = (TestCaseState) f.get(scenario);
Field f2 = tcs.getClass().getDeclaredField("testCase");
f2.setAccessible(true);
TestCase r = (TestCase) f2.get(tcs);
List<PickleStepTestStep> stepDefs = r.getTestSteps()
.stream()
.filter(x -> x instanceof PickleStepTestStep)
.map(x -> (PickleStepTestStep) x)
.collect(Collectors.toList());
PickleStepTestStep currentStepDef = stepDefs
.get(currentStepDefIndex);
currentStepDescr = currentStepDef.getStep().getText();
currentStepDefIndex += 1;
OtherClass.setStepIndex(currentStepDefIndex);
return currentStepDescr ;
}
Below are the dependencies in my pom.xml
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-core -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-core</artifactId>
<version>5.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-testng -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>5.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>5.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-jvm-deps -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-jvm-deps</artifactId>
<version>1.0.6</version>
<scope>provided</scope>
</dependency>
We solved this problem by wrapping the entire step as a parameter into the Step Definition. In other words, the step
Given I am on my website
translates into
'Given I am on my website'
And the step definition will actually accept a string parameter that will correspond to the step
#And("(.*)") //plus something specific to map step
public void Initialization(String step) throws Exception {
//do something with step
}
Just leaving this here for future reference...
The current version of Cucumber (4.2.5) has the BeforeStep hook, but only provides access to the current running scenario.
What I did to extract the current step, was using reflection to access the steps within that scenario;
#BeforeStep
public void beforeStep(Scenario scn) throws Exception {
currentStepIndex++;
Field testCaseField = scn.getClass().getDeclaredField("testCase");
testCaseField.setAccessible(true);
TestCase tc = (TestCase) testCaseField.get(scn);
Field testSteps = tc.getClass().getDeclaredField("testSteps");
testSteps.setAccessible(true);
List<TestStep> teststeps = tc.getTestSteps();
try {
PickleStepTestStep pts = (PickleStepTestStep) teststeps.get(currentStepIndex);
getLog().info("########################");
getLog().info("##########STEP##########");
getLog().info(pts.getStepText());
getLog().info("########################");
currentStepIndex++;
} catch (Exception ignore) {
}
}
The only downside is, that you require a int currentStepIndex at class level, and need to add 1 with every #Before or #BeforeStep.
BE WARNED that the use of this type of reflection may fail to work in future releases of Cucumber, as the Cucumber team can decide to change their internals.
Being a newbie m not allowed to comment so here is some info, assuming you are using cucumber-jvm.
Short answer, No, Cucumber by itself doesnt have the option to read step names. You could use the method names to identify what was called.
Also, #BEFORE STEP / #AFTER STEP tags are not yet available so you will have to define the call for each step.
https://github.com/cucumber/cucumber-jvm/pull/838#issuecomment-234110573
or the testing framework like junit or testng could let you access the execution details - something like this:
http://junit.org/junit4/javadoc/4.12/org/junit/rules/TestWatcher.html.
And if you really need the step names only for reporting purposes, you can simply parse the xml report that the testing framework generates.
These hooks will help:
#BeforeStep
public void beforeStep(Scenario scenario){
System.out.println(scenario.toString());
}
#AfterStep
public void afterStep(Scenario scenario){
System.out.println(scenario.toString());
}
Grabbing the annotation using self-reflection seems more straightforward to me:
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
#When("^User enters username and password$")
public void userEntersUsernameAndPassword() throws Throwable{
Method callingMethod = new Object() {} .getClass() .getEnclosingMethod();
Annotation myAnnotation = callingMethod.getAnnotations()[0];
System.out.println("myAnnotation=" + myAnnotation);
Results in:
myAnnotation=#cucumber.api.java.en.Given(timeout=0, value=^User is in portal page$)
Are you asking if it is possible to get some logging that indicates that the step When I enter valid credentials is executed?
If so, the answer is yes.
Cucumber as such doesn't have a notion of logging so you would have to add your own favorite logging framework. Since Cucumber doesn't know about logging through your favorite log framework, you will have to add a log statement in each step you implement in Java.
I have never seen the need for logging myself. The execution log from Maven, or whatever build tool you are using, have been sufficient for me for a long time.
The reports include the steps executed so that case is covered.
I had this same question. I attempted to use rs79's answer but either I don't know what I'm actually doing with it or it doesn't work. Java gives me an "AmbiguousStepDefinitionException" or something like that. So I did it a different way. It takes a little work if you have a slew of step definitions but it works and is pretty simple:
#Then(value = "^The page should navigate to url \"([^\"])\"$", timeout = MAX_TIME)
public void the_page_should_navigate_to_url(String url) {
//below I use a custom class with a static method setStepName() which just sets a string field in the class
CustomClass.setStepName("Then The page should navigate to url " + url);
//Assert
}
Now you have access to the step name without needing any kind of complicated tool. Just use a get method to access the step variable in your custom class. Hope that helps.
As a newbie I could not comment the answer from AndyGee but if you want to have the actual name you have to use the .getName() method or .getUri + .getLine() to get something like an id (.getId() does not return a unique ID).
#BeforeStep
public void beforeStep(Scenario scenario){
System.out.println(scenario.getName().toString());
}
Currently we are using the .getUri() method and check the Uri against a substring to be a little more flexible in the future.
you could add a step like
When I log in with the user 'user' and the password 'password'
and repeat this step whenever you need a login
You have to put the class containing the step definition in a package used by every Runner that will need the login.
I have a method that contains 2 conditions. In each condition the Logger.error method is invoked. The first test, that verifies the invocation of that method, succeeds, but any other test fails with
Wanted but not invoked...Actually, there were zero interactions with
this mock.
Does anyone know why this happens?
Below, I provided an example class and a unit test that will generate the issue:
package packageName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class X {
private static final Logger LOGGER = LoggerFactory.getLogger(X.class);
public void execute(boolean handle1stCase) {
if (handle1stCase) {
LOGGER.error("rumpampam");
} else {
LOGGER.error("latida");
}
}
}
The test:
package packageName;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
#RunWith(PowerMockRunner.class)
#PrepareForTest({LoggerFactory.class})
public class XTest {
#Mock
private Logger loggerMock;
private X x;
#Before
public void construct() {
MockitoAnnotations.initMocks(this);
mockStatic(LoggerFactory.class);
when(LoggerFactory.getLogger(any(Class.class))).thenReturn(loggerMock);
x = new X();
}
#Test
public void whenFirstCaseErrorLogged() throws Exception {
x.execute(true);
verify(loggerMock, times(1)).error("rumpampam");
}
#Test
public void whenSecondCaseErrorLogged() throws Exception {
x.execute(false);
verify(loggerMock, times(1)).error("latida");
}
}
The outcome:
Wanted but not invoked: loggerMock.error("latida");
-> at packageName.XTest.whenSecondCaseErrorLogged(XTest.java:51)
Actually, there were zero interactions with this mock.
EDIT:
I gave my short answer of why the every test except the 1st was failing in a comment of this answer.
MY SOLUTION TO THE PROBLEM:
In the test provide a:
public static Logger loggerMockStatic;
Than create only one instance for all the tests and provide it in the static variable, and use the static loggerMockStatic from than on. So you would have:
...
MockitoAnnotations.initMocks(this);
if (loggerMockStatic == null) {
loggerMockStatic = loggerMock;
}
mockStatic(LoggerFactory.class);
//when(LoggerFactory.getLogger(any(Class.class))).thenReturn(loggerMock);
when(LoggerFactory.getLogger(any(Class.class))).thenReturn(loggerMockStatic);
...
and use loggerMockStatic in the verify methods instead of loggerMock.
SOME THOUGHTS REGARDING APPROACH:
For me this is good because
1. it doesn't break design (if you considered that the needed variable should have been a constant, than it will stay this way).
2. its only 4 lines added in the test that will allow you to test the constant (in this case logger) behavior. Not much polluting and test case is still clear.
The "delete final and provide setter" approach as I explained in this answer opens the system to vulnerabilities. There is no need for someone to set the logger to the class, and I would always like the system to be as opened as needed. Providing a setter only for the need of a test is not desired. The test should work for the implementation, not the other way around.
Specifically on testing the logging, I don't consider that logging should be tested in general (most of the) cases. Logging should be an aspect of the application. And when you have other outputs to test for a certain path, than those outputs should be tested. But in this case (and maybe others) where there is no other output for a certain path, like logging and returning on a certain condition, testing the log (according to me) is needed. I want to always know that the log message will remain being logged even if someone changes the condition. If there was no log, and if someone changes the condition the wrong way there will be no way of knowing that the mistake resides in this piece of code (except maybe with debugging).
I was discussing with some colleagues that having a separate class for doing the logging would do the trick. That way the constant is isolated in another class, and you will be able to check the behavior with only Mockito. They made a further remark that this way if you would wanted to send the log to an email it would be easier to change.
First of all, I consider this a premature modularization IF you are not aiming in near future to switch between logging ways.
Secondly, using only Mockito + having another class and + 3 lines of code VS my one line of code (logger.error(...)) + using PowerMockito, I would again use the later. Adding additional dependencies during testing will not make you production code slower and bulkier. Perhaps when considering continues integration and that testing is also as important as other phases, you might say that this will make the process slower and bulkier while testing, but I would sacrifice that - it seems not too big of deal to me.
Your logger is static thus it's loaded when your class is loaded not when object is initialized. You have no guaratee that your mock will be ready on time, sometimes it might work sometimes not.
Here is why this is not working:
Field in class X is static and final which allows to set this only the first time class is loaded. This is dangerous because of what I wrote in my first answer. In your case you are lucky and this is not happening but...
Junit executes your test case in following order:
construct()
whenFirstCaseErrorLogged()
construct()
whenSecondCaseErrorLogged()
Now lets say that after first call to construct() XTest's field loggerMock is pointing to object which resides at the address 0001. This object is then used by LoggerFactory to initialize LOGGER field of x object. x.error is then called from whenFirstCaseErrorLogged() and that works finde because both loggerMock and X::Logger are pointing at the same object.
Now we get to the second construct(). Your loggerMock is reinitialized and now it points to a different object let assume that is stored in memory at the address 0002. This is a new object different from the previously created. Now because your X::LOGGER is static final it won't be reinitialized thus it still points at the object stored at the address 0001. When you will try to verify methods invoked on loggerMock you will get error because nothing was executed on that object instead error method of your previous object was called.
And here are some thoughts from me. Maybe they will appear helpful.
I think in future you should reconsider using static twice. Why do you want to make something constant when it is not constant ? Will your reference variable have the same value after you will run for the second time ? Of course it can happen but it is very unlikely. Can static final prevent you from changing the state of the object ? Of course not they will only prevent you from reassigning LOGGER to a different instance. You mentioned in your previous comment that you don't want a user of your code to provide null reference for your LOGGER. That is ok but you can prevent that by throwing an exception when one is provided or using a different null handling mechanism.
A lot has been said about using static keyword. Some consider it as pure evil some don't and some still love singletons :)
No matter what you think you have to know that static is no good for testing and threading.
I use static final when something is static like PI, or euler number but I don't use static final for objects that have mutable state. I use static methods for utility classes that don't store state but just do some processing ( parsing, counting etc.) and return the result imediatelly. A good example is a math function like power.
I think that will be useful ;)
Add a method to class X to allow the logger to be set, and remove the final from it. Then do something like this in the test.
#Mock private Logger mockLogger;
private X toTest = new X();
...
#Before
public void setUp() throws Exception {
toTest.setLogger(mockLogger);
}
#Test
public void logsRumpampamForFirstCall() throws Exception {
toTest.execute(true);
verify(mockLogger).error("rumpampam");
}
#Test
public void logsLatidaForOtherCalls() throws Exception {
toTest.execute(false);
verify(mockLogger).error("latida");
}
This question already has answers here:
Java: How to test methods that call System.exit()?
(19 answers)
Closed 9 years ago.
I am trying to test a given java application, and for that purpose I want to use JUnit.
The problem I am facing is the following: Once the code I am trying to test finishes its work, its calling System.exit(), which closes the application. Although it is also stoping my tests from completing, as it closes the JVM (I assume).
Is there anyway to go around this problem, without modifying the original code? Initially I tried launching the application im testing from new thread, although that obviously didn't make much difference.
You can use System Rules: "A collection of JUnit rules for testing code which uses java.lang.System."
Among their rules, you have ExpectedSystemExit, below is an example on how to use it. I believe it is a very clean solution.
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.Assertion;
import org.junit.contrib.java.lang.system.ExpectedSystemExit;
public class SystemExitTest {
#Rule
public final ExpectedSystemExit exit = ExpectedSystemExit.none();
#Test
public void noSystemExit() {
//passes
}
#Test
public void executeSomeCodeAFTERsystemExit() {
System.out.println("This is executed before everything.");
exit.expectSystemExit();
exit.checkAssertionAfterwards(new Assertion() {
#Override
public void checkAssertion() throws Exception {
System.out.println("This is executed AFTER System.exit()"+
" and, if exists, the #org.junit.After annotated method!");
}
});
System.out.println("This is executed right before System.exit().");
System.exit(0);
System.out.println("This is NEVER executed.");
}
#Test
public void systemExitWithArbitraryStatusCode() {
exit.expectSystemExit();
System.exit(0);
}
#Test
public void systemExitWithSelectedStatusCode0() {
exit.expectSystemExitWithStatus(0);
System.exit(0);
}
#Test
public void failSystemExit() {
exit.expectSystemExit();
//System.exit(0);
}
}
If you use maven, you can add this to your pom.xml:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-rules</artifactId>
<version>1.3.0</version>
</dependency>
System.exit(status) actually delegates the call to Runtime class. Runtime before proceeding with this shutdown request invokes checkExit(status) on JVM's current SecurityManager which can prevent the impending shutdown by throwing a SecurityException.
Usually, the SecurityManager needs to establish if the current thread has the privilege to shutdown defined by the current security policy in place but since all we need is to recover from this exit call we simply throw a SecurityException that we'll now have to catch in our JUnit test case.
In your JUnit test class, setup a SecurityManager in setUP() method:
securityManager = System.getSecurityManager();
System.setSecurityManager(new SecurityManager() {
#Override
public void checkExit(int status) {
super.checkExit(status); // This is IMPORTANT!
throw new SecurityException("Overriding shutdown...");
}
});
In tearDown() replace the SecurityManager again with the instance that we saved before. Failure to do so would prevent JUnit from shutting down now! :)
References:
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/SecurityManager.html
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/SecurityManager.html#checkExit(int)
The SecurityManager class contains many methods with names that begin with the word check. These methods are called by various methods in the Java libraries before those methods perform certain potentially sensitive operations. The invocation of such a check method typically looks like this:
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkXXX(argument, . . . );
}
The security manager is thereby given an opportunity to prevent completion of the operation by throwing an exception. A security manager routine simply returns if the operation is permitted, but throws a SecurityException if the operation is not permitted.
There is no way around System.exit() except for calling the application to run as a seperate proces (outside your JVM).
You can do this from your unit test and observe the errorlevel that comes back from it. Whether that gives enough feedback on passing of the test is up to your judgement.