Страницы

Поиск по вопросам

Показаны сообщения с ярлыком юнит-тесты. Показать все сообщения
Показаны сообщения с ярлыком юнит-тесты. Показать все сообщения

среда, 15 апреля 2020 г.

Процент покрытия кода юнит-тестами

#c_sharp #net #тестирование #юнит_тесты

                    
Есть ли какой нибудь способ точно оценить процент кода, покрытого юнит-тестами (nunit)?    


Ответы

Ответ 1



Для тестов в Visual Studio есть встроенные средства, позволяющие считать покрытие. Нужно Включить расчет покрытия: Test > Edit Test Run Configurations > Your Test Configuration, далее Code Coverage, и выбрать сборку для тестирования. Прогнать тесты. Правый клик по тестам, и вызов Code Coverage Results. В итоге вы увидите статистику по процентам Not Covered Blocks Not Covered Blocks % Covered Blocks Covered Blocks % Не проверял, как это соотносится с nUnit. Еще есть сторонние стредства, например, nCover, partCover, openCover.

четверг, 19 марта 2020 г.

Проблема с import в python при написании unittest'ов

#python #юнит_тесты


Мне достался в "наследство" некоторый немалый по размерам проект.

Структура папок проекта примерно такая:

projectname
---somefolder1
---somefolder2
------somesubfolder
---------__init__.py
---------module1.py
---------module2.py
---tests
------testsomesubfolder1
---------__init__.py
---------recipy1.py
---project.py


Для разработки я использую PyCharm. Для запуска у меня создана конфигурация python
в которой задано:

Script: D:\project\projectname\project.py
Working directory: D:\project\projectname


Я хочу покрыть часть проекта unit-test'ами. 

Например, мне необходимо написать тест в recipy1.py для некоторого класса из somefolder1/somesubfolder/module1.py

Как мне правильно сделать import для module1 в recipy1?

# recipy1.py
# как написать import для module1?
import unittest

class Test1(unittest.TestCase):

    def test_testtest(self):
        self.assertEquals(100,100)

    def test_fakeclass(self):
        obj = module1.SomeClass(10)
        self.assertEquals(10, obj.GetValue() )

if __name__ == '__main__':
    unittest.main()


Правильно ли я делаю, что пишу тесты в отдельной папке? 

Имеет ли значение Working directory, указанная в конфигурации python для запуска
проекта (запуска project.py). Какой Working directory мне необходимо указать для тестов?
    


Ответы

Ответ 1



Для начала создай в папках somefolder1 и somefolder2 файл __init__.py Иначе ты ни когда не достучишься до своих модулей. docs.python.org читаем внимательно;)

воскресенье, 15 марта 2020 г.

Как сделать data-driven юнит-тесты?

#java #юнит_тесты #junit


Тестирую веб-сайт с использованием Selenium Webdriver (Firefox) и JUnit. В данный
момент разные тест-кейсы работают с одним набором данных. Хочу разделить данные и реализацию,
чтобы запускать одни и те же сценарии с разными данными.

Пока что данные инциализируются в @Before, но я могу выделить их хоть в XML, хоть
в Properties. С этим затруднений нет.

В чем проблема: не представляю, как заставить юнит-тесты запускаться многократно,
используя различные данные?

Я мог бы легко это сделать, отказавшись от использования JUnit. Просто вручную запускать
методы и вручную же перебирать и скармливать им данные. Но это страшный велосипед и
я не хочу терять преимуществ JUnit (таких, например, как автозапуск через Maven, красивое
логирование, легкий запуск одного метода из тест-комплекта либо всего комплекта и т.д).

public class TestSuite {
    /**
     * Входные данные — строки и числа 
     * */
    String searchString;
    String firmID;
    String geoID;

    private DGDriver driver; //extends FirefoxDriver

    @Before
    public void setUp() throws Exception {
        driver = new DGDriver(); 
        driver.manage().timeouts().implicitlyWait(1000, TimeUnit.SECONDS);

        /** Сейчас данные инициализируются так */
        searchString = "главный вокзал";
        firmID = "141265769369926";
        geoID = "141373143526113";

    }

    @Test
    public void testCase1 {

        /* Тут вызываются методы, использующие данные.
        Думаю, по именам понятно, как они их используют */

        driver.homepage();
        driver.searchFor(searchString);
        driver.searchResults.clickItem(firmID);
        driver.firmCard.clickAddress();
        ...
    }
    ...
}

    


Ответы

Ответ 1



