10

I've got an abstract class:

@Component
public abstract class BaseReport {

  public void export() {
   ...
}

And a bunch of classes that extend it, and override the export() method (or not).

@Component
public final class Report1 extends BaseReport

@Component
public final class Report2 extends BaseReport

Most of my tests autowire concrete classes that extend BaseReport, with no problems:

public class Report1Test extends BaseTest {

    @Autowired
    Report1 _report;

public class Report2Test extends BaseTest {

    @Autowired
    Report2 _report;

This works fine for autowiring of all classes that extend BaseReport. But I also need to autowire the abstract class itself, BaseReport, to test the export() method.

public class BaseReportTest extends BaseTest {

  @Autowired
  BaseReport _report;

When I try to run it I get the infamous:

No unique bean of type BaseReport is defined: expected single matching bean but found 2 [Report1, Report2].

I've tried using @Qualifier but the problem with @Qualifier is that (as I understand it) you use it to tell Spring which class -- that implements an Interface or extends an Abstract class - you wish to use. But that's not my case. I want to use the abstract class itself.

I also tried using @Resource, like this:

public class BaseReportTest extends BaseTest {

  @Resource(name = "baseReport")
  BaseReport _report;

Spring tells me there is no bean with this name. :(

How can I do this?

Cheers.

Robert Bowen
  • 487
  • 2
  • 13
  • 24

1 Answers1

17

Abstract classes can't be instantiated, you need to use a concrete implementation. Same as in regular java, if you try to instantiate an abstract class, it tells you to implement the abstract methods within. When you do, an anonymous class is created. It's not an instatiation of the abstract class, but a new subclass of that abstract class.

Spring will look for classes which extend your base class, being Report1 and Report2, Spring sees it has multiple classes which match the requirements and doesn't know which one to choose. thus you get the error that there are multiple matching beans.

You can fix this by making an "adapter" basicly create a concrete class which extends your base-class, implements the abstract methods, but doesn't do anything them. Then you can autowire that implementation and test against it. However your abstract class should allready be tested due to the fact you are testing report 1 and 2. If errors still occur with your base class, it means logic you don't use is causing bugs, which is a bad practice anyway. also with a test coveage tool you could spot unused code that way.

G-Man
  • 1,321
  • 8
  • 15
  • You are absolutely right. After reading your reply I took another look and sure enough, I had slightly misunderstood the problem. All concrete classes extend BaseReport and implement the export() method, *but* the export() method does not come from BaseReport, but rather the IReport interface, which BaseReport and all concrete Reports implement. So there is no need to test BaseReport's export() method, because it doesn't have one! Since I've already tested each concrete class' export() method, my work is done. Many thanks for the help. – Robert Bowen Apr 12 '13 at 14:06