Технологии Android для фиктивного источника данных в модульном тестировании активности - PullRequest
5 голосов
/ 08 сентября 2010

Я новичок в модульном тестировании, и я изучаю, как использовать фреймворк jUnit для Android (используя ActivityInstrumentationTestCase2), но у меня возникают проблемы с разработкой, как вводить фиктивный источник данных и активность Пример:

В активе у меня есть

public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState,R.layout.market_screen);
        ListView products = (ListView)findViewById(R.id.product_list);
        MarketsAdapter adapter = new  MarketsAdapter(this, new ProductDataSource());
        products.setAdapter(adapter);

}

В настоящее время я передаю ProductDataSource в адаптер, который подключается к веб-сервису для загрузки продуктов для адаптера. В своих тестах я не хочу подключаться к веб-сервису. Как лучше всего внедрить фиктивный источник данных в упражнение для тестирования? Должен ли я создать ProductDataSource в экземпляре приложения, а затем использовать MockApplication в своих тестах для создания фиктивного источника данных?

Спасибо

Я решил, выполнив в методе класса setUp() следующие действия: Схватите ссылку на ListView и установите Mock Data Source, используя setAdapter(MockDataSource). Это должно быть выполнено в потоке пользовательского интерфейса, используя метод runOnUiThread().

mActivity = getActivity();
mDataSource = new FakeDataSource();     
mMarketsListView = (ListView)mActivity.findViewById(R.id.product_list);
mActivity.runOnUiThread(
      new Runnable() {
        public void run() {
          mMarketsListView.setAdapter(new MarketsAdapter(mActivity,mDataSource));

        } // end of run() method definition
   } // end of anonymous Runnable object instantiation
); // 

1 Ответ

7 голосов
/ 14 июня 2012

Судя по вашему разрешению, вы больше ссылаетесь на "издевательство", как на заглушение некоторых тестовых данных.Это всегда отличный способ продвинуться вперед в разработке, когда вы больше озабочены функциональностью и не особо заботитесь о специфике.

Так что я просто даю вам этот ответ, потому что вы сказали, что вы новичокна модульное тестирование.Поэтому, если вы писали модульный тест, который зависел от ProductsDatasource , вы могли бы также использовать инфраструктуру Mocking для подключения «фиктивного» объекта вместо того, чтобы заглушить конкретный класс.Я больше работаю с .Net, чем с Java, но все мои примеры кода будут использовать JUnit и JMock для описания того, о чем я говорю.Помните, что интерфейс - это просто контракт, в котором говорится, что ваш класс будет предоставлять указанные методы.

Допустим, у вас есть реализация интерфейса ProductsDatasource, например:

public interface IProductsDatasource {

    public List<Product> getProducts();

}

И конкретный тип:

public class ProductsDatasource implements IProductsDatasource {

    private List<Product> mProducts;

    public ProductsDatasource(List<Product> products) {
        mProducts = products;
    }

    public List<Product> getProducts() {
        return mProducts;
    }

}

Теперь, скажем, вы что-то тестируете, скажем TargetAdapter , который принимает ProductsDatasource .Если вы создадите новый ProductsDatasource , у вас будет зависимость.Ваш модульный тест теперь будет зависеть от класса, который вы тестируете, и ProductsDatasource .Возможно, вы уже тестировали ProductsDatasource в другом наборе.

public class TargetAdapter {

    private IProductsDatasource mDatasource;

    public TargetAdapter(IProductsDatasource datasource) {
        mDatasource = datasource;
    }

    public List<Product> products() {
        return mDatasource.getProducts();
    }

}

Итак, вот контрольный пример без насмешек, который подробно описывает то, о чем я говорю.

@Test
public void TargetAdapterReturnsProducts() {

    List<Product> data = new ArrayList<Product>();
    data.add(new Product("Sample Product 1"));
    data.add(new Product("Sample Product 2"));
    data.add(new Product("Sample Product 3"));


    TargetAdapter adapter = new TargetAdapter(new ProductsDatasource(data)); // See the dependency
    List<Product> products = adapter.products();

    Assert.assertNotNull(adapter);
    Assert.assertTrue(products.size() == 3);
}

Итак, чтобы проверить мой адаптер, мне нужно создать новый адаптер и новый источник данных.Меня не волнует источник данных, мне просто нужно убедиться, что мой адаптер выполняет то, что я намеревался сделать.Mocking позволяет мне тестировать мой адаптер изолированно, указав тип интерфейса и настроив, как я хочу, чтобы он себя вел.Теперь я не привязан к конкретной реализации класса для тестирования моего адаптера.

Итак, вот пример, где я использую JMock для создания фиктивного источника данных:

@Test
public void MockingTest() {

    final Mockery context = new Mockery();

    final List<Product> mockData = new ArrayList<Product>();
    mockData.add(new Product("Sample Product 1"));
    mockData.add(new Product("Sample Product 2"));
    mockData.add(new Product("Sample Product 3"));

    final IProductsDatasource mockDatasource = context.mock(IProductsDatasource.class);

    context.checking(new Expectations(){{
        oneOf (mockDatasource).getProducts(); will(returnValue(mockData));  // This is where I tell JMock to return my test data when getProducts() is called
    }});

    TargetAdapter adapter = new TargetAdapter(mockDatasource); // No dependency ;)
    List<Product> products = adapter.products();

    Assert.assertNotNull(adapter);
    Assert.assertTrue(products.size() == 3);

}

Так как выЯ сказал, что вы новичок в модульном тестировании, и я хотел бы указать на мощь объектов Mock в модульном тестировании и на то, как вы можете использовать их для написания лучшего кода.Вы также можете настроить фиктивные объекты, чтобы убедиться, что ваш целевой объект вызывает метод в вашей имитации.Я часто это использую, когда меня не интересует реализация метода или результата, я просто хочу убедиться, что мой класс вызывает его, когда это необходимо.Теперь, чтобы все это работало, вы должны использовать интерфейсы, но довольно просто сделать refactor -> extract interface

Я запустил все это в eclipse, прежде чем опубликовать его, поэтому кодработает, если вы хотите поиграть с ним.Надеюсь, это поможет!

...