Решил следующим образом: В JUnit есть аннотации @Parametrized /** * Обязательна вот такая аннотация класса: */ @RunWith(Parameterized.class) public class LeafletMarkerTests { /** * значение аннотации value указывает на номер параметра в массиве */ @Parameterized.Parameter(0) public String searchString; @Parameterized.Parameter(1) public String firmID; @Parameterized.Parameter(2) public String geoID; @Parameterized.Parameter(3) public int expectedTransformX; @Parameterized.Parameter(4) public int expectedTransformY; @Parameterized.Parameter(5) public int expectedTransformZ; Vector3d expectedCzarTransform; // разные другие переменные /** * можно задать названия датасетов, чтобы было понятнее, на каком тест зафейлился */ @Parameterized.Parameters(name = "{index}: {0}") public static Collection data() { return Arrays.asList(new Object[][]{ /*{searchString, firmID, geoID, x, y, z}*/ {"главный вокзал", "141265769369926", "141373143526113", 767, 289, 0}, {"цирк", "141265769338191", "141373143518884", 935, 289, 0}, {"оперный", "141265769360673", "141373143521691", 767, 289, 0}, {"старый дом", "141265769360664", "141373143532548", 767, 289, 0}, {"сансити", "141265770417218", "141373143572328", 767, 289, 0}, }); } @Before public void setUp() throws Exception { driver = new DGDriver(); //extends FirefoxDriver driver.manage().timeouts().implicitlyWait(1000, TimeUnit.SECONDS); // Инициализация этих параметров в @Before уже не нужна } public void test1() {...} ... } Так запущенный параметризованный тест-комплект выглядит в IntelliJ IDEA CE: Нашел специализированное решение для data-driven тестов на JUnit.

пятница, 13 марта 2020 г.

Как реализовать заглушку метода поиска в базе данных для тестового контекста?

#c_sharp #net #entity_framework #юнит_тесты


Изолирую зависимости от реальной БД при написании модульных тестов в проекте. Интерфейс
репозитория имеет следующий вид:

public interface IRepository
{
   T Find(int id) where T: class;
}


В реальном контексте метод реализован следующим образом:

public ConcreteDBContext : DBContext, IRepository
{
//some code
   public T Find(int id) where T : class
   {
      return this.Set.Find(id);
   }
}


Вопрос: как правильно реализовать такой метод в FakeDBContext? И от чего наследовать
сам контекст, кроме самого интерфейса IRepository? 
    


Ответы

Ответ 1



Если в местах, где вы используете db context, зависимость имеет тип IRepository, то вам достаточно наследовать заглушку от этого интерфейса (для этого зависимости и интерфейсы и нужны :)). Как правило, для каждого теста вам нужна будет своя реализация метода Find. Поэтому правильнее говорить о моках. Самый правильный способ работать с моками -- использовать мок-фреймворки. Их великое множество: Moq, RhinoMock, NSubstitute и проч. Синтаксис их будет немного отличаться, однако суть работы с моками всегда одна: Создаем мок-объект. Устанавливаем, что должен возвращать нужный нам метод мок-объекта при вызове с такими-то параметрами. Проверяем, что нужный нам метод был вызван (опционально). Например, с использованием NSubstite это будет выглядеть так: var personId = 1; var repo = Substitute.For(); // для id = 1 возвращаем объект repo.Find(personId).Returns(new Person { Name = "John", LastName = "Doe" }); // для всех остальных id возвращаем null repo.Find(Arg.Is(a => a != 1)).Returns(null); var someObjectThatUsesRepo = new SomeObjectThatUsesRepo(repo); someObjectThatUsesRepo.SomeMethod(); // проверяем, что метод вызвался ровно один раз repo.Received(1).Find(personId);

Обработчик проваленного теста

#cpp #юнит_тесты


Производится юнит-тестирование usb-устройства (CDC). Тест открывает устройство, формирует
запрос, отправляет его устройству, проверяет ответ, закрывает устройство. В случае,
если тест провален, закрытие устройства не выполнится, и, следовательно, все последующие
тесты также будут провалены, так как не смогут открыть устройство. В связи с этим вопрос:
есть ли способ установить обработчик (handler, hook) проваленного теста, который бы
выполнял освобождение ресурсов, запрошенных тестирующей функцией?

Тесты создаются в Visual Studio, тип проекта c++ unit test.
    


Ответы

Ответ 1



В тестовых фреймворках всегда есть возможность указать код, который будет выполнять перед каждым тестом и после каждого теста. Этот код выполняется всегда, вне зависимости от результата теста. Соответственно в вашем сценарии можно сделать так: Код перед тестом открывает устройство. Выполняется тест. Код после теста закрывает устройство. В MSTest для этого используются атрибуты TestInitialize и TestCleanup. P.S. Только это у вас не юнит-тестирование, а интеграционное тестирование. В юнит-тестировании не используются сторонние зависимости.

Ответ 2



