มีวิธีตั้งชื่อกรณีทดสอบแบบกำหนดเองของฉันเองเมื่อใช้การทดสอบแบบกำหนดพารามิเตอร์ใน JUnit4 หรือไม่
ฉันต้องการเปลี่ยนค่าเริ่มต้น[Test class].runTest[n]
- เป็นสิ่งที่มีความหมาย
มีวิธีตั้งชื่อกรณีทดสอบแบบกำหนดเองของฉันเองเมื่อใช้การทดสอบแบบกำหนดพารามิเตอร์ใน JUnit4 หรือไม่
ฉันต้องการเปลี่ยนค่าเริ่มต้น[Test class].runTest[n]
- เป็นสิ่งที่มีความหมาย
คำตอบ:
คุณลักษณะนี้ได้ทำให้มันเป็นJUnit 4.11
หากต้องการใช้เปลี่ยนชื่อของการทดสอบแบบมีพารามิเตอร์คุณพูดว่า:
@Parameters(name="namestring")
namestring
เป็นสตริงซึ่งสามารถมีตัวยึดตำแหน่งพิเศษดังต่อไปนี้:
{index}
- ดัชนีของชุดอาร์กิวเมนต์นี้ ค่าเริ่มต้นคือnamestring
{index}
{0}
- ค่าพารามิเตอร์แรกจากการเรียกใช้การทดสอบนี้{1}
- ค่าพารามิเตอร์ที่สองชื่อสุดท้ายของการทดสอบจะเป็นชื่อของวิธีทดสอบตามด้วยnamestring
เครื่องหมายวงเล็บตามที่แสดงด้านล่าง
ตัวอย่างเช่น (ดัดแปลงจากการทดสอบหน่วยสำหรับParameterized
คำอธิบายประกอบ):
@RunWith(Parameterized.class)
static public class FibonacciTest {
@Parameters( name = "{index}: fib({0})={1}" )
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
{ 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
}
private final int fInput;
private final int fExpected;
public FibonacciTest(int input, int expected) {
fInput= input;
fExpected= expected;
}
@Test
public void testFib() {
assertEquals(fExpected, fib(fInput));
}
private int fib(int x) {
// TODO: actually calculate Fibonacci numbers
return 0;
}
}
จะให้ชื่อเหมือนและtestFib[1: fib(1)=1]
testFib[4: fib(4)=3]
( testFib
ส่วนหนึ่งของชื่อคือชื่อวิธีการของ@Test
)
{0}
และ{1}
อาร์เรย์? JUnit ควรโทรไม่Arrays.toString({0})
{0}.toString()
ตัวอย่างเช่นdata()
วิธีการของฉันกลับArrays.asList(new Object[][] {{ new int[] { 1, 3, 2 }, new int[] { 1, 2, 3 } }});
มา
ดูที่ JUnit 4.5 นักวิ่งของมันไม่สนับสนุนอย่างชัดเจนเนื่องจากตรรกะนั้นฝังอยู่ในคลาสส่วนตัวภายในคลาส Parameterized คุณไม่สามารถใช้นักวิ่ง JUnit Parameterized และสร้างของคุณเองแทนซึ่งจะเข้าใจแนวคิดของชื่อ (ซึ่งนำไปสู่คำถามที่ว่าคุณอาจตั้งชื่อ ... ) ได้อย่างไร
จากมุมมอง JUnit มันจะดีถ้าแทนที่จะ (หรือเพิ่มเติม) เพียงแค่ผ่านการเพิ่มพวกเขาจะผ่านอาร์กิวเมนต์ที่คั่นด้วยเครื่องหมายจุลภาค TestNG ทำสิ่งนี้ หากคุณลักษณะสำคัญสำหรับคุณคุณสามารถแสดงความคิดเห็นในรายชื่อผู้รับจดหมายของ yahoo ที่อ้างอิงได้ที่ www.junit.org
ฉันเพิ่งเจอปัญหาเดียวกันเมื่อใช้ JUnit 4.3.1 ฉันใช้คลาสใหม่ซึ่งขยายพารามิเตอร์เรียกว่า LabelledParameterized มันได้รับการทดสอบโดยใช้ JUnit 4.3.1, 4.4 และ 4.5 มันสร้างอินสแตนซ์รายละเอียดอีกครั้งโดยใช้การแทนค่าสตริงของอาร์กิวเมนต์แรกของแต่ละพารามิเตอร์อาร์เรย์จากวิธีการ @Parameters คุณสามารถดูรหัสสำหรับสิ่งนี้ได้ที่:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../LabelledParameterized.java?r=3789
และตัวอย่างของการใช้งานที่:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../ServerBuilderTest.java?r=3789
รูปแบบคำอธิบายการทดสอบเป็นอย่างดีใน Eclipse ซึ่งเป็นสิ่งที่ฉันต้องการเนื่องจากทำให้การทดสอบล้มเหลวง่ายต่อการค้นหามาก! ฉันอาจจะปรับแต่งเพิ่มเติมและจัดทำเอกสารชั้นเรียนในอีกไม่กี่วัน / สัปดาห์ วาง '?' ส่วนหนึ่งของ URL หากคุณต้องการขอบเลือดออก :-)
หากต้องการใช้งานสิ่งที่คุณต้องทำคือคัดลอกคลาสนั้น (GPL v3) และเปลี่ยน @RunWith (Parameterized.class) เป็น @RunWith (LabelledParameterized.class) โดยสมมติว่าองค์ประกอบแรกของรายการพารามิเตอร์ของคุณเป็นฉลากที่สมเหตุสมผล
ฉันไม่ทราบว่าจะมีการเผยแพร่ JUnit ในภายหลังหรือไม่แม้ว่าจะเป็นเช่นนั้นฉันก็ไม่สามารถอัปเดต JUnit ได้เนื่องจากผู้พัฒนาร่วมของฉันทั้งหมดจะต้องอัปเดตด้วยและเรามีลำดับความสำคัญสูงกว่าการใช้เครื่องมือใหม่ ดังนั้นการทำงานในคลาสจะสามารถคอมไพล์ได้โดย JUnit หลายเวอร์ชัน
หมายเหตุ:มีการสะท้อน jiggery-pokery บางส่วนเพื่อให้ทำงานข้าม JUnit เวอร์ชันต่าง ๆ ดังที่แสดงไว้ด้านบน รุ่นเฉพาะสำหรับ JUnit 4.3.1 สามารถพบได้ที่นี่และสำหรับ JUnit 4.4 และ 4.5 ที่นี่
execute[0], execute[1] ... execute[n]
อยู่ในรายงานการทดสอบที่สร้างขึ้น
ด้วยการParameterized
เป็นนางแบบที่ผมเขียนเองทดสอบที่กำหนดเองวิ่ง / ชุดของฉัน - เพียงใช้เวลาประมาณครึ่งชั่วโมง มันแตกต่างกันเล็กน้อยจาก darrenp ในการที่จะช่วยให้คุณสามารถระบุชื่อชัดเจนมากกว่าอาศัยแห่งแรกของพารามิเตอร์LabelledParameterized
toString()
มันไม่ได้ใช้อาร์เรย์เพราะฉันเกลียดอาร์เรย์ :)
public class PolySuite extends Suite {
// //////////////////////////////
// Public helper interfaces
/**
* Annotation for a method which returns a {@link Configuration}
* to be injected into the test class constructor
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public static @interface Config {
}
public static interface Configuration {
int size();
Object getTestValue(int index);
String getTestName(int index);
}
// //////////////////////////////
// Fields
private final List<Runner> runners;
// //////////////////////////////
// Constructor
/**
* Only called reflectively. Do not use programmatically.
* @param c the test class
* @throws Throwable if something bad happens
*/
public PolySuite(Class<?> c) throws Throwable {
super(c, Collections.<Runner>emptyList());
TestClass testClass = getTestClass();
Class<?> jTestClass = testClass.getJavaClass();
Configuration configuration = getConfiguration(testClass);
List<Runner> runners = new ArrayList<Runner>();
for (int i = 0, size = configuration.size(); i < size; i++) {
SingleRunner runner = new SingleRunner(jTestClass, configuration.getTestValue(i), configuration.getTestName(i));
runners.add(runner);
}
this.runners = runners;
}
// //////////////////////////////
// Overrides
@Override
protected List<Runner> getChildren() {
return runners;
}
// //////////////////////////////
// Private
private Configuration getConfiguration(TestClass testClass) throws Throwable {
return (Configuration) getConfigMethod(testClass).invokeExplosively(null);
}
private FrameworkMethod getConfigMethod(TestClass testClass) {
List<FrameworkMethod> methods = testClass.getAnnotatedMethods(Config.class);
if (methods.isEmpty()) {
throw new IllegalStateException("@" + Config.class.getSimpleName() + " method not found");
}
if (methods.size() > 1) {
throw new IllegalStateException("Too many @" + Config.class.getSimpleName() + " methods");
}
FrameworkMethod method = methods.get(0);
int modifiers = method.getMethod().getModifiers();
if (!(Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new IllegalStateException("@" + Config.class.getSimpleName() + " method \"" + method.getName() + "\" must be public static");
}
return method;
}
// //////////////////////////////
// Helper classes
private static class SingleRunner extends BlockJUnit4ClassRunner {
private final Object testVal;
private final String testName;
SingleRunner(Class<?> testClass, Object testVal, String testName) throws InitializationError {
super(testClass);
this.testVal = testVal;
this.testName = testName;
}
@Override
protected Object createTest() throws Exception {
return getTestClass().getOnlyConstructor().newInstance(testVal);
}
@Override
protected String getName() {
return testName;
}
@Override
protected String testName(FrameworkMethod method) {
return testName + ": " + method.getName();
}
@Override
protected void validateConstructor(List<Throwable> errors) {
validateOnlyOneConstructor(errors);
}
@Override
protected Statement classBlock(RunNotifier notifier) {
return childrenInvoker(notifier);
}
}
}
และตัวอย่าง:
@RunWith(PolySuite.class)
public class PolySuiteExample {
// //////////////////////////////
// Fixture
@Config
public static Configuration getConfig() {
return new Configuration() {
@Override
public int size() {
return 10;
}
@Override
public Integer getTestValue(int index) {
return index * 2;
}
@Override
public String getTestName(int index) {
return "test" + index;
}
};
}
// //////////////////////////////
// Fields
private final int testVal;
// //////////////////////////////
// Constructor
public PolySuiteExample(int testVal) {
this.testVal = testVal;
}
// //////////////////////////////
// Test
@Ignore
@Test
public void odd() {
assertFalse(testVal % 2 == 0);
}
@Test
public void even() {
assertTrue(testVal % 2 == 0);
}
}
จาก junit4.8.2 คุณสามารถสร้างคลาส MyParameterized ของคุณเองโดยเพียงแค่คัดลอกคลาส Parameterized เปลี่ยนเมธอด getName () และ testName () ใน TestClassRunnerForParameters
คุณอาจต้องการลอง JUnitParams: http://code.google.com/p/junitparams/
คุณสามารถสร้างวิธีการเช่น
@Test
public void name() {
Assert.assertEquals("", inboundFileName);
}
ในขณะที่ฉันจะไม่ใช้มันตลอดเวลามันจะมีประโยชน์ที่จะคิดออกว่าหมายเลขทดสอบ 143 คืออะไร
ฉันใช้การนำเข้าแบบคงที่สำหรับ Assert และเพื่อน ๆ ดังนั้นจึงเป็นเรื่องง่ายสำหรับฉันที่จะกำหนดคำยืนยันใหม่:
private <T> void assertThat(final T actual, final Matcher<T> expected) {
Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected);
}
ตัวอย่างเช่นคุณสามารถเพิ่มฟิลด์ "ชื่อ" ลงในคลาสทดสอบของคุณเริ่มต้นในตัวสร้างและแสดงว่าเมื่อการทดสอบล้มเหลว เพียงผ่านมันเป็นองค์ประกอบแรกของอาร์เรย์พารามิเตอร์ของคุณสำหรับการทดสอบแต่ละครั้ง นอกจากนี้ยังช่วยติดป้ายกำกับข้อมูล:
public ExampleTest(final String testLabel, final int one, final int two) {
this.testLabel = testLabel;
// ...
}
@Parameters
public static Collection<Object[]> data() {
return asList(new Object[][]{
{"first test", 3, 4},
{"second test", 5, 6}
});
}
ไม่มีสิ่งใดที่ทำงานได้สำหรับฉันดังนั้นฉันจึงได้แหล่งข้อมูลสำหรับพารามิเตอร์และแก้ไขมันสร้างนักวิ่งทดสอบใหม่ ฉันไม่จำเป็นต้องเปลี่ยนแปลงอะไรมากมาย แต่มันก็ใช้ได้ !!!
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Assert;
import org.junit.internal.runners.ClassRoadie;
import org.junit.internal.runners.CompositeRunner;
import org.junit.internal.runners.InitializationError;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.internal.runners.MethodValidator;
import org.junit.internal.runners.TestClass;
import org.junit.runner.notification.RunNotifier;
public class LabelledParameterized extends CompositeRunner {
static class TestClassRunnerForParameters extends JUnit4ClassRunner {
private final Object[] fParameters;
private final String fParameterFirstValue;
private final Constructor<?> fConstructor;
TestClassRunnerForParameters(TestClass testClass, Object[] parameters, int i) throws InitializationError {
super(testClass.getJavaClass()); // todo
fParameters = parameters;
if (parameters != null) {
fParameterFirstValue = Arrays.asList(parameters).toString();
} else {
fParameterFirstValue = String.valueOf(i);
}
fConstructor = getOnlyConstructor();
}
@Override
protected Object createTest() throws Exception {
return fConstructor.newInstance(fParameters);
}
@Override
protected String getName() {
return String.format("%s", fParameterFirstValue);
}
@Override
protected String testName(final Method method) {
return String.format("%s%s", method.getName(), fParameterFirstValue);
}
private Constructor<?> getOnlyConstructor() {
Constructor<?>[] constructors = getTestClass().getJavaClass().getConstructors();
Assert.assertEquals(1, constructors.length);
return constructors[0];
}
@Override
protected void validate() throws InitializationError {
// do nothing: validated before.
}
@Override
public void run(RunNotifier notifier) {
runMethods(notifier);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public static @interface Parameters {
}
private final TestClass fTestClass;
public LabelledParameterized(Class<?> klass) throws Exception {
super(klass.getName());
fTestClass = new TestClass(klass);
MethodValidator methodValidator = new MethodValidator(fTestClass);
methodValidator.validateStaticMethods();
methodValidator.validateInstanceMethods();
methodValidator.assertValid();
int i = 0;
for (final Object each : getParametersList()) {
if (each instanceof Object[])
add(new TestClassRunnerForParameters(fTestClass, (Object[]) each, i++));
else
throw new Exception(String.format("%s.%s() must return a Collection of arrays.", fTestClass.getName(), getParametersMethod().getName()));
}
}
@Override
public void run(final RunNotifier notifier) {
new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable() {
public void run() {
runChildren(notifier);
}
}).runProtected();
}
private Collection<?> getParametersList() throws IllegalAccessException, InvocationTargetException, Exception {
return (Collection<?>) getParametersMethod().invoke(null);
}
private Method getParametersMethod() throws Exception {
List<Method> methods = fTestClass.getAnnotatedMethods(Parameters.class);
for (Method each : methods) {
int modifiers = each.getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))
return each;
}
throw new Exception("No public static parameters method on class " + getName());
}
public static Collection<Object[]> eachOne(Object... params) {
List<Object[]> results = new ArrayList<Object[]>();
for (Object param : params)
results.add(new Object[] { param });
return results;
}
}
วิธีแก้ปัญหาคือการดักจับและซ้อน Throwables ทั้งหมดลงใน Throwable ใหม่ด้วยข้อความที่กำหนดเองที่มีข้อมูลทั้งหมดเกี่ยวกับพารามิเตอร์ ข้อความจะปรากฏในการติดตามสแต็ก ทำงานได้ทุกครั้งที่การทดสอบล้มเหลวสำหรับการยืนยันข้อผิดพลาดและข้อยกเว้นเนื่องจากเป็นคลาสย่อยทั้งหมดของ Throwable
รหัสของฉันมีลักษณะเช่นนี้:
@RunWith(Parameterized.class)
public class ParameterizedTest {
int parameter;
public ParameterizedTest(int parameter) {
super();
this.parameter = parameter;
}
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] { {1}, {2} });
}
@Test
public void test() throws Throwable {
try {
assertTrue(parameter%2==0);
}
catch(Throwable thrown) {
throw new Throwable("parameter="+parameter, thrown);
}
}
}
การติดตามสแต็กของการทดสอบที่ล้มเหลวคือ:
java.lang.Throwable: parameter=1
at sample.ParameterizedTest.test(ParameterizedTest.java:34)
Caused by: java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:92)
at org.junit.Assert.assertTrue(Assert.java:43)
at org.junit.Assert.assertTrue(Assert.java:54)
at sample.ParameterizedTest.test(ParameterizedTest.java:31)
... 31 more
ตรวจสอบ JUnitParams ตามที่กล่าวถึง dsaff, ทำงานโดยใช้ ant เพื่อสร้างคำอธิบายวิธีการทดสอบแบบพารามิเตอร์ในรายงาน html
ปัญหานี้เกิดขึ้นหลังจากลอง LabelledParameterized และพบว่ามันทำงานได้กับ eclipse แต่จะไม่ทำงานกับ ant เท่าที่รายงาน html เกี่ยวข้อง
ไชโย
เนื่องจากพารามิเตอร์ที่เข้าถึง (เช่นมีการ"{0}"
ส่งคืนการtoString()
แทนเสมอการแก้ปัญหาหนึ่งข้อคือการใช้งานแบบไม่ระบุชื่อและการลบล้างtoString()
ในแต่ละกรณีตัวอย่างเช่น:
public static Iterable<? extends Object> data() {
return Arrays.asList(
new MyObject(myParams...) {public String toString(){return "my custom test name";}},
new MyObject(myParams...) {public String toString(){return "my other custom test name";}},
//etc...
);
}