Есть ли более аккуратный способ тестирования вызовов для проверенных методов для каждого элемента в списке - PullRequest
1 голос
/ 26 июня 2009

Это пример паттерна, с которым я часто сталкивался в последнее время. У меня есть метод для тестирования, который принимает список и может вызывать некоторые другие методы для каждого элемента в списке. Чтобы проверить это, я определяю Итератор с ожидаемыми параметрами вызова и цикл в ожиданиях JMock, чтобы проверить, сделан ли вызов для каждого элемента итератора (см. Простой пример ниже).

Я посмотрел на сопоставители Hamcrest, но не нашел что-то, что проверяет это (или неправильно понял, как работают доступные сопоставители). У кого-нибудь есть более элегантный подход?

package com.hsbc.maven.versionupdater;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.maven.plugin.testing.AbstractMojoTestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.Sequence;
import org.jmock.internal.NamedSequence;

public class FooTest extends AbstractMojoTestCase {

    public interface Bar {
        void doIt(String arg);
    }

    public class Foo {

        private Bar bar;

        public void executeEven(final List<String> allParameters) {
            for (int i = 0; i < allParameters.size(); i++) {
                if (i % 2 == 0) {
                    bar.doIt(allParameters.get(i));
                }
            }
        }

        public Bar getBar() {
            return bar;
        }

        public void setBar(final Bar bar) {
            this.bar = bar;
        }

    }

    public void testExecuteEven() {
        Mockery mockery = new Mockery();

        final Bar bar = mockery.mock(Bar.class);
        final Sequence sequence = new NamedSequence("sequence");

        final List<String> allParameters = new ArrayList<String>();
        final List<String> expectedParameters = new ArrayList<String>();

        for (int i = 0; i < 3; i++) {
            allParameters.add("param" + i);
            if (i % 2 == 0) {
            expectedParameters.add("param" + i);
            }
        }

        final Iterator<String> iter = expectedParameters.iterator();

        mockery.checking(new Expectations() {
            {
                while (iter.hasNext()) {
                    one(bar).doIt(iter.next());
                    inSequence(sequence);
                }
            }
        });

        Foo subject = new Foo();
        subject.setBar(bar);
        subject.executeEven(allParameters);
        mockery.assertIsSatisfied();
    }
}

Ответы [ 4 ]

1 голос
/ 17 июля 2009

Я думаю, что ваша текущая тестовая реализация близка к идеальной. Любое дальнейшее уплотнение рискует либо изменить семантику теста, либо скрыть цель теста для читателя (или обоих).

Однако, если вы ищете способ ожидать определенного количества вызовов метода, вы можете использовать exactly(n).of():

mockery.checking(new Expectations() {{
  exactly(expectedParameters.length()).of(bar).doIt(with(anyOf(expectedParameters)));
}});

(я пропустил проверку равномерности, но вы поняли). Это похоже на пример jmockit в другом ответе. Помните, что это не то же самое, что ваш первоначальный тест. В частности это не проверяет:

  1. Порядок звонков на doIt
  2. что каждый элемент списка параметров передается ровно один раз

Например, этот тест прошел бы, если бы ваш метод повторялся по списку в обратном порядке или если он просто вызывал метод doIt n раз, но каждый раз передавал первый элемент списка. Если вы хотите, чтобы каждый элемент в списке был пропущен, вам придется многократно повторять его, устанавливая индивидуальное ожидание для каждого. Если вам не важен порядок вызовов, вы можете опустить использование Последовательности (в этом случае вы можете захотеть изменить свой оригинальный метод, чтобы принимать Коллекцию вместо Списка).

1 голос
/ 27 июня 2009

Возможно следующее (использование JMockit вместо jMock)?


import java.util.*;

import org.junit.*;
import org.junit.runner.*;

import org.hamcrest.*;
import static org.hamcrest.core.AnyOf.*;
import static org.hamcrest.core.Is.*;
import org.hamcrest.core.*;

