2014-01-22 1 views
0

У меня есть абстрактный класс, AbstractService и несколько классов, которые проходят этот абстрактный класс:Как проверить, содержит ли общий список точный подмножество подтипа в Java?

Silly class diagram

Я тогда ServiceFactory, который возвращает меня общий список с некоторыми службами, в соответствии с параметром Переходит:

public class ServiceFactory { 
    public List<? extends AbstractService> getServices(final MyParameter param) { 
     // Service is an interface implemented by AbstractService 
     List<Service> services = new ArrayList<>(); 

     for (Foo foo : param.getFoos()) { 
      services.add(new AService(foo.getBar())); 
     } 
     // creates the rest of the services 
     return services; 
    } 
} 

в моей UnitTest, я хотел бы проверить, если мой список услуг содержит ровно 3 AService подтипы. То, как я делаю это сейчас:

@Test 
public void serviceFactoryShouldReturnAServiceForEachFoo() { 
    // I'm mocking MyParameter and Foo here 
    Mockito.when(param.getFoos()).thenReturn(Arrays.asList(foo, foo, foo); 

    AService aservice = new AService(foo); 

    List<AService> expectedServices = Arrays.asList(aservice, aservice, aservice); 
    List<? extends AbstractService> actualServices = serviceFactory.getServices(); 

    assertTrue(CollectionUtils.isSubCollection(expectedServices, actualServices)); 
} 

Когда actualServices содержит менее 3 AService, тест не пройден правильно. Единственная проблема с этим решением заключается в том, что если actualServices содержит более 3-х AService, тест проходит ...

Есть ли метод, который делает это, или я должен сам реализовать его с помощью петель?

+0

Я бы использовал Set и, вероятно, проверил следующее: http://stackoverflow.com/questions/3341202/fastest-way-to-compare-two-set-in-java – shippi

+0

Является ли ваш пример кодом правильно? Вы помещаете 3 одинаковых вещи в свой список 'expectedServices'. Вы хотите подсчитать количество разных классов в своем списке, чтобы узнать, равно ли это число? –

+0

@DanGetz не совсем. В моем приложении я добавляю эти службы в список в соответствии с другим параметром, поэтому службы не все одинаковы, и они не всегда возвращают ровно 3. Но в моем модульном тесте я использую макетные параметры для получения ожидаемого результата , – Tarek

ответ

1

Вы можете использовать Hamcrest Matchers.

hamcrest-library содержит образцы для проверки коллекции/итерируемого содержимого.

Я надеюсь, что следующий пример соответствует вашим Szenario свободно

import static org.hamcrest.CoreMatchers.not; 
import static org.hamcrest.MatcherAssert.assertThat; 
import static org.mockito.Mockito.mock; 
import static org.hamcrest.Matchers.containsInAnyOrder; 

import java.util.ArrayList; 
import java.util.Collection; 

import org.apache.commons.lang.builder.EqualsBuilder; 

import org.junit.Test; 

public class ServiceFactoryTest 
{ 
    @Test 
    public void serviceFactoryShouldReturnAServiceForEachFoo() 
    { 
     Foo foo = mock(Foo.class); 
     Service service = new AService(foo); 
     Service[] expected = { service, service, service }; 
     Service[] tooFew = { service, service }; 
     Service[] tooMany = { service, service, service, service }; 

     ServiceFactory factory = new ServiceFactory(); 

     assertThat(factory.createServices(foo, foo, foo), containsInAnyOrder(expected)); 
     assertThat(factory.createServices(foo, foo, foo), not(containsInAnyOrder(tooFew))); 
     assertThat(factory.createServices(foo, foo, foo), not(containsInAnyOrder(tooMany))); 
    } 

    interface Foo 
    {} 

    interface Service 
    {} 

    class AService implements Service 
    { 
     Foo foo; 

     public AService(Foo foo) 
     { 
      this.foo = foo; 
     } 

     @Override 
     public boolean equals(Object that) 
     { 
      return EqualsBuilder.reflectionEquals(this, that); 
     } 
    } 

    class ServiceFactory 
    { 
     Collection<? extends Service> createServices(Foo... foos) 
     { 
      Collection<Service> list = new ArrayList<>(); 

      for (Foo foo : foos) 
      { 
       list.add(new AService(foo)); 
      } 

      return list; 
     } 
    } 
} 
+0

Я пробовал ваш ответ, но тест терпит неудачу, когда в списке есть больше видов услуг (например, AnotherService). Тем не менее, я должен действительно научиться этим помощникам. – Tarek

+1

В этом случае вы, вероятно, должны просто утверждать размер в этом тесте и тестировать правильное создание сервиса в своем собственном тесте - например, createFooServiceForFoo() и createBarServiceForBar(). содержит/containsInAnyOrder Matcher также принимает набор совпадений, поэтому вы можете использовать совпадения экземпляров класса (класса) вместо равноценных проверок. –

0

Это определенно тот случай, когда подтипы AService являются экземплярами AService, и условие, возвращающее true, является правильным.

Существует даже неплохой способ реализовать его с использованием циклов, и многое зависит от контекста выполнения, «те же» классы, загруженные из разных загрузчиков классов, «разные».

Я бы сделал шаг назад и подумал о свойствах AService, которые вы хотите проверить, должно ли это свойство быть определено в AbstractService? И, наконец, используйте свойство для проверки результатов.

+0

благодарит за ваш ответ! Но я не тестирую свой класс AService, а Factory, который возвращает список сервисов. Я собираюсь улучшить свой вопрос. – Tarek

0

Большое спасибо Max Fichtelmann указать меня в правильном направлении!

В конечном итоге я начал использовать Fest assert, но тот же результат, вероятно, может быть достигнут с помощью Hamcrest.

Я создал пользовательский Искателя, assertEvery, сравнивая размеры списка:

public class ServiceAssert extends AbstractAssert<ServiceAssert, List<Service>> { 
    public ServiceAssert(List<Service> actual) { 
    super(actual, ServiceAssert.class); 
    } 

    // it's usually assertThat, but it conflicts with the List<T> assertions 
    public static ServiceAssert assertThatMy(List<Service> actual) { 
    return new ServiceAssert(actual); 
    } 

    public ServiceAssert containsEvery(List<? extends Service> expectedServices) { 
    Integer listSize = 0; 

    isNotNull(); 

    if (expectedServices == null || expectedServices.isEmpty()) { 
     throw new AssertionError("Do not use this method for an empty or null list of services, use doesNotContain instead."); 
    } 

    Class<? extends Service> serviceClass = expectedServices.get(0).getClass(); 

    for (Service f : actual) { 
     if (f.getClass().equals(serviceClass)) { 
     listSize++; 
     } 
    } 

    if (listSize != expectedServices.size()) { 
     throw new AssertionError("expected " + expectedServices.size() + " " + serviceClass.getSimpleName() + " but was " + listSize + "."); 
    } 

    return this; 
    } 
} 

Теперь я могу использовать его с import static myassertions.impl.ServiceAssert.assertThatMy;.