Pull to refresh
7
0

User

Send message
Я попытался выразить предложенный вами псевдокод в коде. Не нравится — предложите свой вариант тестов в коде. А то остается слишком много места для разночтений и волшебства.

По поводу согласия — опять нужен код. А то вы меня потом как котенка будете тыкать носом в то о чем я не говорил.
		[TestMethod]
		public void ExecTaskTest()
		{
			using (var disp = new TaskDispatcher())
			{
				DateTime now = DateTime.Now;
				DateTime? time = null;

				disp.Add(TimeSpan.FromSeconds(1), () => time = DateTime.Now);

				Thread.Sleep(TimeSpan.FromSeconds(2));

				Assert.IsTrue(time - now > TimeSpan.FromSeconds(1));
			}
		}

		[TestMethod]
		public void ExecTwoTaskTest()
		{
			using (var disp = new TaskDispatcher())
			{
				DateTime now = DateTime.Now;
				DateTime? time0 = null;
				DateTime? time1 = null;

				disp.Add(TimeSpan.FromSeconds(1), () => time0 = DateTime.Now);

				Thread.Sleep(TimeSpan.FromSeconds(2));

				disp.Add(TimeSpan.FromSeconds(1), () => time1 = DateTime.Now);

				Assert.IsTrue(time0 - now > TimeSpan.FromSeconds(1));
				Assert.IsTrue(time1 - time0 > TimeSpan.FromSeconds(1));
			}
		}

		[TestMethod]
		public void ReplaceTaskTest()
		{
			using (var disp = new TaskDispatcher())
			{
				DateTime now = DateTime.Now;
				DateTime? time0 = null;
				DateTime? time1 = null;

				disp.Add(TimeSpan.FromSeconds(2), () => time0 = DateTime.Now);

				Thread.Sleep(TimeSpan.FromSeconds(1));

				disp.Add(TimeSpan.FromSeconds(2), () => time1 = DateTime.Now);

				Thread.Sleep(TimeSpan.FromSeconds(3));

				Assert.IsNull(time0);
				Assert.IsTrue(time1 - now > TimeSpan.FromSeconds(3));
			}
		}

		[TestMethod]
		public void AddSomeTimeTwoTaskTest()
		{
			using (var disp = new TaskDispatcher())
			{
				DateTime now = DateTime.Now;
				DateTime? time0 = null;
				DateTime? time1 = null;

				//как-то и где-то в отдельных потоках
				disp.Add(TimeSpan.FromSeconds(1), () => time0 = DateTime.Now);
				disp.Add(TimeSpan.FromSeconds(1), () => time1 = DateTime.Now);

				Thread.Sleep(TimeSpan.FromSeconds(2));

				//только один не null
				Assert.IsTrue((time0 != null) != (time1 != null));
			}
		}

		[TestMethod]
		public void AddSomeTimeTwoTaskTest()
		{
			using (var disp = new TaskDispatcher())
			{
				DateTime now = DateTime.Now;
				DateTime? time0 = null;
				DateTime? time1 = null;

				//как-то и где-то в отдельных потоках
				disp.Add(TimeSpan.FromSeconds(1), () => time0 = DateTime.Now);
				disp.Add(TimeSpan.FromSeconds(1), () => time1 = DateTime.Now);

				Thread.Sleep(TimeSpan.FromSeconds(2));

				//только один не null
				Assert.IsTrue((time0 != null) != (time1 != null));
			}
		}

		[TestMethod]
		public void AddNewFromEndFirstTest()
		{
			using (var disp = new TaskDispatcher())
			{
				DateTime now = DateTime.Now;
				DateTime? time0 = null;
				DateTime? time1 = null;

				disp.Add(TimeSpan.FromSeconds(1), () => time0 = DateTime.Now);

				//где-то точно (точно во время схватывания первой задачи)
				Thread.Sleep(TimeSpan.FromSeconds(1));

				disp.Add(TimeSpan.FromSeconds(1), () => time1 = DateTime.Now);
				
				//только один не null
				Assert.IsTrue((time0 != null) != (time1 != null));
			}
		}


Будем считать, что вы написали это (если нет возражений).

Мне кажется в каждом из тестов есть явные проблеммы — синхронизация по sleep
Я бы конечно начал с чего-то более общего. Но если хотите п.3 пусть будет п.3
Давайте попробуем.

Предложение — сценарий есть. Может заставим его работать вместе? Заодно лишний раз польем воды на мельницу TDD, создав реальный механизм.

Если да, то с вас тест.
Последняя решаемая с помощью этого инструмената задача — был диспетчер отложенных задач для wpf-приложения.

