Using PowerMock with Cucumber - java

I've written a JUnit test that uses Mockito and PowerMock to mock some classes. I'm trying to convert it a Cucumber test, but the static PowerMock features don't work.
Extracts of the two relevant Cucumber classes:
Runner
#RunWith(Cucumber.class)
public class JWTValidatorBDDTest {
}
Steps Class
public class JWTValidatorCukeTest {
String tokenValue;
JWTValidator jwtValidator;
MockHttpServletRequest mockRequest;
#Before
public void before() throws IOException {
this.mockRequest = new MockHttpServletRequest();
PowerMockito.mockStatic(JWTAuthConnectionManager.class);
BDDMockito.given(JWTAuthConnectionManager.postToken(anyString(), anyString(), anyString())).willReturn(200);
Mockito.doReturn(200).when(JWTAuthConnectionManager.postToken(anyString(), anyString(), anyString()));
}
#Given("^a JWT token with the value (.*)")
public void a_JWT_token_with_the_value_(String token) {
this.jwtValidator = new JWTValidator("https://test.7uj67hgfh.com/openam", "Authorization", "Bearer");
this.tokenValue = token;
}
Whilst this code works within the JUnit test, it fails here - it enters the JWTAuthConnectionManager.postToken() method that should be mocked and then fails by executing code within there. I've tried adding the lines:
#RunWith(PowerMockRunner.class)
#PrepareForTest(JWTAuthConnectionManager.class)
to both of the above classes (although of course I can't use RunWith in the Runner class as it already has one RunWith annotation), but this doesn't change anything.
How do I get PowerMock to work within Cucumber?

Seems like it is possible now with #PowerMockRunnerDelegate annotation. I use #RunWith(PowerMockRunner.class) and #PowerMockRunnerDelegate(Cucumber.class) and it's working. Taken an advise from here: https://medium.com/#WZNote/how-to-make-spock-and-powermock-work-together-a1889e9c5692
Since version 1.6.0 PowerMock has support for delegating the test execution to another JUnit runner without using a JUnit Rule. This leaves the actual test-execution to another runner of your choice. For example tests can delegate to “SpringJUnit4ClassRunner”, “Parameterized” or the “Enclosed” runner.
There are also options of using #Rule: PowerMockRule rule = new PowerMockRule(); instead of #RunWith(PowerMockRunner.class) (so Runner can be something else) - but the comment by Stefan Birkner suggests that Cucumber runner should support rules to use this and I am not sure if it does (now).
Hope it helps someone.

You can't use the PowerMockRunner because a test can only have one runner (in your case Cucumber). But AFAIK you can use the PowerMockRule instead of the PowerMockRunner.

Related

Change the behavior of a method to use in unit Test in Java

my problem is that my unit test are slow because I'm publishing in a topic in those unit test, I would like to mock or change its behavior in some way. I was thinking in use reflection for this class and change the method behavior but I'm not sure if that is possible.
This is the behavior that I like to mock or change:
TopicCall.builder()
.toTopic(XXXX)
.withAttribute(XXXXXX, XXXXX)
.withAttribute(XXXXX, XXXXXX)
.withAttribute(XXXXX,XXXXX)
.publish();
I would like to do this because publis() is a real invocation and the test is slow and causing some problems in jenkins, because several unit test are publishing at the same time.
The Topic class is a public class with a static builder method which return a class instance, just like the next one:
public static TopicCall builder() {
return new TopicCall();
}
My problem is that I just acceding the method of this class from outside and I'm not sending the class in the constructor as example and I'm not able to mock its behavior, I'm not able to modify the TopicCall class because it is a .class utility from a jar, besides that I'm not able to use PowerMockito or another library, just Mockito, is there any way to achieve that?
Thanks!
Disclaimer: I missed the fact that PowerMock is forbidden for the author, but the answer could be useful for other users with the same problem.
PowerMock
As far as you want to mock a static method, then Mockito is not the solution.
This could be done using PowerMock.
PowerMock uses ClassLoader way for mocking, which could significantly increase tests time to run.
Here is an examle on Baeldung how to mock static methods.
Solution scratch:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ TopicCall.class })
public class Test {
#Test
void test() {
mockStatic(TopicCall.class);
when(TopicCall.builder()).thenReturn(/*value to be returned*/ null);
// the test code...
}
}

