I have a bunch of test inputs that I would like to run and compare the output with expected:
#Test
void test () throws IOExeption {
for (File i : readDir()) {
File out = foo(i);
assertEquals(FileUtils.readLines(expected), FileUtils.readLines(out));
}
}
I would like to run the tests using JUnit. But if I do it like the above then JUnit will stop after encountering the first test failure. Is there a better way to do this other than making each file its own test case like below?
#Test
void test1 () throws IOExeption {
File i = readFile("1.txt");
File out = foo(i);
assertEquals(FileUtils.readLines(expected), FileUtils.readLines(out));
}
#Test
void test2 () throws IOExeption {
File i = readFile("2.txt");
File out = foo(i);
assertEquals(FileUtils.readLines(expected), FileUtils.readLines(out));
}
I think, that you could use Parameterized. This is standard feature of JUnit. Below you can see an example.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;
#RunWith(Parameterized.class)
public class Foo {
#Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] { { "1.txt" }, { "2.txt" } });
}
#Parameterized.Parameter // first data value (0) is default
public /* NOT private */ String fileName;
#Test
public void test() {
File i = readFile(fileName);
File out = foo(i);
assertEquals(FileUtils.readLines(expected), FileUtils.readLines(out));
}
}
I have not found JUnit official documentation about this, but you can find more details e.g. in this tutorial: https://www.tutorialspoint.com/junit/junit_parameterized_test.htm
Related
Public Class DailyJob(){
public void runJob(ScheduleJob currentJob) {
try {
int employee = employeeService.getEmployeeNum();
JobPerformance jobPerformance = performanceService.searchJobPerformance(employee);
if(jobPerformance.size() >0 ) {
currentJob.setRecord("success");
}
else {
currentJob.setRecord("failed");
}
}
catch{
//catch error
}
}
}
By now I want to write a Junit test for runJob(). But how can I setup the value I prefer into 'employee' parameter in Junit Test while I test runJob()? Anyone know how to implement it?
You can use Mockito. You setup stubbing, which will return desired value. Also you can stub for multiple calls by
.thenReturn(9, 10, 7);
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MyTest {
#Test
void test() {
EmployeeService employeeService = mock(EmployeeService.class);
when(employeeService.getEmployeeNum()).thenReturn(9);
}
}
While using parametrized JUnit tests in Eclipse, I'm running into a problem when I want to rerun a single test. The tests themselves run fine, and while I can rerun the first test from the context menu, rerunning the second test:
fails with the following message:
java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=test[1: A2 --> [Ljava.lang.String;#1e4a7dd4]], {ExactMatcher:fDisplayName=test[1: A2 --> Ljava.lang.String;#1e4a7dd4]] from org.junit.internal.requests.ClassRequest#6c3f5566
I'm pretty sure this is because JUnit doesn't 'like' my arrays; for some context: I'm using this to account for the fact that due to external circumstances, the code under test can produce one of two outcomes for a particular test case.
Here is some code to reproduce this:
package com.stackexchange.toolbox;
import java.util.ArrayList;
import java.util.Arrays;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
#RunWith(Parameterized.class)
public class Tester {
public Tester(String source, Object target) {
this.source = source;
this.target = target;
}
private final String source;
private final Object target;
private static final Object[][] testCases = { { "A1", "B1" }, { "A2", new String[] { "B2", "C2" } } };
#Parameters(name = "{index}: {0} --> {1}")
public static Iterable<Object[]> data() throws Exception {
return new ArrayList<>(Arrays.asList(testCases));
}
#Test
public void test() throws Exception {
if (target instanceof String) {
Assert.assertEquals(source.charAt(1), ((String)target).charAt(1));
} else {
for (String target : (String[])this.target) {
Assert.assertEquals(source.charAt(1), target.charAt(1));
}
}
}
}
Is there an easy way to fix this, perhaps with Lists or variadic arguments? Most of the (100+) test cases are simple 'source', 'target' entries, and I'd like to keep the conciseness of { "A1", "B1" }.
This seems to be a limitation of JUnit4 (at least you get the same error on the command line).
The simplest and straightforward solution would be to migrate from JUnit4 to JUnit5, which would also mean less code:
package com.stackexchange.toolbox;
import java.util.Arrays;
import java.util.stream.Stream;
import org.junit.Assert;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class Tester {
private static final Object[][] testCases = { { "A1", "B1" }, { "A2", new String[] { "B2", "C2" } } };
#ParameterizedTest(name = "{index}: {0} --> {1}")
#MethodSource("provideArguments")
void test(String source, Object target) {
if (target instanceof String) {
Assert.assertEquals(source.charAt(1), ((String)target).charAt(1));
} else {
for (String targetElement : (String[])target) {
Assert.assertEquals(source.charAt(1), targetElement.charAt(1));
}
}
}
static Stream<? extends Arguments> provideArguments() throws Exception {
return Arrays.stream(testCases).map(Arguments::of);
}
}
For example I have the following classes below:
public class TesteEstatico {
public static String teste(){
return "FOO";
}
}
And I have a class that uses her method:
public class UsaTesteEstatico {
public String metodoParaTeste1 (){
return TesteEstatico.teste() + " BAR ";
}
public String metodoParaTeste2 (){
return "FOO "+TesteEstatico.teste() + " BAR ";
}
}
Test class:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class) public class UsaTesteEstaticoTest {
#InjectMocks
UsaTesteEstatico usaTesteEstatico;
#Test
void teste1(){
Mockito.mockStatic(TesteEstatico.class);
Mockito.when(TesteEstatico.teste())
.thenReturn("BANANA");
String res = usaTesteEstatico.metodoParaTeste1();
System.out.println(res);
}
#Test
void teste2(){
Mockito.mockStatic(TesteEstatico.class);
Mockito.when(TesteEstatico.teste())
.thenReturn("LARANJA");
String res = usaTesteEstatico.metodoParaTeste2();
System.out.println(res);
}
}
Error I get when trying to run the tests:
org.mockito.exceptions.base.MockitoException:
For TesteEstatico, static mocking is already registered in the current thread
To create a new mock, the existing static mock registration must be deregistered
Versions of the libs that are in the project:
junit-jupiter 5.5.2
mockito-junit-jupiter 3.2.14
mockito-inline 3.2.14
Any idea how to solve this, i've tried a few things but nothing successful.
NOTE: I cannot change or add any new libraries as it is a restricted project.
You should use try-with-resources block in each of the tests to close the mockStatic.
public class UsaTesteEstaticoTest {
UsaTesteEstatico usaTesteEstatico = new UsaTesteEstatico();
#Test
void teste1(){
try (var ms = Mockito.mockStatic(TesteEstatico.class)) {
Mockito.when(TesteEstatico.teste()).thenReturn("BANANA");
String res = usaTesteEstatico.metodoParaTeste1();
System.out.println(res);
}
}
#Test
void teste2(){
try (var ms = Mockito.mockStatic(TesteEstatico.class)) {
Mockito.when(TesteEstatico.teste()).thenReturn("LARANJA");
String res = usaTesteEstatico.metodoParaTeste2();
System.out.println(res);
}
}
}
Note on mockStatic in #BeforeAll
Using #BeforeAll is a trap and bad advice.
You should strive for independent tests that don't affect each other.
This is not the case for mockStatic called in #BeforeAll, as stubbing from test methods outlive the test methods.
For example
// BAD CODE DONT USE
public class UsaTesteEstaticoTest {
UsaTesteEstatico usaTesteEstatico = new UsaTesteEstatico();
static MockedStatic<TesteEstatico> ms;
#BeforeAll
public static void init() {
ms = Mockito.mockStatic(TesteEstatico.class);
}
#AfterAll
public static void close() {
ms.close();
}
#Test
void teste1() {
Mockito.when(TesteEstatico.teste()).thenReturn("BANANA");
String res = usaTesteEstatico.metodoParaTeste1();
System.out.println(res);
}
#Test
void teste2() {
String res = usaTesteEstatico.metodoParaTeste2();
System.out.println(res);
}
}
teste2 prints:
FOO BANANA BAR if run after teste1
FOO null BAR if run separately
This is precisely what you want to avoid.
you need to use static block to mock it.
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
public class UsaTesteEstaticoTest {
#InjectMocks
UsaTesteEstatico usaTesteEstatico;
#BeforeAll
public static void init(){
Mockito.mockStatic(TesteEstatico.class);
}
#Test
void teste1(){
Mockito.when(TesteEstatico.teste())
.thenReturn("BANANA");
String res = usaTesteEstatico.metodoParaTeste1();
System.out.println(res);
}
#Test
void teste2(){
Mockito.when(TesteEstatico.teste())
.thenReturn("LARANJA");
String res = usaTesteEstatico.metodoParaTeste2();
System.out.println(res);
}
}
From the examples on the PowerMock homepage, I see the following example for partially mocking a private method with Mockito:
#RunWith(PowerMockRunner.class)
// We prepare PartialMockClass for test because it's final or we need to mock private or static methods
#PrepareForTest(PartialMockClass.class)
public class YourTestCase {
#Test
public void privatePartialMockingWithPowerMock() {
PartialMockClass classUnderTest = PowerMockito.spy(new PartialMockClass());
// use PowerMockito to set up your expectation
PowerMockito.doReturn(value).when(classUnderTest, "methodToMock", "parameter1");
// execute your test
classUnderTest.execute();
// Use PowerMockito.verify() to verify result
PowerMockito.verifyPrivate(classUnderTest, times(2)).invoke("methodToMock", "parameter1");
}
However, this approach does not seem to work when the private method we wish to mock is static. I wish to create a partial mock of the below class, with the readFile method mocked:
package org.rich.powermockexample;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import static com.google.common.io.Files.readLines;
public class DataProvider {
public static List<String> getData() {
List<String> data = null;
try {
data = readFile();
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
private static List<String> readFile() throws IOException {
File file = new File("/some/path/to/file");
List<String> lines = readLines(file, Charset.forName("utf-8"));
return lines;
}
}
Please could someone let me know how this can be achieved?
After doing a bit more research, it seems that PowerMockito.spy() and PowerMockito.doReturn() are what is required here:
package com.richashworth.powermockexample;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
#RunWith(PowerMockRunner.class)
#PrepareForTest({DataProvider.class})
public class ResultsWriterTest {
private static List<String> mockData = new ArrayList<String>();
private ResultsWriter resultsWriter;
#BeforeClass
public static void setUpOnce() {
final String firstLine = "Line 1";
final String secondLine = "Line 2";
mockData.add(firstLine);
mockData.add(secondLine);
}
#Before
public void setUp() {
resultsWriter = new ResultsWriter();
}
#Test
public void testGetDataAsString() throws Exception {
PowerMockito.spy(DataProvider.class);
PowerMockito.doReturn(mockData).when(DataProvider.class, "readFile");
final String expectedData = "Line 1\nLine 2\n";
final String returnedString = resultsWriter.getDataAsString();
assertEquals(expectedData, returnedString);
}
}
For further details and the complete code listing, check out my blog post here: https://richashworth.com/post/turbocharge-your-mocking-framework-with-powermock/
Test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest(DataProvider.class)
public class DataProviderTest {
#Test
public void testGetDataWithMockedRead() throws Exception {
mockStaticPartial(DataProvider.class, "readFile");
Method[] methods = MemberMatcher.methods(DataProvider.class, "readFile");
expectPrivate(DataProvider.class, methods[0]).andReturn(Arrays.asList("ohai", "kthxbye"));
replay(DataProvider.class);
List<String> theData = DataProvider.getData();
assertEquals("ohai", theData.get(0));
assertEquals("kthxbye", theData.get(1));
}
}
Class being tested (basically yours):
public class DataProvider {
public static List<String> getData() {
try {
return readFile();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static List<String> readFile() throws IOException {
File file = new File("/some/path/to/file");
return readLines(file, Charset.forName("utf-8"));
}
}
In general, only use static mocking for classes that are beyond your control (e.g. java.io.File). Since DataProvider and readFile are your own, refactor DataProvider into a proper class (i.e. make its methods non-static), pull out readFile into a helper object and then mock that. See this answer https://stackoverflow.com/a/8819339/116509.
I'm testing extensively with JUnit and sometimes - while debugging my code - I want (temporary) only run a single #Test of my #RunWith(Arquillian.class) test class. Currently I'm adding a #Ignore to all other tests and wondering if something like #IgnoreOther does exist.
Are there better solutions to ignore all other tests?
The simplest way is to replace all #Test to //###$$$#Test. Then when your debugging is finished replace //###$$$#Test to #Test.
Moreover typically IDEs allow running one test only. For example in Eclipse you can do it from Outline view.
Just my two cents. You can try to use Junit Rules as #srkavin suggested.
Here is an example.
package org.foo.bar;
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
public class SingleTestRule implements MethodRule {
private String applyMethod;
public SingleTestRule(String applyMethod) {
this.applyMethod = applyMethod;
}
#Override
public Statement apply(final Statement statement, final FrameworkMethod method, final Object target) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
if (applyMethod.equals(method.getName())) {
statement.evaluate();
}
}
};
}
}
package org.foo.bar;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
public class IgnoreAllTest {
#Rule
public SingleTestRule test = new SingleTestRule("test1");
#Test
public void test1() throws Exception {
System.out.println("test1");
}
#Test
public void test2() throws Exception {
Assert.fail("test2");
}
#Test
public void test3() throws Exception {
Assert.fail("test3");
}
}
Test rules (JUnit 4.7+) will help. For example, you can write a rule that ignores all #Test methods except one with a specific name.
The answer from srkavin (and mijer) is correct, but the code is deprecated from JUnit 4.9. The interface and the method signature have changed. I want to provide this for others interested in this issue.
public class IgnoreOtherRule implements TestRule
{
private String applyMethod;
public IgnoreOtherRule(String applyMethod){
this.applyMethod = applyMethod;
}
#Override
public Statement apply(final Statement statement, final Description description)
{
return new Statement()
{
#Override
public void evaluate() throws Throwable {
if (applyMethod.equals(description.getMethodName())) {
statement.evaluate();
}
}
};
}
}