Câu trả lời:
Tính năng này đã biến nó thành JUnit 4.11 .
Để sử dụng thay đổi tên của các bài kiểm tra tham số, bạn nói:
@Parameters(name="namestring")
namestring
là một chuỗi, có thể có các trình giữ chỗ đặc biệt sau:
{index}
- chỉ số của bộ đối số này. Mặc định namestring
là {index}
.{0}
- giá trị tham số đầu tiên từ lệnh gọi thử nghiệm này.{1}
- giá trị tham số thứ haiTên cuối cùng của bài kiểm tra sẽ là tên của phương thức kiểm tra, theo sau là namestring
trong ngoặc, như được hiển thị bên dưới.
Ví dụ: (được điều chỉnh từ bài kiểm tra đơn vị cho Parameterized
chú thích):
@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;
}
}
sẽ đặt tên như testFib[1: fib(1)=1]
và testFib[4: fib(4)=3]
. (Phần testFib
của tên là tên phương thức của @Test
).
{0}
và {1}
là mảng thì sao? JUnit nên lý tưởng gọi Arrays.toString({0})
, không {0}.toString()
. Ví dụ, data()
phương thức của tôi trả về Arrays.asList(new Object[][] {{ new int[] { 1, 3, 2 }, new int[] { 1, 2, 3 } }});
.
Nhìn vào JUnit 4.5, người chạy của nó rõ ràng không hỗ trợ điều đó, vì logic đó được chôn trong một lớp riêng bên trong lớp Parameterized. Bạn không thể sử dụng trình chạy Thông số JUnit và tự tạo thay thế để hiểu khái niệm về tên (điều này dẫn đến câu hỏi về cách bạn có thể đặt tên ...).
Từ góc độ JUnit, sẽ tốt hơn nếu thay vì (hoặc ngoài) chỉ vượt qua một gia số, họ sẽ vượt qua các đối số được phân tách bằng dấu phẩy. TestNG làm điều này. Nếu tính năng này quan trọng đối với bạn, bạn có thể nhận xét về danh sách gửi thư yahoo được tham chiếu tại www.junit.org.
Gần đây tôi đã gặp một vấn đề tương tự khi sử dụng JUnit 4.3.1. Tôi đã triển khai một lớp mới mở rộng Parameterized gọi là LabellingParameterized. Nó đã được thử nghiệm bằng JUnit 4.3.1, 4.4 và 4.5. Nó xây dựng lại cá thể Mô tả bằng cách sử dụng biểu diễn Chuỗi của đối số đầu tiên của từng mảng tham số từ phương thức @Parameter. Bạn có thể xem mã cho điều này tại:
và một ví dụ về việc sử dụng nó tại:
http://code.google.com.vn/p/migen/source/browse/trunk/java/src/.../ServerBuilderTest.java?r=3789
Các định dạng mô tả thử nghiệm độc đáo trong Eclipse, đó là điều tôi muốn vì điều này làm cho các thử nghiệm thất bại dễ dàng tìm thấy hơn nhiều! Tôi có thể sẽ tiếp tục tinh chỉnh và ghi lại các lớp học trong vài ngày / tuần tới. Thả '?' một phần của các URL nếu bạn muốn cạnh chảy máu. :-)
Để sử dụng nó, tất cả những gì bạn phải làm là sao chép lớp đó (GPL v3) và thay đổi @RunWith (Parameterized. Class) thành @RunWith (LabellingParameterized. Class) giả sử phần tử đầu tiên của danh sách tham số của bạn là nhãn hợp lý.
Tôi không biết liệu có bất kỳ bản phát hành nào sau này của JUnit giải quyết vấn đề này không, nhưng ngay cả khi chúng đã xảy ra, tôi không thể cập nhật JUnit vì tất cả các nhà đồng phát triển của tôi cũng sẽ phải cập nhật và chúng tôi có mức độ ưu tiên cao hơn so với công cụ lại. Do đó, công việc trong lớp có thể được biên dịch bởi nhiều phiên bản của JUnit.
Lưu ý: có một số jiggery-pokery phản chiếu để nó chạy trên các phiên bản JUnit khác nhau như được liệt kê ở trên. Phiên bản dành riêng cho JUnit 4.3.1 có thể được tìm thấy ở đây và, cho JUnit 4.4 và 4.5, tại đây .
execute[0], execute[1] ... execute[n]
trong các báo cáo thử nghiệm được tạo ra.
Với Parameterized
tư cách là một người mẫu, tôi đã viết bộ chạy / bộ kiểm tra tùy chỉnh của riêng mình - chỉ mất khoảng nửa giờ. Nó hơi khác so với darrenp LabelledParameterized
ở chỗ nó cho phép bạn chỉ định một cái tên rõ ràng thay vì dựa vào tham số đầu tiên toString()
.
Nó cũng không sử dụng mảng vì tôi ghét mảng. :)
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);
}
}
}
Và một ví dụ:
@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);
}
}
từ Junit4.8.2, bạn có thể tạo lớp MyParameterized của riêng mình bằng cách sao chép lớp Parameterized. thay đổi các phương thức getName () và testName () trong TestClassRunnerForParameter.
Bạn cũng có thể muốn dùng thử JUnitParams: http://code.google.com.vn/p/junitparams/
Bạn có thể tạo một phương thức như
@Test
public void name() {
Assert.assertEquals("", inboundFileName);
}
Mặc dù tôi sẽ không sử dụng nó mọi lúc, nó sẽ hữu ích để tìm ra chính xác số kiểm tra 143 là gì.
Tôi sử dụng rộng rãi nhập tĩnh cho Assert và bạn bè, vì vậy tôi dễ dàng xác định lại xác nhận:
private <T> void assertThat(final T actual, final Matcher<T> expected) {
Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected);
}
Ví dụ: bạn có thể thêm trường "tên" vào lớp kiểm tra của mình, được khởi tạo trong hàm tạo và hiển thị trường đó khi thử nghiệm thất bại. Chỉ cần vượt qua nó như là các yếu tố đầu tiên của mảng tham số của bạn cho mỗi thử nghiệm. Điều này cũng giúp gắn nhãn dữ liệu:
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}
});
}
Không ai trong số đó làm việc cho tôi, vì vậy tôi đã nhận được nguồn cho Parameterized và sửa đổi nó tạo ra một trình chạy thử nghiệm mới. Tôi đã không phải thay đổi nhiều nhưng CÔNG TRÌNH NÓ !!!
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;
}
}
Một cách giải quyết khác là bắt và lồng tất cả các throwable vào một throwable mới với một thông báo tùy chỉnh chứa tất cả thông tin về các tham số. Thông báo sẽ xuất hiện trong theo dõi ngăn xếp. Điều này hoạt động bất cứ khi nào một bài kiểm tra thất bại cho tất cả các xác nhận, lỗi và ngoại lệ vì chúng đều là các lớp con của throwable.
Mã của tôi trông như thế này:
@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);
}
}
}
Dấu vết ngăn xếp của bài kiểm tra thất bại là:
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
Hãy xem JUnitParams như DSaff đã đề cập, hoạt động bằng cách sử dụng ant để xây dựng các mô tả phương pháp thử nghiệm được tham số hóa trong báo cáo html.
Điều này là sau khi thử LabellingParameterized và thấy rằng mặc dù nó hoạt động với nhật thực nhưng nó không hoạt động với kiến theo như báo cáo html có liên quan.
Chúc mừng
Vì tham số được truy cập (ví dụ: "{0}"
luôn trả về toString()
biểu diễn, một cách giải quyết sẽ là thực hiện ẩn danh và ghi đè toString()
trong mỗi trường hợp. Ví dụ:
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...
);
}