Set environment variables in Junit5 for each test

I have to migrate tests from JUnit 4 to JUnit 5, I had #Rule which sets EnvironmentVariables variable. In Junit 4 variables were cleared for each test, in JUnit 5 i need the same behaviour, because now running tests are passing separately, but when I ran all tests from class then the second test fails because still has the same environment variables.
I had:
#Rule
public final EnvironmentVariables environmentVariables = new EnvironmentVariables();
#Test
void method1(){...}
#Test
void method2(){...}
I'm not familiar yet with Junit 5 so thanks in advance for any help
What you did with #Rule in JUnit 4 should be done with Extension that provides a very close feature in JUnit 5.
So you could create an Extension for example EnvironmentVariablesExtension that performed the logic of your #Rule and use it in your unit test by annotating your test class(es) #ExtendWith(EnvironmentVariablesExtension.class).
Note that if you use a single #Rule in a single unit test class, you could move this logic into the #BeforeEach lifecyle method that is invoked before each ran test.
If you have just few classes that uses this setup in a single project, you can still introduce an abstract base test class that does this setup in a #BeforeEach method and make your unit test classes inherit from this base class.
One of the greatest value of #Rule is when you want to define multiple rules for an unit test or as you want to reuse rules in other apps/projects. It it is not the case, don't feel constraint to introduce them as Extension in JUnit5 and keep things simple for the moment.
The EnvironmentVariables rule above looks like System Rules - https://github.com/stefanbirkner/system-rules
This was superseded by System Lambda - https://github.com/stefanbirkner/system-lambda which allows a variable to be set inside the body of a test:
withEnvironmentVariable("FOO", "bar")
.execute(() -> { ... test code } );
However, there is a new library called System Stubs - https://github.com/webcompere/system-stubs which contains the equivalent of the OP's code:
#ExtendWith(SystemStubsExtension.class)
class SomeTest {
#SystemStub
private EnvironmentVariables environmentVariables;
#Test
void someTest() {
environmentVariables.set("FOO", "bar");
// use environment
// environment cleared at end
}
}
The environment variables object can also be initialised in the #BeforeEach method or constructed explicitly to have values in the initializer list of the test class.
I think You have the problem with keyword final. Remove it, and it should work,
try like this:
#Rule
public EnvironmentVariables environmentVariables = new EnvironmentVariables();
final = define an entity that can only be assigned once
so from my perspective this is messing Your run.
Hope this helps,

Declaring Jmockit mock parameters on #BeforeMethod of TestNG