Ваши ответы подсказали мне, в какой области искать верный ответ. В Visual Studio в Native Unit test методы до и после тестирования объявляются следующим образом: TEST_METHOD_INITIALIZE(methodName) { // method initialization code } TEST_METHOD_CLEANUP(methodName) { // test method cleanup code } Внутри этих методов можно генерировать исключения и вызывать функции из статического класса Assert. Больше информации здесь: MSDN

unit test как протестировать отдельные операции в методе

#java #юнит_тесты


Метод проверяет является ли елемент массива не буквенным символом

public static char[] checkLetterInWord(char[] checkWord, int firsLetter, int lastLetter){
        while(firsLetter


Ответы

Ответ 1



Перечитайте предыдущий ответ о том, какие должны быть тесты. Перечитав, хорошенько подумайте и составьте список тесткейсов для метода checkLetterInWord. Составив правильный список тесткейсов, вы поймете, что все ветки вашего метода уже покрыты тестами. Это работает именно так. Программист обычно не задается мыслью "протестировать вот этот кусок кода", особенно если код пишется в стиле TDD. Тестируется метод целиком, но под разными углами, так сказать. Отталкивайтесь от тесткейсов, пишите тесты, потом выполняйте и смотрите на покрытие метода. И если вдруг увидите, что какая-то ветка не покрыта, значит вы упустили какой-то тесткейс.

Ответ 2



Нет. Как вы себе представляете, можно обратиться к части метода из другого метода, исходя из специкации java? Правильный способ - это сразу писать так, чтобы было удобно тестировать. Пишите отдельный тест для каждого условия, а потом смотрите как можно можно завалить ваш код: бесконечный цикл, нулевые ссылки и т.д. И если это имеет смысл в рамках вашего приложения, для них тоже пишите тесты.

Как вы истрактовали бы постановку тестовой задачи?

#java #юнит_тесты #тестирование #junit


Имеется тестовое задание:


  Дан файл вида

operand1;operand2;operation;result
operand1;operand2;operation;result
operand1;operand2;operation;result
operand1;operand2;operation;result

  
  
  
  Каждая строка описывает арифметическое действие. 
  
  
  operand1 и operand2 - операнды, целые числа 
  operation - арифметическое действие + - / * 
  result - результат операции operation над operand1 и operand2
  
  
  В файле могут содержаться любые значения полей
  
  
  
  Требуется
  
  
  Реализовать юнит (JUnit) тесты арифметических действий.
  Каждое действие должно выглядеть в отчете как отдельный тестовый сценарий
  
  
  Конец задания
  
  


Я понимаю, как написать тесты JUnit, но не понимаю, что именно тестируется в данном
случае.

Правильно ли я считаю, что нужно сначала написать код, который парсит переменные,
4 функции для разных арифметических действий, а потом проверять с помощью JUnit корректность
работы функций на основании тестовых равенств в файле 
?

Прошу вас высказать свое понимание задачи.
    


Ответы

Ответ 1



Я бы написал скрипт (хотя, если там несколько строк, то можно и ручками), который на основании исходного файла нагенерирует Java файл с тестами. То есть, на каждую строку исходного файла будет генерировать что то вида @Test public void test1() { int actual = operand1 operationoperand2; int expect = result; assertEquals(expect , actual); } ну м конечно несколько строк "обвязки" для всего этого, что бы модуль был "компилируемый". В более навороченном виде я бы добалял проверку на 0 для operand2 если operation равно /. UPD Вот на коленке на perl сделать за минут 5, для собеседования считаю самое оно #!/usr/bin/perl use strict; use warnings; print ' import junit.framework.*; public class JavaTest extends TestCase { protected void setUp(){ } '; my $i = 1; while (my $line = <>) { chomp $line; my ($op1, $op2, $oper, $result) = split /;/, $line; print <<"ONE_TEST"; \@Test public void test$i() { int actual = $op1 $oper $op2; int expect = $result; assertEquals(expect, actual); } ONE_TEST $i += 1; } print "}\n";

Ответ 2



Я бы сделал 4 теста по 1 на каждую операцию (+ - / *) Каждый тест бы выбирал из файла строки со своими операциями и проверял правильность результата. Но конечно задача очень странная.

воскресенье, 8 марта 2020 г.

Почему валится unittest тест? AttributeError: module '__main__' has no attribute 'true'

#python #python_3x #юнит_тесты #import


Имеется модуль под названием unit,
в нем определена функция def get_formated


def get_formated(first, midle, last=''):
    if last:

          full_name=first+' ' + midle + ' ' + last
    else:
        full_name=first+' '+last

    return full_name.title()



Сам тест в модуле testirovanie




from unit import get_formated
import unittest

class UTestFigny(unittest.TestCase):


    def test_ingfigny(self):
        fomatedName=get_formated('жаклин','кенеди',)
        self.assertEqual(fomatedName,'Жаклин Кенеди')


unittest.main()



Тест фейлится с сообщениями


EE
======================================================================
ERROR: testirovanie (unittest.loader._FailedTest)
----------------------------------------------------------------------
AttributeError: module '__main__' has no attribute 'testirovanie'

======================================================================
ERROR: true (unittest.loader._FailedTest)
----------------------------------------------------------------------
AttributeError: module '__main__' has no attribute 'true'



Не могу понять причину.

Используется Питон 3.5.
    


Ответы

Ответ 1



В testirovanie должно быть if __name__ == '__main__': unittest.main() __name__ может принимать два значения в зависимости от ситуации. Если модуль импортируется, то оно равно имени модуля. Если модуль исполняется напрямую, оно равно __main__. У меня в IDLE (стандартная базовая IDE от python) приведенный Вами код работает. В более же навороченной сторонней IDE выдает похожие Вашим ошибки. Очевидно, некоторые IDE по-своему обрабатывают файлы и требуют дополнительных уточнений в коде. Кроме того, именно такая конструкция предлагается официально.

понедельник, 24 февраля 2020 г.

Как покрыть тестами конструктор класса в java?

#java #intellij_idea #юнит_тесты #test_driven_development


Есть код, который по алгоритму Эвклида находит наибольший общий делитель. 

import java.util.Scanner;

/**
 * Created by user on 24.11.2015.
 * По данным двум числам 1 b) {
                return Euclid(a % b, b);          //рекурсивно вызовется             
            } //  алгоритм, если будет остаток от деления большего              
                                    
            if (b > a) {       //  числа на меньшее и наоборот    
                return Euclid(a, b % a); 
            } else return Euclid(a % b, b);

        }
    }
        public static void main(String[] args) {

        Scanner sc1 = new Scanner(System.in);      //ввод с клавиатуры 
        Scanner sc2 = new Scanner(System.in);
        int a = sc1.nextInt();
        int b = sc2.nextInt();
            System.out.println(Euclid(a,b));
    }
}


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

Пишу тест: 

import org.junit.Test;

import static org.junit.Assert.*;
public class EuclidTest {

    @Test
    public void testEuclid() throws Exception {

        int result = new Euclid(234, 45); //в этой строке ошибка компиляции 
        assertEquals(9, result, 1e-9);
    }
}


В строке, где я объявляю result мне показывает ошибку компиляции.
 

Когда я переписал класс, заменив конструктор методом с другим названием всё прошло
как по маслу. Связи с этим вопросы:


Можно ли покрыть конструктор тестами так, чтобы не вызвать ошибку компиляции?
Как это сделать?

    


Ответы

Ответ 1



public static int Euclid(int a, int b) { //конструктор Дело в том, что это у вас не конструктор, а статический метод, возвращающий int. Конструктор был бы такой (он возвращает объект класса Euclid): public Euclid(int a, int b) { //конструктор Вообще, конструктор здесь не нужен никоим образом. Вам не нужно хранить какое-то состояние, так что и объекты создавать незачем. Переименуйте ваш метод euclid с маленькой буквы, как положено по стандарту именования и пишите вот такой тест: import org.junit.Test; import static org.junit.Assert.*; public class EuclidTest { @Test public void testEuclid() throws Exception { assertEquals(9, euclid(234, 45), 1e-9); } }

Тестирование и избавление от связности классов.

#c_sharp #aspnet #aspnet_mvc #юнит_тесты #тестирование


Добрый день! У меня есть приложение asp.net mvc. В нем я пытаюсь создавать архитектуру
"по уму" - с Dependency Injection, тестами и тд. В приложении есть классы-сервис, в
которых сосредоточена бизнес-логика. Мне необходимо покрыть эти сервисы тестами. Многие
сервисы используют класс настроек, экземпляры которых им передаются в конструкторах.
Например: 

public class AppSettings  
{
    public AppSettings()
    {
        SomeStr =  WebConfigurationManager.AppSettings["str"]).ToString();  
        // и еще несколько подобных  строк         
    }

    public string SomeStr { get;set; }
}


public class MyService 
{
    public MyService(AppSettings settings)
    {
        _settings = settings;
    }

    private readonly AppSettings _settings;
}


Проблема тут вот какая. В конструкторе класса AppSettings происходит инициализация
неких переменных, данные о которых получаются из файла web.config. В коде самого приложения
это работает, а вот если я пытаюсь тестировать класс MyService в отдельном тестовом
приложении, то возникает проблема: классу нужно передавать экземпляр AppSettings, но
при его создании  вываливается исключение из-за невозможности обратиться к web.config.
Кроме того сами классы я напрямую не создаю, этим занимается DI-библиотека. Как быть
в данном случае и как нормально протестировать MyService? К тому же мне кажется проблемой
то, что в конструкторе класса AppSettings происходит обращение к `web.config. Подскажите
пожалуйста как решить эту проблему? Заранее благодарю.
    


Ответы

Ответ 1



Если вы используете класс AppSettings как класс настроек и IoC-контейнер, то опишите интерфейс IAppSettings и протестируйте с помощью Mock объектов. Приведу пример. В классе MyService вы заменяете тип аргумента конструктора settings на IAppSetting. В IoC-контейнере регистрируете реализацию. Предположим, что вы используете Ninject, тогда kernel.Bind().To(). Тогда в методе тестирования вы можете настроить свой объект. Mock() mock = new Mock(); mock.Setup(m => m.Field1).Returns(value1); MyService service = new MyService(mock.Object); Здесь вы задаете, что при запрашивании Field1 должно вернуться значение value1. Внимание поле Field1 должно быть определено в интерфейсе IAppSetting Пример показан для Ninject

воскресенье, 16 февраля 2020 г.

Тестирование и избавление от связности классов.

#c_sharp #aspnet #aspnet_mvc #юнит_тесты #тестирование


Добрый день! У меня есть приложение asp.net mvc. В нем я пытаюсь создавать архитектуру
"по уму" - с Dependency Injection, тестами и тд. В приложении есть классы-сервис, в
которых сосредоточена бизнес-логика. Мне необходимо покрыть эти сервисы тестами. Многие
сервисы используют класс настроек, экземпляры которых им передаются в конструкторах.
Например: 

public class AppSettings  
{
    public AppSettings()
    {
        SomeStr =  WebConfigurationManager.AppSettings["str"]).ToString();  
        // и еще несколько подобных  строк         
    }

    public string SomeStr { get;set; }
}


public class MyService 
{
    public MyService(AppSettings settings)
    {
        _settings = settings;
    }

    private readonly AppSettings _settings;
}


Проблема тут вот какая. В конструкторе класса AppSettings происходит инициализация
неких переменных, данные о которых получаются из файла web.config. В коде самого приложения
это работает, а вот если я пытаюсь тестировать класс MyService в отдельном тестовом
приложении, то возникает проблема: классу нужно передавать экземпляр AppSettings, но
при его создании  вываливается исключение из-за невозможности обратиться к web.config.
Кроме того сами классы я напрямую не создаю, этим занимается DI-библиотека. Как быть
в данном случае и как нормально протестировать MyService? К тому же мне кажется проблемой
то, что в конструкторе класса AppSettings происходит обращение к `web.config. Подскажите
пожалуйста как решить эту проблему? Заранее благодарю.
    


Ответы

Ответ 1



Если вы используете класс AppSettings как класс настроек и IoC-контейнер, то опишите интерфейс IAppSettings и протестируйте с помощью Mock объектов. Приведу пример. В классе MyService вы заменяете тип аргумента конструктора settings на IAppSetting. В IoC-контейнере регистрируете реализацию. Предположим, что вы используете Ninject, тогда kernel.Bind().To(). Тогда в методе тестирования вы можете настроить свой объект. Mock() mock = new Mock(); mock.Setup(m => m.Field1).Returns(value1); MyService service = new MyService(mock.Object); Здесь вы задаете, что при запрашивании Field1 должно вернуться значение value1. Внимание поле Field1 должно быть определено в интерфейсе IAppSetting Пример показан для Ninject

четверг, 13 февраля 2020 г.

Как избежать дублирования в юнит-тестах?

#java #юнит_тесты #junit


Тестирую некоторый алгоритм. Данные для алгоритма хранятся в списке. В итоге у меня
получается вот так:

@Test
public void testOneDirectModeBestCaseEven(){
    list.add(new Agent(6));
    list.add(new Agent(1));
    list.add(new Agent(2));
    list.add(new Agent(3));
    list.add(new Agent(4));
    list.add(new Agent(5));

    int i = 0;
    while(!list.hasSolution()){
        LeaderElection.solve(list, i++);
    }

    int leaderId = list.getLeaderId();
    assertEquals(6, leaderId);
}

@Test
public void testOneDirectModeBestCaseOdd(){
    list.add(new Agent(5));
    list.add(new Agent(1));
    list.add(new Agent(2));
    list.add(new Agent(3));
    list.add(new Agent(4));

    int i = 0;
    while(!list.hasSolution()){
        LeaderElection.solve(list, i++);
    }

    int leaderId = list.getLeaderId();
    assertEquals(5, leaderId);
}


И еще много функций. Получается дублирование в том что я заполняю List в каждом тесте.
Но и в setUp не вынесешь, потому что заполнять то нужно по-разному для каждого теста. 
    


Ответы

Ответ 1



Вам могут помочь параметризованные тесты. JUnit их тоже поддерживает. Приведу код, подробнее почитать можете по ссылкам. (На джаве давно не писал, поэтому скорее всего тут есть ошибки компиляции :)) @RunWith(Parameterized.class) public class LeaderElectionTests{ @Parameters public static Collection data(){ return Arrays.asList(new Object[][]{ { <список 1>, <ответ 1> }, { <список 2>, <ответ 2> } }); } private List list; private int expected; public LeaderElectionTests(List list, int expected){ list = input; expected = expected; } @Test public void testOneDirectModeBestCase(){ int i = 0; while(!list.hasSolution()){ LeaderElection.solve(list, i++); } int leaderId = list.getLeaderId(); assertEquals(answer, leaderId); } } Однако такой подход нужно использовать только когда вы тестируете один кейс на разных наборах входных данных. Если же у вас разные кейсы, причем эта разница заложена непосредственно во входных данных, то я бы рекомендовал идти по пути отдельных тестов. Это проще для восприятия, потому что так или иначе в названия тестов мы закладываем наши решения и наш опыт, полученные в процессе написания кода. Глядя же на обезличенный список тестовых наборов данных, через месяц уже будет сложно сказать, чем они отличаются между собой. Судя по коду, ваш объект list -- не просто список, он еще содержит в себе некоторую логику. Так что скорее всего разумнее будет остаться с разными тестами и дублирующимися данными, которые на самом деле не дублирующие данные, а разные тест кейсы. Хотя вам должно быть виднее, конечно.

воскресенье, 9 февраля 2020 г.

Мокинг статических методов. Когда это нужно делать?

#c_sharp #юнит_тесты #тестирование #статические_члены


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

Например, есть код

public class Hasher
{
    public static void RenameByHash(string filePath)
    {
        //...
    }
}

public class MyService
{
    public void ProccessPres(string presentationPath)
    {
        //...

        Hasher.RenameByHash(presentationPath);

        //...
    }
}


Я ведь теперь при тестировании метода ProccessPres() не могу замокать Hasher.RenameByHash().
И прихожу к тому, что приходиться вообще отказаться от статических методов, т.к. не
могу изолировать их поведение и тестировать только свой сервис.

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

Фреймворки для мокинга статических методов. Также есть статья в которой говорится,
что для мокинга статических методов можно использовать более навороченные мокинг-фреймворки
типа Typemock, JustMock, Moles. Но насколько нужно это мне, как я писал выше, было
бы ясно если я убедился бы в том, что правильно понимаю теоретическую основу статических
методов касательно их имитации. Быть может мокать поведение статические методов в большинстве
случаев и вовсе не нужно как не нужно мокать приватные методы.
    


Ответы

Ответ 1



Имхо, статические методы должны 1) Делать очень маленькую специфичную работу 2) Не должны напрямую относиться к бизнес логике 3) Как следствие 1) и 2) - их не надо мокать 4) Их можно тестировать отдельно По большому счету они очень похожи на расширяющие методы. Например, string.IsNullOrEmpty(...) DateTime.ConvertToLocalTIme(...) Но если у вас есть какой то бизнес класс, который не обладает состоянием (например, какой то сервис отсылки сообщений или калькулятор стоимости товара или что то подобное), то есть смысл делать его синглтоном, то есть иметь только 1 экземпляр такого сервиса на всё приложение/домен/подсистему и тд. Я предпочитаю делать синглтоны за счет инверсии зависимости. То есть класс сам не знает, что он синглтон - это указывается в корне композиции при регистрации типов в IoC контейнере или вручную. Таким образом, классы без состояния можно тестировать как любые другие классы. UPD На самом деле, если заходить дальше, то ваши подсистемы по идее должны взаимодействовать посредством контрактов (это такая общая рекомендация), что обычно означает интерфейсов. Грубо говоря, при использовании IoC весь необходимый для класса воспомогательный бизнес функционал должен быть инжектирован через конструтор, свойство или каким либо другоим методом (я почти всегда выбираю конструктор). То есть используюя какой то бизнес функционал, ваш класс должен вызвать метод какого либо инстанса (ведь вы не можете инжектировать статический класс/метод). Таким образом мы приходим к тому, что вызов статического метода стороннего класса идет в разрез с принципом инверсии зависимости. С другой стороны, преобразование обычного класса (или его пубичного метода) в статический увеличивает связность классов, так как интерфейсы не содерджат статических методов, а значит вызвать статический класс/метод вы можете только явно. То есть делая класс или его публичный метод статическим, вы уменьшаете гибкость системы вцелом. Потому есть смысл это делать только в тех случаях, когда у таких классов/методов в приципе возможна только одна имплементация, которая не содержит состояния и которую вы точно не захотите менять никогда (string.IsNullOrEmpty четко дает понять, что происходит внутри метода и другой реализации этого метода быть не может) - то есть в таких случаях вы жертвуете гибкостью там, где она в принципе не нужна. Во всех остальных случаях я стараюсь избегать модификатора static вообще. Что же касается внутренней реализации класса - приватных статических членов, это уже другой разговор, тут уже можно делать статическим все, что удобно, так как это внутренняя кухня класса (особенно если от этого класса запрещено наследование).

среда, 5 февраля 2020 г.

В чем разница между Assert.AreEqual и Assert.Equals C#?

#c_sharp #visual_studio #юнит_тесты


В чем разница между Assert.AreEqual и Assert.Equals C#?
    


Ответы

Ответ 1



Assert - это обычный класс, который, как и все классы .NET, наследуют от System.Object. Assert.Equals – это просто наследуемый метод Object.Equals. Asser.AreEquals – это метод класса Assert, который выкидывает AssertFailedException если два объекта не равны. Это штатный способ проверять утверждение о равенстве двух объектов. В версии TestFramework 14.0.0.X Assert.Equals перекрыт, и выкидывает исключение, что бы избежать путаницы и не вводить никого в заблуждение. Внутри написано что-то такое: /// Static equals overloads are used for comparing instances of two types for reference /// equality. This method should not be used for comparison of two instances for /// equality. This object will always throw with Assert.Fail. Please use /// Assert.AreEqual and associated overloads in your unit tests. public new static bool Equals(object objA, object objB) { Asser.Fail("Assert.Equals should not be used for Assertions. Please use Assert.AreEqual & overloads instead."); }

пятница, 31 января 2020 г.

Тестирование слоя валидации данных

#c_sharp #visual_studio #entity_framework #юнит_тесты


Люди, подскажите хорошее решение. Имеется некий слой валидации данных, бизнес логики
приложения и слой работы с БД. Есть правила накладывающие ограничения (например у некоторых
сущностей есть 2 идентификатора [Guid и string определенной длины] и должно контролироваться
отсутствие их дублирования с помощью валидации). При всем этом, при написании тестов
возникает кейс что мы тестируем работу слоя валидации и нужно что бы валидация не прошла
(т.к. в базе такой объект уже существует), либо наоборот прошла так как такого объекта
еще нет. И тут возникает проблема потому что мы не знаем что есть а чего нет в тестовой
базе. Возможный вариант решения это заранее наполнить базу некими сущностями и постоянно
работать с ними, но такой подход не нравится тем что при написании теста приходится
помнить что у нас есть в базе (да и тем более в других тестах могу создаваться новые
сущности), а хочется что бы мы могли в каждом тесте создать N удобных для этого теста
объектов и работать с ними. Но такой подход с обычной базой невозможен так как единственное
решение которое я вижу, это нужно будет чистить базу в каждом тесте а это не быстро
во всяком случае. Что приходит в голову это создать мок объекты работы с базой которые
можно будет чистить в каждом тесте или создавать заново но это не кажется изящным решением.
Собственно как решить проблему изящно ?
PS. Язык C# тесты встроенные в visual studio, использую entity-framework 
    


Ответы

Ответ 1



Ваши юнит-тесты вообще не должны подключаться к базе данных. Вы должны тестировать только ваш класс, отвечающий за валидацию, все внешние зависимости этого класса надо заменить моками. У вас должен быть мок, который будет притворяться, что он подключается к базе данных, но на деле никуда не подключаться, а просто возвращать подходящие для тестирования значения. На примере одного теста. Скажем вы проверяете, что в базе нет двух элементов с одинаковым id. Тогда тест должен выглядеть примерно так: создаете мок класса для доступа к базе данных настраиваете нужный метод этого мока так, чтобы он возвращал заведомо неверные данные передаете этот мок валидатору и убеждаетесь, что валидация не прошла Код примерно такой (в примере используется nunit и moq): [Test] public void Test1() { // arrange var dbRepositoryMock = new Mock(); dbRepositoryMock.Setup(x => x.GetItems()).Returns(new [] {new Item(){ Id = 1}, new Item(){ Id = 1}}); var validator = new Validator(); // action var validationResult = validator.ValidateItemsUnique(dbRepositoryMock.Object); // assert Assert.IsFalse(validationResult); } То есть идея вот в чем: вы создаете реальный экземпляр ТОЛЬКО для тестируемого класса. В нашем случае тестируемый класс - валидатор. Для всех зависимостей вы создаете моки, и настраиваете у этих моков ТОЛЬКО те методы, которые используются тестируемым функционалом, все остальные просто игнорируете. Например, у вашего репозитория может быть еще 20 методов, возвращающих разные сущности из базы данных, но если вы проверяете уникальность Item'ов, вы настраиваете возвращаемое значение только метода GetItems(), а все остальные методы просто игнорируете. Про подключение к базе данных вообще забудьте, ваши юнит тесты должны выполняться без этого. Если вам сложно написать код в таком стиле - без реального подключения к базе данных, значит он плохо приспособлен для юнит-тестирования, и надо выполнять его рефакторинг. Ваша попытка написать юнит-тесты с подключением к реальной бд довольно типичная ошибка для начинающих писать юнит-тесты. Я тоже пытался делать так и видел как то же самое пытаются сделать другие. Эта ошибка указывает на то, что вы не до конца понимаете смысл юнит-тестирования и вам стоит потратить время на чтение какой нибудь книги по этой теме. "Разработка через тестирование" Кента Бека будет отличным вариантом.

Ответ 2



Строго говоря, вы пишете не юнит тесты, а, скорее, интеграционные тесты. Если не ограничиваться ответом "тесты на базе это плохо, постарайтесь по возможности избегать этого" - то проще всего решить проблему чистки базы в интеграционных тестах отменой транзакции на каждом тесте: один раз создавать чистую базу при старте тестов (указанием DropCreateDatabaseAlways, или вручную, по статическому флагу) создавать новый TransactionScope в TestInitialize диспоузить этот TransactionScope в TestCleanup, без вызова Complete Работать будет не мгновенно, накладные расходы будут порядка 100-200 ms на тест, но для существующего (уже написанного без тестов) кода это самый быстрый вариант. Eсли вы планируете рефакторить код в сторону лучшей тестируемости моками - лучше если к моменту рефакторинга у вас будут готовые тесты на тот код, который уже есть, пусть даже эти тесты будут медленными и будут делать реальные запросы к базе. Рефакторить непокрытый тестами код может только Чак. Для всех остальных это черевато затягиванием сроков, толпами новых багов и "в гробу я видал ваши тесты и рефакторинг" от ближайшего вверх по иерархии нетехнического начальника. Кстати, ваш способ валидации ненадежен. Ничто не помешает гому потоку влезть в базу между проверкой с результатом "все ок" и вставить туда дубликат. Такие проверки все равно надо дублировать констрейнтами на уровне базы.

вторник, 28 января 2020 г.

Модульное тестирование, необходимо ли?

#c_sharp #юнит_тесты


Доброго времени суток.
1) Часто ли в разработке коммерческого ПО применяются модульные тесты?
2) Обязательно ли оно или желательно?
3) Где можно почитать на русском про модульные тесты, желательно "без воды".    


Ответы

Ответ 1



1) Да, часто. В большинстве сколько-нибудь серьезных проектов, с которыми лично мне приходилось работать, модульные тесты были 2) Насчет его обязательности может сложиться одно пагубное заблуждение. Программист, особенно не использовавший его ранее, может подумать, что оно излишне (на него, дескать, тратится время и силы, которые можно было бы использовать для написания новых фич). И вообще, дескать, достаточно просто не делать ошибок, и никакие тесты не нужны. Однако ошибок не делает только Господь Бог, а он с разработкой уже, кажется, завязал после того, как за семь дней написал нашу вселенную (ну, если, конечно, это был именно он). Простые же смертные, к сожалению, к ошибкам склонны, а потому тестирование относится скорее к обязательнвым дисциплинам, чем к желательным. Разумеется, если вы не покроете ваш код тестами, небо не упадет на землю, но ваша софтина упадет наверняка. Стоит лишь разграничивать тот код, который нужно покрывать тестами, и тот, для которого тесты будут пустой тратой времени. В основном это разграничение упирается в объем и сложность того кода, что вы пишете. Кстати, картинка, предоставленная @Etki, хорошо иллюстрирует эту мысль - пока вы пишете что-то небольшое, тесты только отнимут у вас ресурсы. Но чем больше вы тратите на свой проект времени, тем больше пользы принесет тестирование. 3) Эта тема не сказать что сильно сложна. Принцип модульного тестирования становится интуитивно понятен после нескольких практических занятий. Поэтому скорее всего будет достаточно пары-тройки статей с примерами. Вот пример с MSDN

Ответ 2



1) Когда как. Маленькие компании любят работать «на тяп-ляп», большие любят требовать совершенно избыточные ненужные тесты. Наличие адекватного тимлида обычно всё улучшает. 2) Теоретически обязательно! На практике, как вы сами понимаете...

Ответ 3



Зависит от бюджета обычно, т.к. процесс строится, исходя из применения тестов "в нагрузку", без изменения самой парадигмы процесса. Нету ещё до конца понимания того, что если изменить парадигму разработки и написание тестов поставить в начало процесса, то бюджет наоборот сократиться. Обязательно оно, если процесс строится вокруг него, т.е. разработка тестов, покрывающих функционал, а только потом разаработка кода, который покрывает эти тесты. Желательно - это когда наоборот, но в таких ситуациях оно скорее НЕжелательно. :-) Так что я бы поставил вопрос по-другому - или обязательно, или НЕжелательно. Самому интересно. :-)

воскресенье, 26 января 2020 г.

Как протестировать скорость работы и затраты памяти?

#юнит_тесты #java #junit


Я начинаю осваивать Java, так что без рук). 
Мне нужно на простой программе протестировать скорость роботы и затраты памяти. 
Как в Java можно это сделать?

import java.io.*;
class SearchPhrase {

// walk to root way
public void walk(String path, String whatFind) throws IOException {

    File root = new File(path);
    File[] list = root.listFiles();
    for (File titleName : list) {
        if (titleName.isDirectory()) {
            walk(titleName.getAbsolutePath(), whatFind);
        } else {
            if (read(titleName.getAbsolutePath()).contains(whatFind)) {
                System.out.println("File: " + titleName.getAbsolutePath());
            }
        }
    }
}

// Read file as one line
public static String read(String fileName) {
    StringBuilder strBuider = new StringBuilder();
    try {
        BufferedReader in = new BufferedReader(new FileReader(new File(
                fileName)));
        String strInput;
        while ((strInput = in.readLine()) != null) {
            strBuider.append(strInput);
            strBuider.append("\n");
        }
        in.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return strBuider.toString();
}

public static void main(String[] args) {

    SearchPhrase example = new SearchPhrase();

    try {
        example.walk("C:\\Documents and Settings\\User\\Java", "programmed");
    } catch (IOException e) {
        e.printStackTrace();
    }
  }
}

    


Ответы

Ответ 1



Это решение разработано самим автором вопроса, но он оставил его в вопросе. Время работы программы: public static void main(String[] args) { SearchPhrase example = new SearchPhrase(); long startTime = System.currentTimeMillis(); try { example.walk("E:\\Document\\Effortless English\\Level 3", "spot"); } catch (IOException e) { e.printStackTrace(); } long stopTime = System.currentTimeMillis(); long elapsedTime = stopTime - startTime; System.out.println(elapsedTime); } Потребление памяти: package task; public class SearchPhrase_PerformanceTest { private static final long MEGABYTE = 1024L * 1024L; public static long bytesToMegabytes(long bytes) { return bytes / MEGABYTE; } public static void main(String[] args) { SearchPhrase example = new SearchPhrase(); example.askUserPathAndWord(); // Get the Java runtime Runtime runtime = Runtime.getRuntime(); // Run the garbage collector runtime.gc(); // Calculate the used memory long memory = runtime.totalMemory() - runtime.freeMemory(); System.out.println("Used memory is bytes: " + memory); System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory)); } } Все просто и понятно ;)

Запуск тест-сьютов юнит-тестов в PyCharm

#python #selenium #pycharm #юнит_тесты


Есть следующий файл:
SomeTest1.py 

    __author__ = 'vbilohorodskyi'

import unittest
from selenium import webdriver
from selenium.webdriver.common import keys


class InitDriverTest(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Firefox()

    print("=========================================================")

    #initializing browser and verifying if the selected URL is accessible and has
correct content by key values
    def testInit_driver_and_url(self):
        driver = self.driver
        driver.get("somesite.com")
        assert "Some title" in driver.title

        print("Init driver and url: PASS")

    def tearDown(self):
        self.driver.quit()

    if __name__ == "__main__":
        unittest.main()


И есть второй файл с такой же архитектурой, но другим тест-кейсом.

Оба этих файла находятся в одном Python Package, и отдельно запускаются без проблем.
Как только пытаюсь запустить все тесты с пекеджа, наблюдаю ошибку 

Process finished with exit code 0
Empty test suite.


файл init.py для данного пекеджа пустой. 

Пробовал менять конфигурацию Runner'а, игрался с названиями классов и методов(test-
вначале и -Test в конце), пробовал разные Regexp'ы в качестве паттерна в конфигурации
Runner'а, но ничего не помогло. 

Кто-нибудь сталкивался с этой проблемой? 
Буду признателен за помощь в настройке запуска тест-сьютов из пекеджа.
    


Ответы

Ответ 1



У меня успешно работает такая конфигурация тестов: Настройки: "All in folder", указана папка (не пакет) с тестами: tests. Никаких шаблонов не указано. Рабочая папка — папка всего проекта. Вот как выглядит папка с тестами: Все файлы имеют префикс test_, все классы унаследованы от unittest.TestCase и имеют префикс Test. Версия PyCharm Community Edition 5.0.3. Кроме этого, у вас в примере с табуляциями беда. if __name__ == "__main__": не должен иметь табуляции впереди.

Ответ 2



Путём пары попыток выяснилось, что для обработки всех тестов в каталоге есть 2 варианта: все имена файлов с тестами начинать с test_ ни одного имени файла не начинать с test_, а в конфигурации в качестве паттерна указать *.py __init__.py не нужен, т.к. вряд ли будем импортировать.

Ответ 3



Все тесты запустятся, если следовать правилу - файлы с тестами должны содержать в имени строку «test_», имена классов — начинаться с «Test», а методов — со строки «test_».

Ответ 4



Содержание выделенного __init__.py (не выделенный __init__.py - пустой): __all__ = ['ex01', 'ex02'] Содержание ex01.py: import unittest class TestCase01(unittest.TestCase): def test_003(self): self.assertEqual(True, True) Содержание ex02.py: import unittest class TestCase02(unittest.TestCase): def test_001(self): self.assertEqual(True, True) Содержание run_test_package.py: import unittest from tests_package.ex01 import * from tests_package.ex02 import * if __name__ == '__main__': unittest.main() Запускаем run_test_package.py и скрипты тестов выполняются.

Как настроить gerrit + unit tests + CI (unity3d проект)

#git #unity3d #юнит_тесты #непрерывная_интеграция


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


Ответы

Ответ 1



Не могу сказать наверняка, но могу попробовать направить вас в нужном направлении: не желаете ли воспользоваться хуками Git'а, настроенными на commit? Т.е. например, в корне вашего репозитория git найдите директорию hooks (.git/hooks), затем создайте там файл с соответствующим именем (имя файла должно соответствовать имени перехватчика), т.е. например, вам подойдет pre-commit, внутри которого выполнить необходимые действия. Если вас, все-таки, заинтересует данный подход, то можете более подробно ознакомиться здесь и здесь.

четверг, 23 января 2020 г.

Синглтон и тестирование

#java #юнит_тесты #junit


В общем есть следующий код, состоящий из синглтона и класса, использующего его (упрощенная
версия).

public final class Singleton {
    private static Singleton s_instance = new Singleton();

    public static Singleton getInstance(){
        return s_instance;
    }

    public String getString(){
        return "string";
    }

    public String getStringTest(){
        return "string_test";
    }
}


public class SomeClass{
    private String someString;

    public SomeClass(){
        someString = Singleton.getInstance().getString();
    }
}


Синглтон считывает конфигурацию из файла и предоставляет методы для ее получения
компонентами программы (getString() и т.п.). 

Для тестирования работы класса SomeClass мне нужно, чтобы он получил другое значение
при вызове getString(), а именно то, которое вернет getStringTest().

Я хотел бы иметь что-то типа такого, но столкнулся с тем, что не знаю, как создать
mock или spy объект для синглтона:

when(Singleton.getInstance().getString()).thenReturn(Singleton.getInstance().getStringlTest());


Ну и как следствие вопрос: Как можно создать mock или spy объект для синглтона или
как по-другому можно подменить возвращаемое синглтоном значение? Java, jUnit4.
    


Ответы

Ответ 1



Mockito, мне кажется, прекрасно справится с Вашей задачей. Singleton singleton = mock(Singleton.class); when(singleton.getStringTest()).thenReturn("What you need");

Ответ 2



Приведу самый эффективный, пусть и не очень красивый (и очень непопулярный) вариант тестирования Singleton. Шаг 1. Добавьте метод Singleton.setInstance и сделайте конструктор Singleton защищенным: public final class Singleton { private static Singleton s_instance = new Singleton(); public static setInstance(Singleton instance) { s_instance = instance; } // ... } Шаг 2. Мокните класс Singleton любым способом и задайте полученный объект через Singleton.setInstace Шаг 3. Протестируйте класс SomeClass. А когда у вас появится время для выплаты технического долга, выполните еще один шаг: Шаг 4. Проведите рефакториг класса SomeClass, выкинув любое использование метода Singleton.getInstance. Инжектируйте экземпляр Singleton в SomeClass в явном виде.