Как лучше всего создать тест DB при выполнении TDD?

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.

blockquote>

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.
blockquote>

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа прекращает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

Трассировка, где эти значения должны быть установлены

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

Если вы попытались отладить проблему и до сих пор не имеете решения, вы можете отправить вопрос для получения дополнительной справки, но не забудьте включить то, что вы пробовали до сих пор. Как минимум, включите stacktrace в вопрос и отметьте важные номера строк в коде. Также попробуйте сначала упростить код (см. SSCCE ).

17
задан Kindle Q 13 February 2017 в 08:37
поделиться

5 ответов

Принятие Вы используете шаблон Репозитория из Витрины Rob Conery MVC:

http://blog.wekeroad.com/mvc-storefront/mvc-storefront-part-1/

я следовал учебному руководству Rob Conery, но столкнулся с тем же, хотят как Вы. Лучшая вещь сделать переместить Ложные Репозитории, которые Вы создали в отдельный проект под названием Насмешки тогда, можно выгрузить их довольно легко с реальными при инстанцировании сервиса. Если Ваше ощущение себя, предприимчивое, Вы могли бы создать фабрику, которая принимает значение из файла конфигурации, чтобы инстанцировать или насмешки или реального репозитория,

, например,

public static ICatalogRepository GetCatalogRepository(bool useMock)
{
     if(useMock)
          return new FakeCatalogRepository();
     else
          return new SqlCatalogRepository();
}

или использовать платформу внедрения зависимости:)

container.Resolve<ICatalogRepository>();

Удачи!

РЕДАКТИРОВАНИЕ: В ответ на Ваши комментарии, кажется, что Вы хотите использовать список и LINQ для эмуляции операций дб, например, GetProducts, StoreProduct. Я сделал это прежде. Вот пример:

public class Product
{
     public int Identity { get; set; }
     public string Name { get; set; }
     public string Description { get; set; }
     //etc
}

public class FakeCatalogRepository()
{
     private List<Product> _fakes;

     public FakeCatalogCatalogRepository()
     {
          _fakes = new List<Product>();

          //Set up some initial fake data
          for(int i=0; i < 5; i++)
          {
              Product p = new Product
              {
                 Identity = i,
                 Name = "product"+i,
                 Description = "description of product"+i
              };

              _fakes.Add(p);
          }
     }

     public void StoreProduct(Product p)
     {
         //Emulate insert/update functionality

         _fakes.Add(p);
     }

     public Product GetProductByIdentity(int id)
     {
          //emulate "SELECT * FROM products WHERE id = 1234
          var aProduct = (from p in _fakes.AsQueryable()
                         where p.Identity = id
                         select p).SingleOrDefault();

          return aProduct;
     }
}

, который имеет немного больше смысла?

3
ответ дан 30 November 2019 в 14:51
поделиться

Скучный или нет, я думаю, что Вы на правильном пути. Я предполагаю, что Вы создаете fakeRepository, который является конкретной реализацией Вашего IRepository, который в свою очередь введен на Ваш уровень служб. Это хорошо, потому что в какой-то момент в будущем, когда Вы довольны формой своих объектов и поведением Ваших сервисов, контроллеров и представлений, Вы можете тогда тест-драйв Ваши реальные Репозитории, которые будут использовать базу данных для сохранения тех объектов. Конечно, природа тех тестов будет интеграционными тестами, но столь же важный если не больше.

Одна вещь, которая может быть менее скучной для Вас, когда время настает для создания реальных репозиториев, состоит в том при использовании nHibernate для персистентности, Вы будете в состоянии, позволяют nhibernate генерировать Вашу базу данных после создания карт nhibernate для объектов, предполагая, что Вы не должны использовать схему прежней версии.

, Например, у меня есть следующий метод, который называет мой SetUpFixture для генерации моей схемы дб:

public class SchemaBuilder
{
   public static void ExportSchema()
    {
        Configuration configuration = new Configuration();
        configuration.Configure();
        new SchemaExport(configuration).Create(true, true);
    }
}

и мой SetUpFixture следующие:

[SetUpFixture]
public class SetUpFixture
{
    [SetUp]
    public void SetUp()
    {
        SchemaBuilder.ExportSchema();
        DataLoader.LoadData();
    }
}

, где DataLoader ответственен за создание всех моих данных семени и данных тестирования с помощью реального respoitory.

Это, вероятно, не отвечает на Ваши вопросы, но я надеюсь, что это служит для заверения Вас в Вашем подходе.

Greg

0
ответ дан 30 November 2019 в 14:51
поделиться

Хотя я не использую Asp. Сеть или платформа MVC у меня действительно есть потребность протестировать сервисы, не поражая базу данных. Ваш вопрос инициировал запись короткого (хорошо, возможно, не настолько короткий) сводка того, как я делаю это. Не требование это является лучшим или что-либо, но это работает на нас. Мы получаем доступ к данным через репозиторий, и при необходимости мы включаем репозиторий в оперативной памяти, как объяснено в сообщении.

http://blogs.microsoft.co.il/blogs/kim/archive/2008/11/14/testable-data-access-with-the-repository-pattern.aspx

0
ответ дан 30 November 2019 в 14:51
поделиться

Я использую полное в базе данных памяти с SQLite и ActiveRecord. В основном мы удаляем и воссоздаем базу данных, прежде чем каждый интеграционный тест будет выполнен, так, чтобы данные всегда были в известном состоянии. Содержание базы данных вставляется через код. Таким образом, пример был бы похож на это:

ActiveRecord.Initalize(lots of parameters)
ActiveRecord.DropSchema();
ActiveRecord.CreateSchema();

и затем мы просто добавляем много клиентов или что бы то ни было, стиля DDD:

customerRepository.Save(customer);

Другой способ решить это мог использовать NDbUnit для поддержания состояния базы данных.

0
ответ дан 30 November 2019 в 14:51
поделиться

Я пошел путем составления таблиц, и данные во время метода установки в классе модульного теста, запуская тесты, тогда выполнение моется во время разрушения. Да, этот метод работают, но если Вы действительно закончите тем, что использовали свои модульные тесты на отладку целей, неизменно Вы выполните установку, отладите что-то, тогда останавливаются в середине, не делая разрушения. Это является очень хрупким, и Вы, вероятно, закончите (в конечном счете) с неправильными данными в Вашей тестовой базе данных и/или неприменимых модульных тестах. Я лично думаю, что ее лучшее дразнит слой базы данных с помощью платформы насмешки. Я действительно понимаю, что иногда лучше делать логику в базе данных. Для этих случаев можно использовать инструмент как DBFit к тестам записи на слой базы данных.

0
ответ дан 30 November 2019 в 14:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: