So I'm trying to run tests that will evaluate certain properties of different websites. The actual evaluation is being handled by a pay-per-call resource, so I want to minimize the number of times I generate the resource. Also, I need this to run in JUnit to fit into a larger automated test suite.
I've been doing this with parameterized tests so far, but I just learned that they instantiate a new instance for each test method.
Now I'm trying to figure out a way to have the resource created just once for each parameter that is being fed into the constructor of my testing class. #BeforeClass does it just once, and #Before does it once before each test.
All the help topics I've been able to find have dealt with creating expensive resources once for all tests, but in this case I need the resource to be recreated for each new set of parameters.
I've written some example code / output below to better show what I'm looking for:
#RunWith(Parameterized.class)
public class MyTestClass {
private static Resource expensiveToCreateResource;
public MyTestClass(String url) {
System.out.println("Constructing resource for " + url);
expensiveToCreateResource = new Resource(url); //This is getting created 4x, which is wrong
}
#Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {{"url1"},{"url2"}});
}
#Test
public test1() {
expensiveToCreateResource.method1();
System.out.println("test1");
}
#Test
public test2() {
expensiveToCreateResource.method2();
System.out.println("test2");
}
}
would produce output:
Constructing resource for url1
test1
test2
Constructing resource for url2
test1
test2
Any ideas / solutions? Thanks.
If you want to have the class instantiated once per parameter, you'll have to write your own JUnit test runner. Instead I'd try to cache the information as needed, e.g. in a static map that maps URLs to resources.
Related
Using Junit version 5.9.2 I am trying to programmatically add parameter resolvers extension for a test class constructor with a #TestTemplate annotation.
I am trying to add the extensions programmatically using #RegisterExtension.
Example:
public class MyTestClass {
#RegisterExtension
static final TestDependencyResolver resolverExt = new TestDependencyResolver(/*...*/);
private final TestDependency dependency;
public MyTestClass(TestDependency dependency) {
this.dependency = dependency;
}
#TestTemplate
#ExtendWith(SomeContextProvider.class)
void test() {
//...
}
}
I have tried:
making resolverExt field non static
Movine #ExtendWith(SomeContextProvider.class) to class level
And other possible combinations of 1 and 2.
In all cases the ctor parameter dependency is not injected and TestDependencyResolver::resolveParameter is not called, which to my understanding means the object was created without/before registering TestDependencyResolver, please correct me if I am wrong.
Is what I am trying to achieve possible? thanks.
Turns out the issue was not Junit5 but TestTemplateInvocationContextProvider I was using.
I used PactVerificationInvocationContextProvider which seems to have a bug and throws NullPointerException when resolving Ctor params, I have opened an issue for it if you want more details.
I recently developed few Verticles from which I needed to make external API calls. To optimize the code, I moved code of calling APIs to one common Helper class. I am also passing Vertx instance from Verticle to Helper class. I am now trying to write Junit test case for the Helper class which is looking like below working code.
public class ServiceExecutionHelper{
public Promise<String> executeService(String requestURI, JsonObject input, MultiMap headers, Vertx vertx){
Promise<String> promise = Promise.promise();
WebClient client = WebClient.create(vertx);
client.postAbs(requestURI).timeout(60000).putHeaders(headers)
.sendJsonObject(input, ar -> {
if (ar.succeeded()) {
HttpResponse<Buffer> response = ar.result();
JsonObject serviceRespone = new JsonObject(response.bodyAsString());
JsonArray responseData = serviceRespone.getJsonArray("response_data");
if(responseData != null){
promise.complete("promise_completed");
}else{
promise.fail("promise_failed");
}
}
}
return promise;
}
}
Can anyone please guide how could I write test case for above code?
There are a million ways to do this depending on what exactly you need to test.
Here is one suggestion using junit5 and okhttp's MockWebServer. There are a lot of other conceivable alternatives.
The test verifies:
That you send a POST request using the payload contained in the input parameter.
That your implementation can handle a json response from the webserver.
That your implementation sends exactly one request to the webserver.
That your code completes the Promise if the server's response contains the key "promise_completed"
#ExtendWith(VertxExtension.class)
#Slf4j
public class ServiceExecutionHelperTest {
private ServiceExecutionHelper sut;
private MockWebServer mockWebServer;
#BeforeEach
public void setUp() {
sut = new ServiceExecutionHelper();
mockWebServer = new MockWebServer();
}
#Test
public void testExecuteService(final Vertx vertx, final VertxTestContext testContext) throws InterruptedException {
// given
final JsonObject requestPayload = new JsonObject().put("request", new JsonArray("[]"));
final JsonObject serverResponsePayload = new JsonObject().put("response_data", new JsonArray("[]"));
mockWebServer.enqueue(new MockResponse()
.setBody(serverResponsePayload.encode())
.setResponseCode(200)
.setHeader("content-type", "application/json"));
// when
final Promise<String> stringPromise =
sut.executeService(
mockWebServer.url("/").toString(),
requestPayload,
MultiMap.caseInsensitiveMultiMap(),
vertx);
// then
final RecordedRequest recordedRequest = mockWebServer.takeRequest();
assertEquals("POST", recordedRequest.getMethod());
assertEquals("[text={\"request\":[]}]", recordedRequest.getBody().toString());
assertEquals(1, mockWebServer.getRequestCount());
testContext.assertComplete(stringPromise.future())
.map(val -> {
assertEquals("promise_completed", val);
testContext.completeNow();
return val;
})
.onComplete(onComplete -> {
assertTrue(onComplete.succeeded());
log.info("done");
})
.onFailure(onError -> Assertions.fail());
}
}
Some words from a TDD point of view
Before you start writing tests (and your actual code too, if you ask me), you should clarify your functional and technical requirements.
These should be the basis for your tests. And the tests should be a starting point to implement your code against.
So I cannot promise you that this example is a correct test for your use case. It compiles and and runs. But it should be verified and extended following your actual requirements.
Concerning test coverage
To keep this answer short and concise, I did not write the test to cover all possible branches. The case where the server responds without response_data (i.e. the else branch of your if-clause, where the Promise fails) is not tested.
To cover that case, a second test or the usage of a parameterized test would be necessary.
I am trying to use PowerMockito to mock the creation of the java.net.URL class in my code that I'm testing. Basically, I want to prevent the real HTTP request from occurring and instead 1) check the data when the request is made and 2) supply my own test data back on a mocked response. This is what I'm trying:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ URL.class, MockedHttpConnection.class })
public class Test {
URL mockedURL = PowerMockito.mock(URL.class);
MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class);
...
PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL);
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
...
}
The code that I want to test looks like this:
URL wlInvokeUrl = new URL(wlInvokeUrlString);
connection = (HttpURLConnection) wlInvokeUrl.openConnection();
Earlier in my test scenario I mock the wlInvokeUrlString to match "MyURLString". I've also tried using various other forms of the whenNew line, trying to inject the mock. No matter what I try, it never intercepts the constructor. All I want to do is "catch" the call to openConnection() and have it return my mocked HTTP connection instead of the real one.
I have mocked other classes ahead of this one in the same script and these are working as expected. Either I need a second pair of eyes (probably true) or there is something unique about the URL class. I did notice that if I use "whenNew(URL.class).withAnyArguments()" and change the "thenReturn" to "thenAnswer" I could get it to trigger. Only problem is I never get the URL call for my code. What I see is an invocation of the 3-argument constructor for URL.class with all nulls for the parameters. Could it be this class is from the Java runtime and is bootstrapped by the test runner? Any help is much appreciated.
It's a common mistake when use PowerMockito.whenNew.
Note that you must prepare the class creating the new instance of MyClass for test, not the MyClass itself. E.g. if the class doing new MyClass() is called X then you'd have to do #PrepareForTest(X.class) in order for whenNew to work
From Powermock wiki
So, you need a bit change your test and add to #PrepareForTesta class which create a new instance of URLlike:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ URL.class, MockedHttpConnection.class , ConnectionUser.class})
public class URLTest {
public class URLTest {
private ConnectionUser connectionUser;
#Before
public void setUp() throws Exception {
connectionUser = new ConnectionUser();
}
#Test
public void testName() throws Exception {
URL mockedURL = PowerMockito.mock(URL.class);
MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class);
PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL);
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
connectionUser.open();
assertEquals(mockedConnection, connectionUser.getConnection());
}
}
where:
public class ConnectionUser {
private String wlInvokeUrlString = "MyURLString";
private HttpURLConnection connection;
public void open() throws IOException {
URL wlInvokeUrl = new URL(wlInvokeUrlString);
connection = (HttpURLConnection) wlInvokeUrl.openConnection();
}
public HttpURLConnection getConnection() {
return connection;
}
}
I'm not sure the difference between .withParameterTypes(x) and .withArguments(x) but I believe you need to set it up as follows for your code to work. Give it a try and let me know if this doesn't help.
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
PowerMockito.whenNew(URL.class).withArguments(Mockito.anyString()).thenReturn(mockedURL);
The problem is that the arguments of the real call are not matching with the expected in your mock.
PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL) will return mockedURL only the call is new URL("MyURLString").
If you change it to:
PowerMockito.whenNew( URL.class ).withParameterTypes( String.class )
.withArguments( org.mockito.Matchers.any( String.class ) ).thenReturn( mockedURL );
It will catch any string passed to the constructor URL(String) (even null) and return your mock.
When you tried
"whenNew(URL.class).withAnyArguments()" and change the "thenReturn" to
"thenAnswer" I could get it to trigger. Only problem is I never get
the URL call for my code. What I see is an invocation of the
3-argument constructor for URL.class with all nulls for the
parameters.
PowerMock will try to mock all constructors (org.powermock.api.mockito.internal.expectation.DelegatingToConstructorsOngoingStubbing.InvokeStubMethod at line 122) then it calls the first one (with 3 arguments) and mock its answer. But the subsequent calls will return the already mocked one because you told it to mock for any arguments. That's why you see just one call with null, null, null in your Answer.
I wanted to know if there's any way to add test suites dynamically in junit 4.
For example I have a TestClassA as mentioned below having test case "test1"
class TestClassA
{
#Test
public void test1()
{
createTestClassDynamically(); // this creates a test class having
// setUp(), tearDown() methods and one test case .
}
}
Test case test1 has a method createTestClassDynamically() that dynamically creates a new test class (lets say TestClassB) having setUp(), tearDown() methods and one test case (lets say test2()).
I want to run the test1 and then when TestClassB is dynamically generated I want test case "test2" also to be executed.
I know this is quite complicated and not the best thing to do but in my framework I need to do it to generate large number of test classes dynamically rather than having them physically in the package.
Can anyone please provide any help/suggestions?
I have solved this is my framework using the Parameterized feature of Junit 4 which helps to execute same test case with different parameters.
Below mentioned is the sample code on how I acheived it, thought to post it if it helps anyone.
Also, if someone has a better solution, feel free to post it.
class TestClassA
{
private TestClassB classBObj;
public TestClassA(TestClassB obj) {
classBObj= obj;
}
#Test
public void test1()
{
// createTestClassDynamically(); // remove this method as Parameterized
// feature will take care of dynamic test execution.
}
#Test
public void test2()
{
// Test case from Test class B using TestClassB object (classBObj)
}
public static Collection<Object[]> getParameters() {
Collection<Object[]> parameteres = new ArrayList<Object[]>();
Object[] obj1 = new Object[]{new TestClassB()};
Object[] obj2 = new Object[]{new TestClassB()};
parameteres.add(obj1);
parameteres.add(obj2);
// ....... add more test data this way or create a loop
return parameteres;
}
}
Hi I am new to unit testing. Is it possible to access methods that are private?
A very simple example
ObjectA
----------
File file;
private void setupFile (){
//do something
file = "C:\file.dat"
}
In TestCase
File sth = ObjectA.setupFile();
assertNotNull(sth);
I am unable to test whether the file variable is null in method ObjectA.setup()
as I cannot run ObjectA.setupFile()
I am not sure about whether doing like this make sense in terms of unit testing.
So is that a better practice to write every method returning sth and set them public for easier unit testing?
Thanks in advance
In general, you should avoid changing the access of a method/field to enable testing. If you do this then you risk developers using the method directly.
However, if you do need to, then making it protected as Deco says is a good way, so it's accessible from the JUnit tests. If you do this, make sure that it is well documented that this is an method for internal use.
A better way is to test the behaviour of the public methods; you shouldn't care about internal implementation details of a class, so you should only be testing public methods. It's hard to tell from your code, but presumably, the setupFile() has effects later on other methods, so you can test those effects, not the fact that file is not null.
External dependencies (such as dependencies on file system, environment variables) can be worked around in your tests, or injected directly into the class. For the general principle, see my answer to How to test code dependent on environment variables using JUnit?
If it is not absolutely necessary to have the method as private, you can have it as package private (i.e. default access) so that you can call it directly in a JUnit test.
Package private methods can only be used in the package that they are declared, and do not become part of the API of the class. You declare a method package private by putting no modifier on it's declaration.
Here's an example to demonstrate:
public class MyClass() {
int foo;
public MyClass() {
this.foo = 0;
}
void notSoComplexCalculationMethod(int a) {
foo = a * 2;
}
//Other methods here . . .
}
public class MyClassTest extends TestCase {
private MyClass myClass;
protected void setUp() {
super.setUp();
myClass = new MyClass();
}
public void testNotSoComplexCalculationMethod() {
int a = 2;
assertEquals(4, myClass.notSoComplexCalculationMethod(a));
//Unit test passes, yay! Now you've tested a package private method.
}
}