import mockit.*;
import mockit.integration.junit4.*;

@RunWith(JMockit.class)
public class FooTest
{
   public interface Bar { void doIt(String arg); }

   public class Foo
   {
      private Bar bar;

      public void executeEven(final List<String> allParameters)
      {
         for (int i = 0; i < allParameters.size(); i++) {
            if (i % 2 == 0) {
               bar.doIt(allParameters.get(i));
            }
         }
      }

      public void setBar(final Bar bar) { this.bar = bar; }
   }

   @Test
   public void testExecuteEven(final Bar bar)
   {
      final List<String> allParameters = new ArrayList<String>();
      final List<Matcher<? extends String>> expectedParameters =
         new ArrayList<Matcher<? extends String>>();

      for (int i = 0; i < 3; i++) {
         allParameters.add("param" + i);

         if (i % 2 == 0) {
            expectedParameters.add(new IsEqual<String>("param" + i));
         }
      }

      new Expectations()
      {
         {
            bar.doIt(with(anyOf(expectedParameters))); repeats(expectedParameters.size());
         }
      };

      Foo subject = new Foo();
      subject.setBar(bar);
      subject.executeEven(allParameters);
   }

   @Test // a shorter version of the same test
   public void testExecuteEven2(final Bar bar)
   {
      final List<String> allParameters = Arrays.asList("param0", "param1", "param2");

      new Expectations()
      {
         {
            bar.doIt(with(anyOf(is("param0"), is("param2")))); repeats(2);
         }
      };

      Foo subject = new Foo();
      subject.setBar(bar);
      subject.executeEven(allParameters);
   }
}
0 голосов
/ 21 января 2010

Стоит помнить, что вам не нужно создавать свои ожидания сразу. Вы можете сделать свой цикл вне блока checking(new Expectations(){{}}) и манипулировать списком ожиданий, прежде чем, наконец, передать его в издевательство. Это может помочь с ясностью в сложных установках ожидания (и комментарий тоже!):

@Test
public void testExecuteEven() {

  Mockery mockery = new Mockery();
  Sequence evens = mockery.sequence("evens");
  final Bar bar = mockery.mock(Bar.class);

  List<Expectations> expectations = new ArrayList<Expectations>();

  final List<String> allParameters = new ArrayList<String>();
  final List<String> expectedParameters = new ArrayList<String>();


  // generate some parameters 
  for (int i = 0; i < 3; i++) {
      allParameters.add("param" + i);
      if (i % 2 == 0) {
      expectedParameters.add("param" + i);
      }
  }

  // define expectations for the expected parameters
  for (String param : expectedParameters) {
    expectations.add(new Expectations() {{ oneOf(bar).doIt(param); inSequence(evens); }});
  }

  // define any special expectations here
  expectations.add(new Expectations() {{ oneOf(bar).doSomethingElse() /* whatever */ }});

  // load the expectations into the mockery
  for (Expectations expectation : expectations) {
    mockery.checking(expectation);
  }

  Foo subject = new Foo();
  subject.setBar(bar);
  subject.executeEven(allParameters);

}

Кроме того, я заметил, что вы не используете операторы Java 5 foreach. Если вы не застряли с использованием Java 4, это также может помочь с ясностью.

0 голосов
/ 05 сентября 2009

Вы можете упростить этот тест. Вы знаете, что вы хотите, поэтому вы можете быть более конкретным о коде:

public void testExecuteEven() {
  final List<String> values = Arrays.asList("param0", "param1", "param2", "param3");
  Sequence evens = mockery.sequence("evens");

  mockery.checking(new Expectations() {{
    oneOf(bar).doIt(values.get(0)); inSequence(evens);
    oneOf(bar).doIt(values.get(2)); inSequence(evens);
  }});

  subject.executeEven(values);
}

Если вы используете JUnit 4, не забывайте, что аннотация @RunWith (JMock.class) для класса исключает необходимость вызова assertIsSatisfied ().

...