Пример сценария:
1. Пользователь из списка выбирал элемент. Реакцие на это — установка пакета задач в диспетчер с задержкой например 0.5 сек
2. Если пользователь в течении этого времени не делал нового выбора, то пакет начинал исполняться. Нет — новый заменял старый с новой задержкой 0.5 сек
3. Пакет состоял из обращения к БД и заполнения соответствующего контейнера данных, на который смотрел DataGrid. Первая задача выполнялась на ассинхронном потоке (для обеспечения отзывчивости интерфейса), вторая на потоке окна. По потокам раскладывает диспетчер.
4. В случае ошибки — уведомление и в зависимости от типа завершение или нет приложения. Пакет безусловно прерывается.

Пакеты и задачи исполняются последовательно относительно друг-друга. Выполнение пакета не блокирует добавление нового. Приоритизация пакетов по (время установки+задержка). Идентификация пакета по объекту маркера, определяемого при создании пакета.
Я уже отвечал на аналогичный вопрос.

Повторюсь: данное замечание имеет право на существование, но вопрос слишком сложный что бы о нем говорить так категорично.
Упращение построения автоматических тестов для ассинхронной системы.
Зачем мне тестировать конкурентная очередь из внешнего фреймворка? Ее авторы мне за это не заплатят.

Если же необходимо сделать нечто особенное, что она не учитывает (например хитрым способом соединить две очереди), то в своей прокладке кода я смогу поставить Breakpoint-ы и проверить это взаимодействие.
В том то и дело, что могу. Описанный в статье инструмент преобразует понятие «одновременно» в «последовательно». С тем порядком который необходим, для подтверждения того что система реально блокирует возможность обратиться к разделяемому ресурсу одновременно.

По поводу «существующую конкурентую коллекцию». Любая абстракция дырявая по определению, то есть в любой момент можно столкнуться с задачей которая не решается существующими средствами, или существуют особенности делающие их не применимыми в конкретном случае. Например, какая-нибудь хитрая приоретезация.
Допустим, пара потоков пишет объекты в список, а пара считывает и обрабатывает их.

Можно разбить логику тестирования на тесты:
1. Если была запись, то проходит и чтение.
2. Если записи не было, то при чтении метод залипает (например).
3. Возможность записывать двумя потоками.
4. Возможность читать двумя потоками.
5. Возможность читать и писать одновременно.

Первые два теста возможно сделать абсолютно синхронными, три последних нет (в асинхронности весь смысл).

Можно поставить какие-нибудь объекты синхронизации не глядя в код, но где гарантия что они будут делать реально то (и только то) что действительно нужно.
К сожелению лучшего блога для этого я не знаю: тестирование — нет, разработка — нет, C# — нет. А этот блог объединяет TDD и автоматическое тестирование.
Оно так и получается. Строчки Breakpoint.Define("...") синхронизируют проверяемые фрагменты до такой степени, что они начинают работать почти синхронно (почти верно: в один момент времени, один работающий поток). На мой взгляд в таком контексте — это не противоречит «Хамблу и Фарли».

По поводу DEBUG. Справедливо. Но на мой взгляд предназначение DEBUG режима — отладка системы. Одна из ролей автоматических тестов — это отладка.

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

За студентов +1 — большинство из низ путают перегрузку с полиморфизмом (несколько раз собеседовал). А вы о виртуальных диструкторах, ха. Последний раз запутал респондента с ToString (мой основной язык C#).

По поводу «красивые архитектуры». Не буду кидаться именами. Мне кажется ООП нужен исключительно для того, что бы читатель кода мог максимально быстро понять задумку лежащую в основе системы в целом и фрагмента в частности; максимально быстро начал развивать вверенную ему систему (или ее часть). А архитектура, ради архитектуры — это конечно издержки молодости профессии.
У вас наверное очень-очень-(...) хорошая память.
По поводу использовать возможно, а сопровождать — врядли. Такой код обладает по крайней мере одной положительной харрактиристикой — его можно безопастно отрефакторить.
Два слоя тестов — идея интересная. Надо будет попробовать. Сейчас я обычно строю только внешний слой — что бы излишне не блокировать рефакторинг.

Если я вас правильно понял внешнее кольцо может определяться только классическими тестами (по классификации Фаулера martinfowler.com/articles/mocksArentStubs.html). А внутреннее — как получится?
Еще немного и останутся одни дифирамбы :)

А если серьезно. Не думайте, что я утверждаю, что там ничего нет. Я всеми силами пытаюсь осознать ту самую изюминку… Но пока разницы не вижу.

Перечитал ваши комментарии. Что вы имеете ввиду говоря: тестирование поведения, а не состояния? Возможно изюминка здесь.

На мой взгляд мы всегда тестируем состояние. Мы проверяем конкретные характеристики сущностей. Поведение же — это дельта между исходным состояние (которые мы знаем априори т.к. задаем его руками) и изменившимся (проверяемым в конце теста).
Абсолютно с вами согласен.

1. Переименовали Assert в Should.
2. Один тестовый метод — один Assert (Should). Это просто именование отдельно каждой проверки.

И все. Все остальное дифирамбы.

Information

Rating
Does not participate
Registered
Activity