0

我有一个抽象类 AbstractService 和几个扩展这个抽象类的类:

愚蠢的类图

然后我有一个 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,测试通过...

有没有一种方法可以做到这一点,或者我应该使用循环自己实现它?

4

3 回答 3

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;
        }
    }
}
于 2014-01-22T20:23:36.063 回答
0

A big thanks to Max Fichtelmann to point me in the right direction!

I ultimately started using Fest assert, but the same result can probably be achieved with Hamcrest.

I created a custom matcher, assertEvery, comparing list sizes:

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;
  }
}

Now, I can use it with import static myassertions.impl.ServiceAssert.assertThatMy;.

于 2014-01-24T15:29:28.773 回答
0

AService 的子类型肯定是 AService 的实例,并且返回 true 的条件是正确的。

甚至没有使用循环实现它的好方法,而且很大程度上取决于执行上下文,从不同类加载器加载的“相同”类是“不同的”。

我会退后一步,考虑一下您希望验证的 AService 的属性,该属性是否应该在 AbstractService 中定义?并且,最后使用属性来验证结果。

于 2014-01-22T15:14:18.987 回答