With JUnit, the description of my test cases would normally in the method name, like:
#Test
public void should_return_6_if_distance_is_2km() {
}
or
#Test
public void shouldReturn6IfDistanceIs2km() {
}
Which is not quite readable.
But with the testing frameworks of other lanuage, like javascript, or scala, I can use:
describe("Taximeter") {
it("should return 6 if the distance is 2km") {
...
}
}
which is more readable with plain strings.
Is it possible to do this in Java?
Using a good Assertion Framework, like Hamcrest, AssertJ or Truth will automatically result in better error messages at least. And of course, the assertions themselves will be much more readable than just using assertTrue, assertEquals, etc.
For example, AssertJ:
assertThat( myVar ).describedAs( "myVar" ).isEqualTo( 6 );
This will result in an error message (if myVar != 6) that includes the name "myVar", the expected value and the real value.
Spectrum is quite close to my requirement:
#RunWith(Spectrum.class)
public class ExampleSpec {{
describe("A spec", () -> {
final int foo = 1;
it("is just a code block with a run() method", new Block() {
#Override
public void run() throws Throwable {
assertEquals(1, foo);
}
});
it("can also be a lambda function, which is a lot prettier", () -> {
assertEquals(1, foo);
});
it("can use any assertion library you like", () -> {
org.junit.Assert.assertEquals(1, foo);
org.hamcrest.MatcherAssert.assertThat(true, is(true));
});
describe("nested inside a second describe", () -> {
final int bar = 1;
it("can reference both scopes as needed", () -> {
assertThat(bar, is(equalTo(foo)));
});
});
it("can have `it`s and `describe`s in any order", () -> {
assertThat(foo, is(1));
});
});
}
The assert methods in JUnit allow you to do something similar. You could do something like:
assertTrue("Should return 6 if the distance is 2km but returned " + myvar, myvar == 6);
This allows your code to be readable but only returns a message if the expected condition isn't met.
Not sure about the Scala-Part, is this just a Description or runnable Testcode?
If Description:
For readable but automateable Testcase-Descriptions check out
cucumber (https://cucumber.io) or jBehave (http://jbehave.org)
If runnable Code:
To try to make the code of the Testcase itself more readable hamcrest (https://code.google.com/p/hamcrest/wiki/Tutorial) could be an option.
You have to write your own Runner for this and use it with #RunWith.
I have been wondering the same thing for a long time. The closest I have found to the "describe"-"it" way of organizing tests is the NestedRunner. Check it out here
It is not the same and has some limitations, but definitely an improvement.
Just saw the link OP posted - Spectrum is even better! I am definitely trying it out asap. Thanks for sharing :)
Unit tests written with Mockito framework are pretty readable. The docs promise the ability to “write beautiful tests” that are “more readable” and “produce clean verification errors.” See for yourself:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.eq;
#RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
#Mock
private UserRepository userRepositoryMock;
#InjectMocks
private UserService userService = new UserService();
#Test
public void testLoadUserByUsername() {
// Expected objects
User demoUser = new User("user", "demo", "ROLE_USER");
// Mockito expectations
when(userRepositoryMock.findByUsername("user")).thenReturn(demoUser);
// Execute the method being tested
User userDetails = userService.loadUserByUsername("user");
// Validation
assertThat(userDetails.getUsername(), eq("user"));
verify(userRepositoryMock).findByUsername("user");
}
}
To have Mockito available with Maven the following dependency should be used:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.8.47</version>
<scope>test</scope>
</dependency>
And to have Mockito available with Gradle the following dependency should be used:
dependencies { testCompile "org.mockito:mockito-core:2.+" }
Related
I have written some unit tests for a static method. The static method takes only one argument. The argument's type is a final class. In terms of code:
public class Utility {
public static Optional<String> getName(Customer customer) {
// method's body.
}
}
public final class Customer {
// class definition
}
So for the Utility class I have created a test class UtilityTests in which I have written tests for this method, getName. The unit testing framework is TestNG and the mocking library that is used is Mockito. So a typical test has the following structure:
public class UtilityTests {
#Test
public void getNameTest() {
// Arrange
Customer customerMock = Mockito.mock(Customer.class);
Mockito.when(...).thenReturn(...);
// Act
Optional<String> name = Utility.getName(customerMock);
// Assert
Assert.assertTrue(...);
}
}
What is the problem ?
Whereas the tests run successfully locally, inside IntelliJ, they fail on Jenkins (when I push my code in the remote branch, a build is triggered and unit tests run at the end). The error message is sth like the following:
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class
com.packagename.Customer Mockito
cannot mock/spy because :
- final class
What I tried ?
I searched a bit, in order to find a solution but I didn't make it. I note here that I am not allowed to change the fact that Customer is a final class. In addition to this, I would like if possible to not change it's design at all (e.g. creating an interface, that would hold the methods that I want to mock and state that the Customer class implements that interface, as correctly Jose pointed out in his comment). The thing that I tried is the second option mentioned at mockito-final. Despite the fact that this fixed the problem, it brake some other unit tests :(, that cannot be fixed in none apparent way.
Questions
So here are the two questions I have:
How that is possible in the first place ? Shouldn't the test fail both locally and in Jenkins ?
How this can be fixed based in the constraints I mentioned above ?
Thanks in advance for any help.
An alternative approach would be to use the 'method to class' pattern.
Move the methods out of the customer class into another class/classes, say CustomerSomething eg/CustomerFinances (or whatever it's responsibility is).
Add a constructor to Customer.
Now you don't need to mock Customer, just the CustomerSomething class! You may not need to mock that either if it has no external dependencies.
Here's a good blog on the topic: https://simpleprogrammer.com/back-to-basics-mock-eliminating-patterns/
How that is possible in the first place? Shouldn't the test fail both locally and in Jenkins ?
It's obviously a kind of env-specifics. The only question is - how to determine the cause of difference.
I'd suggest you to check org.mockito.internal.util.MockUtil#typeMockabilityOf method and compare, what mockMaker is actually used in both environments and why.
If mockMaker is the same - compare loaded classes IDE-Client vs Jenkins-Client - do they have any difference on the time of test execution.
How this can be fixed based in the constraints I mentioned above?
The following code is written in assumption of OpenJDK 12 and Mockito 2.28.2, but I believe you can adjust it to any actually used version.
public class UtilityTest {
#Rule
public InlineMocksRule inlineMocksRule = new InlineMocksRule();
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Test
public void testFinalClass() {
// Given
String testName = "Ainz Ooal Gown";
Client client = Mockito.mock(Client.class);
Mockito.when(client.getName()).thenReturn(testName);
// When
String name = Utility.getName(client).orElseThrow();
// Then
assertEquals(testName, name);
}
static final class Client {
final String getName() {
return "text";
}
}
static final class Utility {
static Optional<String> getName(Client client) {
return Optional.ofNullable(client).map(Client::getName);
}
}
}
With a separate rule for inline mocks:
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.internal.util.MockUtil;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class InlineMocksRule implements TestRule {
private static Field MOCK_MAKER_FIELD;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
VarHandle modifiers = lookup.findVarHandle(Field.class, "modifiers", int.class);
MOCK_MAKER_FIELD = MockUtil.class.getDeclaredField("mockMaker");
MOCK_MAKER_FIELD.setAccessible(true);
int mods = MOCK_MAKER_FIELD.getModifiers();
if (Modifier.isFinal(mods)) {
modifiers.set(MOCK_MAKER_FIELD, mods & ~Modifier.FINAL);
}
} catch (IllegalAccessException | NoSuchFieldException ex) {
throw new RuntimeException(ex);
}
}
#Override
public Statement apply(Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
Object oldMaker = MOCK_MAKER_FIELD.get(null);
MOCK_MAKER_FIELD.set(null, Plugins.getPlugins().getInlineMockMaker());
try {
base.evaluate();
} finally {
MOCK_MAKER_FIELD.set(null, oldMaker);
}
}
};
}
}
Make sure you run the test with the same arguments. Check if your intellij run configurations match the jenkins. https://www.jetbrains.com/help/idea/creating-and-editing-run-debug-configurations.html. You can try to run test on local machine with the same arguments as on jenkins(from terminal), if it will fail that means the problem is in arguments
I'm currently doing my first Java project and like to fully TDD it. I'm using JUnit for writing the tests. Apparently JUnit does not provide support for data providers, which makes it rather annoying to test the same method with 20 different versions of an argument. What is the most popular/standard testing tool for Java that does support data providers? I came across TestNG, but have no idea how popular that one is, or how it compares to alternatives.
If there is a way to get this behaviour is a nice way using JUnit, then that might also work.
Coworkers of mine at our company wrote a freely available DataProvider in TestNG style for JUnit which you can find on github (https://github.com/TNG/junit-dataprovider).
We use it in very large projects and it works just fine for us. It has some advantages over JUnit's Parameterized as it will reduce the overhead of separate classes and you can execute single tests as well.
An example looks something like this
#DataProvider
public static Object[][] provideStringAndExpectedLength() {
return new Object[][] {
{ "Hello World", 11 },
{ "Foo", 3 }
};
}
#Test
#UseDataProvider( "provideStringAndExpectedLength" )
public void testCalculateLength( String input, int expectedLength ) {
assertThat( calculateLength( input ) ).isEqualTo( expectedLength );
}
Edit: Since v1.7, it also supports other ways to provide data (strings, lists) and can inline the provider so that a separate method is not necessarily needed.
A full, working example can be found on the manual page on github. It also has a few more features, like collecting the providers in utility classes and accessing them from other classes etc. The manual page is very detailed, I'm sure you'll find any questions answered there.
JUnit 4 has parameterized test which is the does the same thing as php data providers
#RunWith(Parameterized.class)
public class MyTest{
#Parameters
public static Collection<Object[]> data() {
/*create and return a Collection
of Objects arrays here.
Each element in each array is
a parameter to your constructor.
*/
}
private int a,b,c;
public MyTest(int a, int b, int c) {
this.a= a;
this.b = b;
this.c = c;
}
#Test
public void test() {
//do your test with a,b
}
#Test
public void testC(){
//you can have multiple tests
//which all will run
//...test c
}
}
Depending on your needs in flexibility vs readability, you can choose Parameterized - junit's built in option, described by dkatzel. Other options are external junit runners provided by external libraries like zohhak, which let's you do:
#TestWith({
"clerk, 45'000 USD, GOLD",
"supervisor, 60'000 GBP, PLATINUM"
})
public void canAcceptDebit(Employee employee, Money money, ClientType clientType) {
assertTrue( employee.canAcceptDebit(money, clientType) );
}
or junitParams with a bit different functionality. just pick whatever suits you the most
You can use JUnit 5's ParameterizedTest. Here's an example from https://www.petrikainulainen.net/programming/testing/junit-5-tutorial-writing-parameterized-tests/ :
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
#DisplayName("Should pass the method parameters provided by the sumProvider() method")
class MethodSourceExampleTest {
#DisplayName("Should calculate the correct sum")
#ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}")
#MethodSource("sumProvider")
void sum(int a, int b, int sum) {
assertEquals(sum, a + b);
}
private static Stream<Arguments> sumProvider() {
return Stream.of(
Arguments.of(1, 1, 2),
Arguments.of(2, 3, 5)
);
}
}
It's possible to load test parameters from an annotation, a method or even a CSV file.
Here is another option. You don't have to use Google Guava, that is just my implementation.
This uses the same #Parameters as #dkatzel's answer, but instead of the class taking the arguments, the #Parameters annotation goes on specific test methods, so you can pick and choose which methods use that set of arguments.
import java.util.Collection;
import com.google.common.collect.ImmutableList;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
#RunWith(JUnitParamsRunner.class)
public class FrobTester {
#SuppressWarnings("unused")
private Collection validfrobAndGorpValues() {
return ImmutableList.of(
new Object[] {"frob28953", 28953},
new Object[] {"oldfrob-189-255", 1890255}
);
}
#Test
#Parameters(method = "validfrobAndGorpValues")
public void whenGivenFrobString_thenGorpIsCorrect(
String frobString,
int expectedGorpValue
) {
// Arrange
Frob frob = new Frob(frobString);
// Act
var actualGorpValue = frob.getGorpValue();
// Assert
Assert.assertEquals(actualGorpValue, expectedGorpValue);
}
}
I've been trying for a while to mock my codes. I'm newby on mocking so lots of things ahead of me to catch. I'm using Powermockito with Mockito and Easymock integration.
I'm having difficulties with mocking a method which is inside the method I wanted to test. So here is an example of the situation I faced:
public class trialClass {
public static int try2(){
return 3;
}
public static int try(int a){
return try2() + a;
}
}
and my test class is:
#RunWith(PowerMockRunner.class)
#PrepareForTest(trial.class)
public class trialTest {
#Before
public void setUp() throws Exception {
PowerMockito.mockStatic(trial.class);
//Here I expect try2() to return 10, even it return 3
PowerMockito.when(trial.try2()).thenReturn(10);
}
#Test
public void testtry() throws Exception {
//After try2() returns 10 recursively inside my try() method,
//I expect result to be 11
Assert.assertEquals(11, trial.try(1));
}
}
This question of mine actually comes from my session variable. My session holds some value and an X method returns that value. All I need is to mock that X method recursively and this question just simulates this case.
Thanks for your help guys.
You need to use Mockito.CALLS_REAL_METHODS
So in your test setup:
PowerMockito.mockStatic(trial.class, CALLS_REAL_METHODS);
EDIT
It occurred to me that you might not want to change to EasyMock instead of Mockito, in that case please disregard...
Partial mocking is the keyword you are after. You don't want to mock everything, just try2(). You should use the PowerMock.mockStaticPartial(Class, String...) method.
Instead of
PowerMockito.mockStatic(trialClass.class);
Use
PowerMock.mockStaticPartial(trialClass.class, "try2");
And then do the actual mocking.
Also note that you defined the clas as trialClass in the code above, but use trial.class in the second code...
Please find an updated version of your TrialTest.java using EasyMock with PowerMock as a solution. PowerMock is easy to configure and does not interfere with most existing jars. You only need a few JARS
powermock-easymock-X.X.X-full.jar
Easymock-X.X.jar
You had some issues with your code that I fixed:
Code Issue: try is a Java keyword so it cannot be used in method name (e.g., try(int a)).
Code Quality: Use Java Object instead of Java primitive (e.g., Use Integer instead of int).
Code Quality: trialClass is a poor Java class name (e.g., Use upperCase for Java class name, don't use the generic word Class unless for educational purposes), maybe Trial.
Here's the updated code:
Trial.java (CUT)
public class Trial {
public static Integer try2() {
return 3;
}
public static Integer try1(int a) {
return try2() + a;
}
}
Working Test Class: TrialTest.java
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(Trial.class)
public class TrialTest {
#Before
public void setUp() throws Exception {
/* Setup */
PowerMock.mockStaticPartial(Trial.class, "try2");
/* Mocks */
// Here I expect try2() to return 10, even it return 3
EasyMock.expect(Trial.try2()).andReturn(10).atLeastOnce();
PowerMock.replayAll();
}
#Test
public void testtry() throws Exception {
// After try2() returns 10 recursively inside my try() method,
// I expect result to be 11
/* Test */
Integer result = Trial.try1(1);
/* Asserts */
PowerMock.verifyAll();
Assert.assertEquals(new Integer(11), result);
}
}
How can I make the 3rd test to check for the existence of cause1 in the message of the exception? I also listed in the first two tests that have drawbacks. First is not checking for the message second needs a lot of boilerplate code.
public class CheckExceptionsWithMockitoTest {
#Test(expected = RuntimeException.class)
public void testExpectedException1() {
A a = new A();
a.doSomethingThatThrows();
}
#Test
public void testExpectedException2() {
A a = new A();
try {
a.doSomethingThatThrows();
fail("no exception thrown");
} catch (RuntimeException e) {
assertThat(e.getMessage(), org.hamcrest.Matchers.containsString("cause1"));
}
}
#Test
public void testExpectedException3() {
A a = new A();
A spyA = org.mockito.Mockito.spy(a);
// valid but doesnt work
// doThrow(new IllegalArgumentException()).when(spyA).doSomethingThatThrows();
// invalid but in the spirit of what i want
//chekThrow(RuntimeException.class,containsString("cause1")).when(spyA).doSomethingThatThrows();
}
}
I couldn't find in Mockito something that works but there is something that looks like could be possible (at the level of syntax) and capabilities.
Using catchexception I created the test like this
import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionHamcrestMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.*;
public class CheckExceptionsWithMockitoTest{
//...
#Test
public void testExpectedException3() {
A a = new A();
verifyException(a,IllegalArgumentException.class)
.doSomethingThatThrows();
//if more details to be analized are needed
assertThat(
(IllegalStateException) caughtException(),
allOf(
is(IllegalStateException.class),
hasMessageThat(
containsString("is not allowed to add counterparties")),
hasNoCause()));
//more asserts could come
assertNotNull(a);
}
}
Use catch-exception library, or I guess that the solution you are looking for is your second implementation.
#expected doesn't provide any way to assert on the thrown exception except for its class, so you can't avoit try/catching (not that much boiler plate code !)
Mockito doesn't provide something likes a verifyThrows method.
So you can trade try/catching for an additional library : using catch-exception, you'll be able to catch exception in a single line and have it ready for further assertion(s).
Sample source code
A a = new A();
when(a).doSomethingThatThrows();
then(caughtException())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("is not allowed to add counterparties")
.hasNoCause();
Dependencies
'com.googlecode.catch-exception:catch-exception:1.2.0'
If A is your system under test, it doesn't make any sense to mock it, and it rarely makes sense to spy on it. Your implementation in testExpectedException2 is the right one; the boilerplate code is necessary because without a try block Java will not let any code run after the method is intercepted (as I described in this previous SO answer).
Though Mockito won't be any help, JUnit will. The #Test(expected=foo) parameter actually has a more-flexible alternative, the built-in ExpectedException JUnit rule:
public class CheckExceptionsWithMockitoTest {
#Rule public ExpectedException thrown = ExpectedException.none();
#Test
public void testExpectedException1() {
A a = new A();
thrown.expect(RuntimeException.class);
thrown.expectMessage(containsString("cause1"));
a.doSomethingThatThrows();
}
}
Mockito would come in handy in a separate test checking whether your method wraps an arbitrary exception while preserving its message, which would look roughly like this:
#Test
public void doSomethingShouldWrapExceptionWithPassedMessage() {
Dependency dependency = Mockito.mock(Dependency.class);
when(dependency.call()).thenThrow(new IllegalArgumentException("quux"));
A a = new A(dependency);
thrown.expect(RuntimeException.class);
thrown.expectMessage(containsString("quux"));
a.doSomethingThatThrows();
}
Be careful to avoid the temptation to make this a common pattern in your tests. If you are catching an exception thrown from your system under test, you're effectively ceding control back to the SUT's consumer. There should be little left to test in the method afterwards, except the properties of the exception and MAYBE the state of your system, both of which should be rare enough that try/catch boilerplate is forgivable.
If you have the opportunity to use scala, scalaTest's fun suite has concise way of testing exceptions using intercept (http://www.scalatest.org/getting_started_with_fun_suite).
It's as simple as
test(a list get method catches exceptions){
intercept[IndexOutBoundsException]{
spyListObject.get(-1)
}
}
You could potentially write your tests to your java project in scala if you are looking for easy to write / clear test. But this may present other challenges.
Updated answer for 06/19/2015 (if you're using java 8)
Using assertj-core-3.0.0 + Java 8 Lambdas
#Test
public void shouldThrowIllegalArgumentExceptionWhenPassingBadArg() {
assertThatThrownBy(() -> myService.sumTingWong("badArg"))
.isInstanceOf(IllegalArgumentException.class);
}
Reference: http://blog.codeleak.pl/2015/04/junit-testing-exceptions-with-java-8.html
Using catchexception I created the test like this
import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionHamcrestMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.*;
public class CheckExceptionsWithMockitoTest{
//...
#Test
public void testExpectedException3() {
A a = new A();
verifyException(a,IllegalArgumentException.class)
.doSomethingThatThrows();
//if more details to be analized are needed
assertThat(
(IllegalStateException) caughtException(),
allOf(
is(IllegalStateException.class),
hasMessageThat(
containsString("is not allowed to add counterparties")),
hasNoCause()));
//more asserts could come
assertNotNull(a);
}
}
If you have a look in Mockito.class on spy method it creates mock with spiedInstance:
public static <T> T spy(T object) {
return MOCKITO_CORE.mock((Class<T>) object.getClass(), withSettings()
.spiedInstance(object)
.defaultAnswer(CALLS_REAL_METHODS));
}
In MockSettings it is possible to register Invocation listeners: https://static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/listeners/InvocationListener.html
I created simple listener which stores all reported invocations:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.mockito.listeners.InvocationListener;
import org.mockito.listeners.MethodInvocationReport;
public class StoringMethodInvocationListener implements InvocationListener {
private List<MethodInvocationReport> methodInvocationReports = new ArrayList<>();
#Override
public void reportInvocation(MethodInvocationReport methodInvocationReport) {
this.methodInvocationReports.add(methodInvocationReport);
}
public List<MethodInvocationReport> getMethodInvocationReports() {
return Collections.unmodifiableList(methodInvocationReports);
}
}
After the invocation you can go through reports and find the one needed and verify that stored throwable is the one expected.
Example:
StoringMethodInvocationListener listener = new StoringMethodInvocationListener();
Consumer mock2 = mock(Consumer.class, withSettings()
.spiedInstance(consumerInstance)
.defaultAnswer(CALLS_REAL_METHODS)
.invocationListeners(listener));
try {
mock2.listen(new ConsumerRecord<String, String>(RECEIVER_TOPIC, 0, 0, null, "{}"));
} catch (Exception e){
//nothing
}
Assert.notEmpty(listener.getMethodInvocationReports(), "MethodInvocationReports list must not be empty");
Assert.isInstanceOf(BindException.class, listener.getMethodInvocationReports().get(1).getThrowable());
Can anyone make any suggestions about how best to use EasyMock to expect a call to Runtime.getRuntime().exec(xxx)?
I could move the call into a method in another class that implements an interface, but would rather not in an ideal world.
interface RuntimeWrapper {
ProcessWrapper execute(String command) throws IOException;
}
interface ProcessWrapper {
int waitFor() throws InterruptedException;
}
I was wondering if anyone had any other suggestions?
Your class shouldn't call Runtime.getRuntime(). it should expect a Runtime to be set as its dependency, and work with it. Then in your test you can easily provide a mock and set it as a dependency.
As a sidenote, I'd suggest watching this lecture on OO Design for testability.
Update: I didn't see the private constructor. You can try using java bytecode instrumentation in order to add another constructor or make the constructor public, but that might turn out to be impossible as well (if there are some restrictions on that class).
So your option is to make a wrapper (as you suggested in the question), and follow the dependency-injection approach.
Bozho above is IMO the Correct Solution. But it is not the only solution. You could use PowerMock or JMockIt.
Using PowerMock:
package playtest;
public class UsesRuntime {
public void run() throws Exception {
Runtime rt = Runtime.getRuntime();
rt.exec("notepad");
}
}
package playtest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.legacy.PowerMockRunner;
import static org.powermock.api.easymock.PowerMock.*;
import static org.easymock.EasyMock.expect;
#RunWith(PowerMockRunner.class)
#PrepareForTest( { UsesRuntime.class })
public class TestUsesRuntime {
#Test
public void test() throws Exception {
mockStatic(Runtime.class);
Runtime mockedRuntime = createMock(Runtime.class);
expect(Runtime.getRuntime()).andReturn(mockedRuntime);
expect(mockedRuntime.exec("notepad")).andReturn(null);
replay(Runtime.class, mockedRuntime);
UsesRuntime sut = new UsesRuntime();
sut.run();
}
}
Perhaps instead of mocking Runtime.getRuntime().exec() you could "mock" the script/program/etc. it's supposed to be calling.
Instead of passing the real command-line string into exec(), write a test script and execute it instead. You could have the script return hard-coded values you could test against just like a mocked class.
Here is how you would do it with EasyMock 3.0 (and JUnit 4):
import org.junit.*;
import org.easymock.*;
import static org.easymock.EasyMock.*;
public final class EasyMockTest extends EasyMockSupport
{
#Test
public void mockRuntimeExec() throws Exception
{
Runtime r = createNiceMock(Runtime.class);
expect(r.exec("command")).andReturn(null);
replayAll();
// In tested code:
r.exec("command");
verifyAll();
}
}
The only problem with the test above is that the Runtime object needs to be passed to code under test, which prevents it from using Runtime.getRuntime().
With JMockit, on the other hand, the following test can be written, avoiding that problem:
import org.junit.*;
import mockit.*;
public final class JMockitTest
{
#Test
public void mockRuntimeExec() throws Exception
{
final Runtime r = Runtime.getRuntime();
new NonStrictExpectations(r) {{ r.exec("command"); times = 1; }};
// In tested code:
Runtime.getRuntime().exec("command");
}
}