我是JUnit的Java测试新手。我必须使用Java,我想使用单元测试。
我的问题是:我有一个带有一些抽象方法的抽象类。但是有些方法并不是抽象的。如何使用JUnit测试此类?示例代码(非常简单):
abstract class Car {
public Car(int speed, int fuel) {
this.speed = speed;
this.fuel = fuel;
}
private int speed;
private int fuel;
abstract void drive();
public int getSpeed() {
return this.speed;
}
public int getFuel() {
return this.fuel;
}
}
我想测试getSpeed()
和getFuel()
函数。
此问题的类似问题是here,但它没有使用JUnit。
在JUnit FAQ部分,我找到了this link,但我不明白作者想用这个例子说些什么。这行代码意味着什么?
public abstract Source getSource() ;
答案 0 :(得分:102)
如果你没有具体的类实现,并且方法不是static
那么测试它们的重点是什么?如果你有一个具体的类,那么你将测试这些方法作为具体类的公共API的一部分。
我知道你在想什么“我不想一遍又一遍地测试这些方法,这就是我创建抽象类的原因”,但我的反驳论点是单元测试的重点是让开发人员能够进行更改,运行测试并分析结果。这些变化的一部分可能包括覆盖抽象类的方法,protected
和public
,这可能导致基本的行为变化。根据这些更改的性质,它可能会影响您的应用程序以意外的方式运行,可能是负面的方式。如果你有一个好的单元测试套件,这些类型引起的问题在开发时应该是明显的。
答案 1 :(得分:30)
创建一个继承抽象类的具体类,然后测试具体类从抽象类继承的函数。
答案 2 :(得分:12)
使用您发布的示例类,测试getFuel()
和getSpeed()
似乎没有多大意义,因为它们只能返回0(没有设置者)。
但是,假设这仅仅是用于说明目的的简化示例,并且您有合理的理由在抽象基类中测试方法(其他人已经指出了其含义),您可以设置测试代码以便它创建一个基类的匿名子类,它只为抽象方法提供虚拟(无操作)实现。
例如,在您的TestCase
中,您可以执行此操作:
c = new Car() {
void drive() { };
};
然后测试其余的方法,例如:
public class CarTest extends TestCase
{
private Car c;
public void setUp()
{
c = new Car() {
void drive() { };
};
}
public void testGetFuel()
{
assertEquals(c.getFuel(), 0);
}
[...]
}
(这个例子基于JUnit3语法。对于JUnit4,代码会略有不同,但想法是一样的。)
答案 3 :(得分:8)
如果你还需要一个解决方案(例如,因为你有太多的抽象类实现,并且测试总是会重复相同的过程),那么你可以用一个抽象的工厂方法创建一个抽象的测试类,该方法将由该测试类的实现。这个例子可以和我一起使用TestNG:
Car
的抽象测试类:
abstract class CarTest {
// the factory method
abstract Car createCar(int speed, int fuel);
// all test methods need to make use of the factory method to create the instance of a car
@Test
public void testGetSpeed() {
Car car = createCar(33, 44);
assertEquals(car.getSpeed(), 33);
...
Car
class ElectricCar extends Car {
private final int batteryCapacity;
public ElectricCar(int speed, int fuel, int batteryCapacity) {
super(speed, fuel);
this.batteryCapacity = batteryCapacity;
}
...
班级ElectricCarTest
的单元测试班ElectricCar
:
class ElectricCarTest extends CarTest {
// implementation of the abstract factory method
Car createCar(int speed, int fuel) {
return new ElectricCar(speed, fuel, 0);
}
// here you cann add specific test methods
...
答案 4 :(得分:5)
你可以做这样的事情
public abstract MyAbstractClass {
@Autowire
private MyMock myMock;
protected String sayHello() {
return myMock.getHello() + ", " + getName();
}
public abstract String getName();
}
// this is your JUnit test
public class MyAbstractClassTest extends MyAbstractClass {
@Mock
private MyMock myMock;
@InjectMocks
private MyAbstractClass thiz = this;
private String myName = null;
@Override
public String getName() {
return myName;
}
@Test
public void testSayHello() {
myName = "Johnny"
when(myMock.getHello()).thenReturn("Hello");
String result = sayHello();
assertEquals("Hello, Johnny", result);
}
}
答案 5 :(得分:3)
我将创建一个继承自抽象类的jUnit内部类。这可以实例化,并且可以访问抽象类中定义的所有方法。
public class AbstractClassTest {
public void testMethod() {
...
}
}
class ConcreteClass extends AbstractClass {
}
答案 6 :(得分:2)
您可以实例化一个匿名类,然后测试该类。
public class ClassUnderTest_Test {
private ClassUnderTest classUnderTest;
private MyDependencyService myDependencyService;
@Before
public void setUp() throws Exception {
this.myDependencyService = new MyDependencyService();
this.classUnderTest = getInstance();
}
private ClassUnderTest getInstance() {
return new ClassUnderTest() {
private ClassUnderTest init(
MyDependencyService myDependencyService
) {
this.myDependencyService = myDependencyService;
return this;
}
@Override
protected void myMethodToTest() {
return super.myMethodToTest();
}
}.init(myDependencyService);
}
}
请注意,摘要类protected
的属性myDependencyService
的可见性必须为ClassUnderTest
。
你也可以将这种方法与Mockito巧妙地结合起来。请参阅here。
答案 7 :(得分:1)
在每个abstractUnitTest.java
中,我的测试方法非常简单。我只是在abstractUnitTest.java中创建了一个扩展抽象类的类。然后以这种方式进行测试。
答案 8 :(得分:0)
您无法测试整个抽象类。在这种情况下,您有抽象方法,这意味着它们应该由扩展给定抽象类的类实现。
在那个类程序员必须编写专门用于他的逻辑的源代码。
换句话说,没有测试抽象类的感觉,因为你无法检查它的最终行为。
如果您在某些抽象类中具有与抽象方法无关的主要功能,则只需创建另一个抽象方法将引发异常的类。
答案 9 :(得分:0)
作为一个选项,您可以创建抽象测试类,覆盖抽象类中的逻辑,并为每个子类测试扩展它。因此,通过这种方式,您可以确保为每个孩子单独测试此逻辑。