I've been testing my code behavior using TestNG and JMockit for a while now and I have had no specific issue with their combination. Today I came across a situation where I needed to mock one of my internal dependencies, in the so called, type wide manner and I did not need to keep that mock around since none of the test cases dealt with it directly while they counted on the mocked version functionality. So, naturally, I put the mocking logic in my #BeforeMethod. Here is a sample:
public class SampleTest
{
#Mocked
#Cascading
private InnerDependency dependency;
#BeforeMethod
public void beforeMethod()
{
new NonStrictExpectations()
{
{
dependency.getOutputStream((String)any);
result = new Delegate<OutputStream>()
{
public OutputStream getOutputStream(String url)
{
return null;
}
};
}
};
}
#Test
public void testNormalOperation()
{
// The test whose desired behavior depends on dependency being mocked out
// ..
}
}
But, since my tests do not care about the mocked dependency explicitly, I'm not willing to declare it as a test class field, unlike what is done above. To my knowledge of JMockit The only options remaining would be:
Declare dependency as a local mock field:
new NonStrictExpectations()
{
#Cascading
private InnerDependency dependency;
{
//...
}
}
Declare dependency as an input argument for beforeMethod(), similar to what is done for normal #Test methods:
#BeforeMethod
public void beforeMethod(#Mocked #Cascading final InnerDependency dependency)
{
// ...
}
I see that JMockit 1.6+ would not like the first option and warns with WARNING: Local mock field "dependency" should be moved to the test class or converted to a parameter of the test method. Hence, to keep everyone happy, I'm ruling this option out.
But for the second option, TestNG (currently 6.8.6) throws exception when running the test saying java.lang.IllegalArgumentException: wrong number of arguments. I don't see this behavior with normal #Test cases passed with #Mocked parameters. Even playing with #Parameter and #Optional will not help (and should not have!).
So, is there any way I could make this work without declaring the unneccessary test class mock field, or am I missing something here?
Thanks
Only test methods (annotated with #Test in JUnit or TestNG) support mock parameters, so the only choice here is to declare a mock field at the test class level.
Even if not used in any test method, I think it's better than having it declared in a setup method (using #Before, #BeforeMethod, etc.). If it were to be possible, the mock would still have to apply to all tests, because of the nature of setup methods; having a mock field of the test class makes it clear what the scope of the mock is.
Dynamic partial mocking is one more technique to specify #Mocked dependencies locally. However, it has it's limitations (see comments below).

How i can get the method in the setup method in spock?

how i can get the method of the running feature? I want to prepare the database with different datasets. This datasets should be defined with annotations like this:
#PrepareDB("dataset1")
def "feature 1"() {
}
and should be used in the setup method like this:
def setup() {
def dataset = currentTestMethod.getAnnotation().value //pseudo method
prepareDB(dataset)
}
I did the same with JUnit4. I used #Rule to get current method name and get the annotation value per reflection. How i can do this in spock?
Update:
I found a solution myself. With the TestWatcher from JUnit4 its possible to get an annotation fom the current running test method:
#Rule
public TestRule watcher = new TestWatcher() {
protected void starting(Description description) {
println description.getAnnotation(PrepareDB.class).value()
};
};
I recommend to implement the database logic as an annotation-driven Spock extension or a JUnit rule, both of which provide easy access to the annotations of the executed feature method.
To answer your question, to get at the annotation from the setup method, you'd use (as of Spock 0.7) specificationContext.iterationInfo.parent.featureMethod.reflection.getAnnotation(PrepareDB). In Spock 1.0-SNAPSHOT and beyond, this has changed to specificationContext.currentFeature.featureMethod.getAnnotation(PrepareDB).

Create JUnit TestSuite in a non-static way

I am seeking for a way to create and let run a JUnit TestSuite in a non-static fashion.
Currently I am doing something like this:
public class MyTestSuite {
public static TestSuite suite() {
TestSuite suite = new TestSuite();
suite.addTest(...);
suite.addTest(...);
// ....
return suite;
}
}
I am doing this because I am creating the TestCases I am adding to the suite programmatically.
With this solution I am facing the problem that my class MyTestSuite is never instantiated. I would like to wire it with a spring container, e.g. using
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={...})
#Transactional
but I see no way of telling the SpringJUnit4ClassRunner that it should also execute my programmatic tests.
Thanks for your help!
Erik
Why use a suite at all? Seems simpler to put your tests in their own subdirectory and have an ant (or whatever build tool you're using) target that runs just the tests found there.
You could try and have MyTestSuite as part of your spring context (the test context) and fire an init method on it which would add your programmatic tests. That would allow you to inject MyTestSuite which has this programmtic tests added when it is instantiated by spring.
Hope that helps.
For JUnit3-style suite methods, JUnit does not create an instance of the class; it calls the method and calls run(TestResult) on the returned object.
SpringJUnit4ClassRunner is a JUnit4 Runner class, so it cannot be used to affect the behavior of JUnit3-style test suites. Spring does not provide a JUnit4-style suite implementation. If you want each of the test cases to use SpringJUnit4ClassRunner, your best option is to upgrade them to JUnit4.
If you are asking how you add your Spring tests to MyTestSuite:
public class MyTestSuite {
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(...);
suite.addTest(...);
suite.addTest(new JUnit4TestAdapter(ExampleSpringTest.class));
// ....
return suite;
}
}

Categories