def test_post_channel_does_not_exist(self, mock_weather_file_dir, mock_urls_file_dir, mock_now): """ 【正常系】『お天気エリア選択』画面 「天気予報を取得」ボタンを押下時処理。 選択したエリアのチャンネルが存在しない場合は、スクレイピングは行わず、週間天気予報画面を表示する。 """ mock_now.return_value = datetime(2017, 8, 7, 11, 00, 00, 000000) area1 = testing.factory_area(name='エリア1') area2 = testing.factory_area(name='エリア2') channel_weekly = testing.factory_channel(area=area2) channel_daily = testing.factory_channel( area=area2, weather_type=Channel.TYPE_DAILY) # URLファイルとお天気情報ファイル出力先を、一時ディレクトリにモックする with tempfile.TemporaryDirectory() as urls_dirpath, \ tempfile.TemporaryDirectory() as weather_dirpath: mock_urls_file_dir.return_value = urls_dirpath mock_weather_file_dir.return_value = weather_dirpath res = self.client.post(reverse('weather:select_area'), data={ 'selected_area': area1.id, 'scrapy_weather': '天気予報を取得', }) self.assertRedirects( res, reverse('weather:weekly', kwargs={'area_id': area1.id})) # URLファイルが出力されていないことを確認 w_urls_file_path = scrapyutils._get_urls_file_path( 'yahoo_weekly_weather') d_urls_file_path = scrapyutils._get_urls_file_path( 'yahoo_daily_weather') self.assertFalse(os.path.isfile(w_urls_file_path)) self.assertFalse(os.path.isfile(d_urls_file_path)) # お天気情報ファイルが出力されていないことを確認 w_weather_file_path = scrapyutils._get_weather_file_path( 'yahoo_weekly_weather_20170807110000000000') d_weather_file_path = scrapyutils._get_weather_file_path( 'yahoo_daily_weather_20170807110000000000') self.assertFalse(os.path.isfile(w_weather_file_path)) self.assertFalse(os.path.isfile(d_weather_file_path)) # DB登録されないことを確認 self.assertEqual(Weather.objects.count(), 0) self.assertEqual(HourlyWeather.objects.count(), 0) self.assertRedirects( res, reverse('weather:weekly', kwargs={'area_id': area1.id}))
def test_post_ValidationError(self): """ 【異常系】『チャンネル変更』画面 ValidationErrorの場合、チャンネルを変更せずエラーメッセージを表示する。 """ # データの準備 area = testing.factory_area() channel = testing.factory_channel(area=area, ) data = {'url': ''} # 対象機能の実行 res = self.client.post(reverse('channel:update', args=(channel.id, )), data=data) # 実行結果の確認 self.assertTemplateUsed(res, 'channel/edit.html') self.assertFalse(res.context['form'].is_valid()) self.assertEqual(res.context['form'].instance, channel) self.assertEqual(res.context['form']['url'].data, '') self.assertEqual(res.context['form'].errors['url'], ['このフィールドは必須です。']) # DB変更されてないよね確認。 self.assertEqual(Channel.objects.count(), 1) channel.refresh_from_db() self.assertEqual(channel.area, area) self.assertEqual(channel.name, Channel.CHANNEL_YAHOO) self.assertEqual(channel.weather_type, Channel.TYPE_WEEKLY) self.assertEqual( channel.url, 'https://weather.fumi.co.jp/weather/jp/11/2222/33333.html')
def test_post_confirmed_ValidationError(self): """ 【異常系】『チャンネル登録確認』画面 バリデーションエラーの場合は、チャンネル登録画面を再表示する。 """ # データの準備 area = testing.factory_area() input_weekly_url = 'https://aaa.jp/input_weekly_url=c&q=茨城県守谷市' input_daily_url = '' res = self.client.post(reverse('channel:register'), data={ 'area': area.id, 'channel': Channel.CHANNEL_TENKIJP, 'weather_type_weekly_url': input_weekly_url, 'weather_type_daily_url': input_daily_url, 'confirmed': '1', 'register': '登録する', }) # 実行結果の確認 self.assertTemplateUsed(res, 'channel/register.html') self.assertFalse(res.context['form'].is_valid()) self.assertEqual(res.context['form'].errors['weather_type_daily_url'], ['このフィールドは必須です。']) self.assertEqual(res.context['form']['area'].data, str(area.id)) self.assertEqual(res.context['form']['channel'].data, str(Channel.CHANNEL_TENKIJP)) self.assertEqual(res.context['form']['weather_type_weekly_url'].data, input_weekly_url) self.assertEqual(res.context['form']['weather_type_daily_url'].data, input_daily_url) self.assertEqual(Channel.objects.count(), 0)
def test_weather_does_not_exist(self): """ 【正常系】『週間天気予報』画面 選択したエリアの週間天気予報(Weather)取得結果が0件の場合 """ area = testing.factory_area() channel_weekly = testing.factory_channel(area=area) channel_daily = testing.factory_channel( area=area, weather_type=Channel.TYPE_DAILY) res = self.client.get(reverse('weather:weekly', args=(area.id, ))) self.assertTemplateUsed(res, 'weather/weekly.html') self.assertEqual(res.context['area'], area) self.assertContains(res, '草津町の週間天気予報') all_weekly_weather = res.context['all_weekly_weather'] self.assertEqual(len(all_weekly_weather), 1) self.assertTrue( channel_weekly.get_name_display() in all_weekly_weather) weathers = all_weekly_weather[channel_weekly.get_name_display()] self.assertEqual(len(weathers), 0) # h2タイトルと表組は表示する。 self.assertContains(res, 'Yahoo!天気') self.assertContains(res, '日付') # 週間天気予報(Weather)が存在しない場合は、天気予報がない旨を表示する。 self.assertContains(res, '天気予報ないよ')
def test_post_confirmed_back(self): """ 【正常系】『チャンネル登録確認』画面 戻るボタンを押下すると入力画面へ戻る。 """ area = testing.factory_area() input_weekly_url = "https://aaa.jp/input_weekly_url=c&q=茨城県守谷市" input_daily_url = 'https://aaa.jp/input_daily_url=c&q=茨城県守谷市' data = { 'area': area.id, 'channel': Channel.CHANNEL_TENKIJP, 'weather_type_weekly_url': input_weekly_url, 'weather_type_daily_url': input_daily_url, 'confirmed': '1', 'back': '戻る', } # 対象機能の実行 res = self.client.post(reverse('channel:register'), data=data) # 実行結果の確認 self.assertTemplateUsed(res, 'channel/register.html') self.assertEqual(Channel.objects.count(), 0) self.assertEqual(res.context['form']['area'].data, str(area.id)) self.assertEqual(res.context['form']['channel'].data, str(Channel.CHANNEL_TENKIJP)) self.assertEqual(res.context['form']['weather_type_weekly_url'].data, input_weekly_url) self.assertEqual(res.context['form']['weather_type_daily_url'].data, input_daily_url)
def test_post(self): """ 【正常系】『チャンネル変更』画面 POSTの場合、チャンネルを更新する。 """ # データの準備 area = testing.factory_area() channel = testing.factory_channel(area=area, ) input_weekly_url = 'https://aaa.jp/input_weekly_url=c&q=茨城県守谷市' data = {'url': input_weekly_url} # 対象機能の実行 res = self.client.post(reverse('channel:update', args=(channel.id, )), data=data) # 実行結果の確認 self.assertRedirects(res, reverse('channel:list')) self.assertEqual(Channel.objects.count(), 1) channel.refresh_from_db() self.assertEqual(channel.area, area) self.assertEqual(channel.name, Channel.CHANNEL_YAHOO) self.assertEqual(channel.weather_type, Channel.TYPE_WEEKLY) self.assertEqual(channel.url, input_weekly_url) messages = list(res.wsgi_request._messages) self.assertEqual(len(messages), 1) self.assertEqual(str(messages[0]), 'チャンネル「草津町 * Yahoo!天気 * 週間天気」を変更しました。')
def test_get(self): """ 【正常系】『お天気エリア選択』画面 お天気エリア選択画面を初期表示する。 """ area = testing.factory_area() res = self.client.get(reverse('weather:select_area')) self.assertTemplateUsed(res, 'weather/select_area.html') self.assertIn('form', res.context)
def test_get_nonexistence(self): """ 【異常系】『チャンネル一覧』画面 削除対象のチャンネルが存在しない場合は404エラー。 """ # データの準備 area = testing.factory_area() # 対象機能の実行 res = self.client.get(reverse('channel:delete', args=(999, ))) # 実行結果の確認 self.assertEqual(res.status_code, 404)
def test_get(self): """ 【正常系】『チャンネル変更』画面 チャンネル変更画面を初期表示する。 """ # データの準備 area = testing.factory_area() channel = testing.factory_channel(area=area) # 対象機能の実行 res = self.client.get(reverse('channel:update', args=(channel.id, ))) # 実行結果の確認 self.assertTemplateUsed(res, 'channel/edit.html') self.assertEqual(res.context['form'].instance, channel)
def test_get(self, m): """ 【正常系】『週間天気予報』画面 選択したエリアの、週間天気予報を表示する。 """ m.return_value = date(2017, 8, 11) area = testing.factory_area() channel_weekly = testing.factory_channel(area=area) channel_daily = testing.factory_channel( area=area, weather_type=Channel.TYPE_DAILY) weather1 = testing.factory_weather(channel=channel_weekly, date=date(2017, 8, 10)) weather2 = testing.factory_weather(channel=channel_weekly, date=date(2017, 8, 11)) weather3 = testing.factory_weather(channel=channel_weekly, date=date(2017, 8, 12)) hourlyweather = testing.factory_hourlyweather(channel=channel_daily, date=weather2) res = self.client.get(reverse('weather:weekly', args=(area.id, ))) self.assertTemplateUsed(res, 'weather/weekly.html') self.assertEqual(res.context['area'], area) self.assertContains(res, '草津町の週間天気予報') all_weekly_weather = res.context['all_weekly_weather'] self.assertEqual(len(all_weekly_weather), 1) self.assertTrue( channel_weekly.get_name_display() in all_weekly_weather) self.assertContains(res, 'Yahoo!天気') # 今日より前の天気予報は表示しない。 weathers = all_weekly_weather[channel_weekly.get_name_display()] self.assertEqual(len(weathers), 2) self.assertEqual(weathers[0], weather2) self.assertEqual(weathers[0].daily_weather_count, 1) self.assertEqual(weathers[1], weather3) self.assertEqual(weathers[1].daily_weather_count, 0) # hourlyweatherが存在する場合は、日付をリンク表示 self.assertContains(res, reverse('weather:daily', args=(weather2.id, ))) self.assertNotContains(res, reverse('weather:daily', args=(weather3.id, )))
def test_post_weekly_weather(self): """ 【正常系】『お天気エリア選択』画面 「週間天気予報を表示」ボタンを押下時処理。 選択したエリアの週間天気予報画面を表示する。 """ area = testing.factory_area(name='エリア1') channel_weekly = testing.factory_channel(area=area) channel_daily = testing.factory_channel( area=area, weather_type=Channel.TYPE_DAILY) res = self.client.post(reverse('weather:select_area'), data={ 'selected_area': area.id, 'display_weekly': '週間天気予報を表示', }) self.assertRedirects( res, reverse('weather:weekly', kwargs={'area_id': area.id}))
def test_post(self, mock_register, mock_scrapy, mock_url): """ 【正常系】『お天気エリア選択』画面 「天気予報を取得」ボタンを押下時処理。 選択した地域の天気予報をスクレイピング&DB登録し、週間天気予報を表示する。 """ area = testing.factory_area() channel_weekly = testing.factory_channel(area=area) channel_daily = testing.factory_channel( area=area, weather_type=Channel.TYPE_DAILY) res = self.client.post(reverse('weather:select_area'), data={ 'selected_area': area.id, 'scrapy_weather': '天気予報を取得', }) self.assertRedirects( res, reverse('weather:weekly', kwargs={'area_id': area.id}))
def test_post_confirmed(self): """ 【正常系】『チャンネル登録確認』画面 確認画面からのPOSTの場合、チャンネルを新規登録する。 * 既存のチャンネルと重複しないチャンネルを登録する場合。 """ # データの準備 area = testing.factory_area() input_weekly_url = 'https://aaa.jp/input_weekly_url=c&q=茨城県守谷市' input_daily_url = 'https://aaa.jp/input_daily_url=c&q=茨城県守谷市' # 対象機能の実行 res = self.client.post(reverse('channel:register'), data={ 'area': area.id, 'channel': Channel.CHANNEL_TENKIJP, 'weather_type_weekly_url': input_weekly_url, 'weather_type_daily_url': input_daily_url, 'confirmed': '1', 'register': '登録する', }) # 実行結果の確認 self.assertRedirects(res, reverse('channel:list')) self.assertEqual(Channel.objects.count(), 2) channels = Channel.objects.select_related('area').order_by('id', ) self.assertEqual(channels[0].area, area) self.assertEqual(channels[0].name, Channel.CHANNEL_TENKIJP) self.assertEqual(channels[0].weather_type, Channel.TYPE_WEEKLY) self.assertEqual(channels[0].url, input_weekly_url) self.assertEqual(channels[1].area, area) self.assertEqual(channels[1].name, Channel.CHANNEL_TENKIJP) self.assertEqual(channels[1].weather_type, Channel.TYPE_DAILY) self.assertEqual(channels[1].url, input_daily_url) messages = list(res.wsgi_request._messages) self.assertEqual(len(messages), 1) self.assertEqual(str(messages[0]), 'チャンネル「草津町 * 日本気象協会 tenki.jp」を登録しました。')
def test_post_validationError(self): """ 【正常系】『お天気エリア選択』画面 バリデーションエラー時は、お天気エリア選択画面に戻る。 """ area1 = testing.factory_area(name='エリア1') channel_weekly = testing.factory_channel(area=area1) channel_daily = testing.factory_channel( area=area1, weather_type=Channel.TYPE_DAILY) res = self.client.post(reverse('weather:select_area'), data={ 'selected_area': '', 'display_weekly': '週間天気予報を表示', }) self.assertTemplateUsed(res, 'weather/select_area.html') self.assertFalse(res.context['form'].is_valid()) self.assertEqual(res.context['form']['selected_area'].data, '') self.assertEqual(res.context['form'].errors['selected_area'], ['このフィールドは必須です。'])
def test_post(self): """ 【正常系】『今日の天気予報』画面 選択した日付の、今日の天気予報(時間ごと)を表示する。 """ area = testing.factory_area() channel_weekly = testing.factory_channel(area=area) channel_daily = testing.factory_channel( area=area, weather_type=Channel.TYPE_DAILY) weather11 = testing.factory_weather(channel=channel_weekly, date=date(2017, 8, 11)) hourlyweather00 = testing.factory_hourlyweather(channel=channel_daily, date=weather11, time='00:00:00') hourlyweather12 = testing.factory_hourlyweather(channel=channel_daily, date=weather11, time='12:00:00') hourlyweather21 = testing.factory_hourlyweather(channel=channel_daily, date=weather11, time='21:00:00') weather12 = testing.factory_weather(channel=channel_weekly, date=date(2017, 8, 12)) hourlyweather09 = testing.factory_hourlyweather(channel=channel_daily, date=weather12, time='09:00:00') res = self.client.post(reverse('weather:daily', args=(weather11.id, ))) self.assertTemplateUsed(res, 'weather/daily.html') self.assertEqual(res.context['weather'], weather11) # H1タイトル self.assertContains(res, '草津町 08/11(金)の天気予報') # H2タイトル self.assertContains(res, 'Yahoo!天気') daily_weather = res.context['daily_weather'] self.assertEqual(len(daily_weather), 3) self.assertEqual(daily_weather[0], hourlyweather00) self.assertEqual(daily_weather[1], hourlyweather12) self.assertEqual(daily_weather[2], hourlyweather21)
def test_post(self, m): """ 【正常系】『週間天気予報』画面 選択したエリアの、週間天気予報を表示する。 """ m.return_value = date(2017, 8, 11) # 同一エリアに、'Yahoo!天気'と'日本気象協会 tenki.jp'のWeatherが存在する場合。 area = testing.factory_area() channel_yahoo = testing.factory_channel(area=area) channel_tenkijp = testing.factory_channel(area=area, name=Channel.CHANNEL_TENKIJP) weather_yahoo = testing.factory_weather(channel=channel_yahoo, date=date(2017, 8, 11)) weather_tenkijp = testing.factory_weather(channel=channel_tenkijp, date=date(2017, 8, 12)) res = self.client.get(reverse('weather:weekly', args=(area.id, ))) self.assertTemplateUsed(res, 'weather/weekly.html') self.assertEqual(res.context['area'], area) self.assertContains(res, '草津町の週間天気予報') all_weekly_weather = res.context['all_weekly_weather'] self.assertEqual(len(all_weekly_weather), 2) self.assertTrue(channel_yahoo.get_name_display() in all_weekly_weather) self.assertTrue( channel_tenkijp.get_name_display() in all_weekly_weather) self.assertContains(res, 'Yahoo!天気') self.assertContains(res, '日本気象協会 tenki.jp') yahoo_weathers = all_weekly_weather[channel_yahoo.get_name_display()] self.assertEqual(len(yahoo_weathers), 1) self.assertEqual(yahoo_weathers[0], weather_yahoo) tenkijp_weathers = all_weekly_weather[ channel_tenkijp.get_name_display()] self.assertEqual(len(tenkijp_weathers), 1) self.assertEqual(tenkijp_weathers[0], weather_tenkijp)
def test_get(self): """ 【正常系】『チャンネル一覧』画面 チャンネルを削除する。 """ # データの準備 area = testing.factory_area() channel_weekly = testing.factory_channel(area=area, ) channel_daily = testing.factory_channel( area=area, weather_type=Channel.TYPE_DAILY) # 対象機能の実行 res = self.client.get( reverse('channel:delete', args=(channel_weekly.id, ))) # 実行結果の確認 self.assertRedirects(res, reverse('channel:list')) self.assertEqual(Channel.objects.count(), 0) messages = list(res.wsgi_request._messages) self.assertEqual(len(messages), 1) self.assertEqual(str(messages[0]), 'チャンネル「草津町 * Yahoo!天気」を削除しました。')
def test_post_duplicate_error(self): """ 【異常系】『チャンネル登録確認』画面 既存のチャンネルと重複するチャンネルは登録できない。 """ # データの準備 area = testing.factory_area() channel = testing.factory_channel(area=area, name=Channel.CHANNEL_TENKIJP, weather_type=Channel.TYPE_WEEKLY, url='https://weathernews.jp.test') input_weekly_url = 'https://aaa.jp/input_weekly_url=c&q=茨城県守谷市' input_daily_url = 'https://aaa.jp/input_daily_url=c&q=茨城県守谷市' # 対象機能の実行 res = self.client.post(reverse('channel:register'), data={ 'area': area.id, 'channel': Channel.CHANNEL_TENKIJP, 'weather_type_weekly_url': input_weekly_url, 'weather_type_daily_url': input_daily_url, 'confirmed': '1', 'register': '登録する', }) # 実行結果の確認 self.assertTemplateUsed(res, 'channel/register.html') self.assertTrue(res.context['form'].is_valid()) msgs = list(res.context['messages']) self.assertEqual(len(msgs), 1) self.assertEqual(str(msgs[0]), 'チャンネル「草津町 * 日本気象協会 tenki.jp」はすでに登録されています。') self.assertEqual(Channel.objects.count(), 1) channels = Channel.objects.select_related('area').order_by('id', ) self.assertEqual(channels[0].area, area) self.assertEqual(channels[0].name, Channel.CHANNEL_TENKIJP) self.assertEqual(channels[0].weather_type, Channel.TYPE_WEEKLY) self.assertEqual(channels[0].url, 'https://weathernews.jp.test')
def test_post(self): """ 【正常系】『チャンネル登録』画面 入力画面からのPOSTの場合、確認画面を表示する。 """ area = testing.factory_area() input_weekly_url = "https://aaa.jp/input_weekly_url=c&q=茨城県守谷市" input_daily_url = 'https://aaa.jp/input_daily_url=c&q=茨城県守谷市' data = { 'area': area.id, 'channel': Channel.CHANNEL_TENKIJP, 'weather_type_weekly_url': input_weekly_url, 'weather_type_daily_url': input_daily_url, } # 対象機能の実行 res = self.client.post(reverse('channel:register'), data=data) # 実行結果の確認 self.assertTemplateUsed(res, 'channel/register_confirm.html') self.assertEqual(res.context['form']['area'].data, str(area.id)) self.assertEqual(res.context['form']['channel'].data, str(Channel.CHANNEL_TENKIJP)) self.assertEqual(res.context['form']['weather_type_weekly_url'].data, input_weekly_url) self.assertEqual(res.context['form']['weather_type_daily_url'].data, input_daily_url) self.assertEqual(res.context['modified']['area'], area) self.assertEqual(res.context['modified']['channel'], str(Channel.CHANNEL_TENKIJP)) self.assertEqual(res.context['modified']['weather_type_weekly_url'], input_weekly_url) self.assertEqual(res.context['modified']['weather_type_daily_url'], input_daily_url) self.assertEqual(res.context['channel_display'], '日本気象協会 tenki.jp')
def test_channel_does_not_exist(self): """ 【正常系】『週間天気予報』画面 選択したエリアのチャンネルが存在しない場合は週間天気予報画面にチャンネル登録を促すメッセージを表示。 """ area = testing.factory_area() res = self.client.get(reverse('weather:weekly', args=(area.id, ))) self.assertTemplateUsed(res, 'weather/weekly.html') self.assertEqual(res.context['area'], area) self.assertContains(res, '草津町の週間天気予報') all_weekly_weather = res.context['all_weekly_weather'] self.assertEqual(all_weekly_weather, {}) # チャンネルが存在しない場合は、表組を表示しない。 self.assertNotContains(res, '日付') # チャンネル登録を促すメッセージとチャンネル登録ボタンを表示する。 msg = list(res.context['messages']) self.assertEqual(len(msg), 1) self.assertContains(res, 'チャンネルが登録されていません。') self.assertContains(res, 'チャンネルを新規登録')
def test_post_output_target_urls_to_csv_OSError(self, mock_register, mock_scrapy, mock_urls_file_dir): """ 【異常系】『お天気エリア選択』画面 天気予報取得対象URLのCSV出力処理呼び出しでOSError発生時は、500エラー。 """ area = testing.factory_area() channel_weekly = testing.factory_channel(area=area) channel_daily = testing.factory_channel( area=area, weather_type=Channel.TYPE_DAILY) # URLファイル出力先を、一時ディレクトリにモックする with tempfile.TemporaryDirectory() as dirpath: mock_urls_file_dir.return_value = dirpath with self.assertRaises(OSError): res = self.client.post(reverse('weather:select_area'), data={ 'selected_area': area.id, 'scrapy_weather': '天気予報を取得', }) self.assertTemplateUsed(res, '500.html')