他人のブログを読んだり、勉強会等のLTを聴いて感じること

こんにちは。

他人のブログを読んでいたり、勉強会や某ABCに参加してLTとか聴いていて感じたことを書きます。

1. お前のブログはオリジナリティがあるのか?
既知の内容なのに、そういった内容をブログに書いている人をよく見かけます。参考サイトのリンクを張っていればまだ許せますが、そういったものがないブログもあります。また、本に載っていることを書いているブログも見かけます。例えば、最近見かけたのでは、iOSでのUITableViewの使い方とか。これはひどいと思いました。お前が考案したAPIなのか?w

2. お前のブログは、どこかのサイトのページの翻訳ではないか?
Androidに関して、キーワードで日本語サイトをググると、Googleのドキュメントリファレンスのページをほぼ翻訳したページを見かけることがあります。ブログを書いている本人は、日本人のために翻訳してあげているんだとか言いそうですが、ブログ中に出てくるサンプルコードも、元のドキュメントリファレンスのページと同じことが多々あります。真っ先に日本語に訳して、日本で最先端を行っているとでも思っているのでしょうか。技術的にとんがりたいなら、自分で作ったサンプルアプリのコードでも載せて欲しいですね。また、最近見かけたブログでは、英語圏のブログのほぼ翻訳で、コード断片もコピペで、元のコードの、作者独自定義の定数を、定義もせずに(定義を引用もせずに)使用している例がありました。元の英語圏のブログでは、githubにサンプルコードがあったので、定数の定義を見ることができましたが、翻訳サイトでは、githubへのリンクは張られていませんでした。

3. 勉強会や某ABCのLTで笑いを取ることしかしない
勉強会や某ABCに参加してLTを聞いていると、次のようなことがあります。

a. アニメの画像をスライドに入れる。
面白くないですし、あなたは、そのアニメの作者に、画像の使用許諾を取ったのですか?

b. 奇声を上げて笑いをとろうとする
勉強会では見かけないですが、某ABCのLTではよく見かけます。ちっとも面白くないです。ビジネス、技術アピールで注目を浴びるようにして下さい。

c. その他
某ABCのLTで、当時流行っていた某ゲームアプリを、某テストツールで動かすことをやったLTがありました。はっきり言って、技術的に面白くないです。自分のプロダクトをテストツールで動かすのならいいですが、そうでないなら、単に笑いを取るだけの内容ですね。

Rails3レシピブック”モックやスタブを使う”のコードを書き換えてみた

Rails3レシピブックの”モックやスタブを使う”のテストコードは、rspec-mocksのバージョンが古く、バージョン 2.14.6では書き換える必要があります。Rubyにおけるモック、スタブについては自分は初心者なので、試行錯誤して、書き換えてみました。最後のテストの部分は、本によると、失敗するはずなのですが、なぜか通ってしまいます。この点は理解不足なので、保留にします。コードは、GitHubにおいてあります。

https://github.com/andropenguin/samplecode-stub-mock

Rails 4.0.0、RSpec、Capybara使用プロジェクト向けGemfile

Ruby on Rails チュートリアルの、Ruby on Rails 4向けの記事を読んでいます。記事に書いてあるGemfileだと、依存関係がおかしく、buundle installでエラーになります。うまくいくGemfileを示します。

source ‘https://rubygems.org’
ruby ‘2.0.0’

gem ‘rails’, ‘4.0.0’
gem ‘bootstrap-sass’, ‘2.3.2.0’
gem ‘bcrypt-ruby’, ‘3.0.1’
gem ‘faker’, ‘1.1.2’
gem ‘will_paginate’, ‘3.0.4’
gem ‘bootstrap-will_paginate’, ‘0.0.9’

group :development, :test do
gem ‘sqlite3’, ‘1.3.7’
gem ‘rspec-rails’, ‘2.13.1’
# The following optional lines are part of the advanced setup.
# gem ‘guard-rspec’, ‘2.5.0’
# gem ‘spork-rails’, github: ‘sporkrb/spork-rails’
# gem ‘guard-spork’, ‘1.5.0’
# gem ‘childprocess’, ‘0.3.6’
end

group :test do
gem ‘selenium-webdriver’, ‘2.0.0’
gem ‘capybara’, ‘2.1.0’
gem ‘factory_girl_rails’, ‘4.2.0’
gem ‘cucumber-rails’, ‘1.3.0’, :require => false
gem ‘database_cleaner’, github: ‘bmabey/database_cleaner’

# Uncomment this line on OS X.
# gem ‘growl’, ‘1.0.3’

# Uncomment these lines on Linux.
# gem ‘libnotify’, ‘0.8.0’

# Uncomment these lines on Windows.
# gem ‘rb-notifu’, ‘0.0.4’
# gem ‘win32console’, ‘1.3.2’
end

#gem ‘sass-rails’, ‘4.0.2’
gem ‘sass-rails’, ‘4.0.0’
gem ‘railties’, ‘4.0.0’
gem ‘uglifier’, ‘2.1.1’
gem ‘coffee-rails’, ‘4.0.0’
gem ‘jquery-rails’, ‘2.2.1’
gem ‘turbolinks’, ‘1.1.1’
gem ‘jbuilder’, ‘1.0.2’

group :doc do
gem ‘sdoc’, ‘0.3.20’, require: false
end

group :production do
gem ‘pg’, ‘0.15.1’
gem ‘rails_12factor’, ‘0.0.2’
end

gem ‘protected_attributes’

gem ‘execjs’
gem ‘therubyracer’

基礎Ruby on Rails改訂新版のサンプルアプリでFakerを使ってみた

最近、Everyday Rails – RSpecによるRailsテスト入門という電子書籍を読んで、RSpecでテストコードを書くことに取り組んでいます。練習として、基礎Ruby on Rails改訂新板のサンプルアプリにRSpecでテストコードを書きました。Memberクラスは、次のようなバリデーションがあります。

class Member < ActiveRecord::Base
  include EmailAddressChecker

  attr_accessible :number, :name, :full_name, :email, :birthday, :gender, :administrator
  validates :number, presence: true,
            numericality:  { only_integer: true,
            greater_than: 0, less_than: 100, allow_blank: true },
            uniqueness: true
  validates :name, presence: true,
            format: { with: /\A&#91;A-Za-z&#93;\w*\z/, allow_blank: true,
                      message: :invalid_member_name },
            length:  { minimum:  2, maximum:  20, allow_blank: true },
            uniqueness: { case_sensitive: false }
  validates :full_name, length: { maximum:  20 }
  validate :check_email
  validates :gender, inclusion: { in: 0..1 }

  private
    def check_email
      if email.present?
        errors.add(:email, :invalid) unless well_formed_as_email_address(email)
      end
    end

    class << self
      def search(query)
        rel = order("number")
        if query.present?
          rel = rel.where("name LIKE ? OR full_name LIKE ?",
                        "%#{query}%", "%#{query}%")
        end
        rel
      end
    end
end
&#91;/ruby&#93;

FactoryGirl gemとFaker gemを使って、テストデータを生成しました。

&#91;ruby&#93;
require 'faker'

FactoryGirl.define do
  factory :member do
    sequence(:number) { |n| n unless n > 99 }
    name {
      name = Faker::Name.first_name.slice!(0, 19).capitalize
      name if name.length >= 2
    }
    full_name { Faker::Name.name.slice!(0, 19) }
    email {Faker::Internet.email }
    birthday "1981-12-01"
    gender { Faker::Number.number(1).to_i % 2 }
    administrator false

    factory :invalid_member do
      name nil
    end
  end
end

この場合、テストコードを書くと、英語環境ではテストは失敗しません。ところが、i18nで国際化して、日本語環境でテストすると、fakerは国際化に対応しているようで、nameが日本語になったり、emailが不正な表現になって失敗するようになります。バリデーションでは、nameは英字で始まり、英数字からならないとダメだからです。しかし、full_nameについては、日本語のデータが生成されるようにしたいです。fakerのサイトページを見ると、Faker::Config.locale でロケールをスイッチできるようです。そこで、次のファイルを使ってテストデータを生成するようにしました。

require 'faker'

FactoryGirl.define do
  factory :member do
    sequence(:number) { |n| n unless n > 99 }
    name {
      Faker::Config.locale = 'en-us'
      name = Faker::Name.first_name.slice!(0, 19).capitalize
      name if name.length >= 2
    }
    full_name {
      Faker::Config.locale = 'ja'
      Faker::Name.name.slice!(0, 19)
    }
    email {
      Faker::Config.locale = 'en-us'
      Faker::Internet.email
    }
    birthday "1981-12-01"
    gender { Faker::Number.number(1).to_i % 2 }
    administrator false

    factory :invalid_member do
      name nil
    end
  end
end

Faker::Config.locale でロケールをスイッチすると、その後は設定したロケールのままになると期待したのですが、そのようにはならないようで、各フィールドごとにロケールをスイッチしないとダメでした。このテストデータを使用すると、テストが通るようになりました。

Jenkins+maven+Sonarを動かすのに役立ったサイトリスト

ここ数日、IntelliJでAndroidアプリのテスト付きmavenプロジェクトを作り、Jenkinsでビルド、テスト、静的解析をする方法を研究していました。Jenkinsとmaven、Sonarを組み合わせる方法は、ググっても、古い情報が引っかかることが多く、苦労しました。以下に、役立ったサイトのリストをあげます。折を見て、方法を紹介する記事を書きたいと思います。

マーチン・ファウラー著リファクタリングの第1章のテストコードを作った

マーチン・ファウラー著のリファクタリングを読んでいるんだけど、第1章のサンプルコードに対するテストコードを作成したので公開する。
この本では、リファクタリングの前に、リファクタリング対象コードに対するテストコードを書けと書いてあるのに、ユニットテストの紹介は第4章で手短にしか説明していない。また、第1章のビデオレンタルシステムのサンプルコードのリファクタリングでは、テストコードが掲載されていなくて、テストコードなしでリファクタリングするのは怖いし、できない。そこで、第1章のサンプルコードに対するテストファイルを作成した。テスト項目をグループ分けしていなかったり、同じような記述をメソッド抽出していないけど、テストコードだからいいかと思う。テストコードはGitHubに上げたので、使いたい人がいたら、どうぞ。ただし、なるべくテスト対象コードを本の記述の通りに入力したつもりだが、ascii、html両フォーマットでのフッター行の期待する文字列が正しくなく、テストコードが動作しない場合があるかもしれない。その場合は、修正するので、Pull Requestを送って欲しい。なお、JUnitは4を使用し、Eclipseでmavenプロジェクトを作成し、サンプルコードを写経、テストコードを作成、テスト、リファクタリングを行った。pom.xmlファイルも同梱した。

https://github.com/andropenguin/testfiles_refactoring_chap01.git

リファクタリングRubyエディションの第1章のサンプルに対するテストコードは、以前、リファクタリングRubyエディションの第1章のテストコードを作ったで記事を書いた。

JUnit 4で、doubleのassertを行う

JUnit4でdoubleを返すメソッドのテストを行う場合、assertThat(is(actual, expected))では誤差でテスト失敗になることがあります。その場合、closeToを使います。closeToは、org.hamcrest.Matchersのクラスメソッドで、ライブラリを導入する必要があります。Eclipseで、mavenプロジェクトのPOMエディタでは、次のように設定をします。

POM設定画面

テストコードでは、

import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.Matchers.closeTo;
import static org.junit.Assert.*;

public class FooTest {
   ...
   @Test
   public void some_test() {
      ...
      assertThat(actual, is(closeTo(expected, delta)));
   }
}

と書きます。

JUnit実践入門を読んで:コード補足その2

渡辺修司さん著の「JUnit実践入門」を読んで、自分なりに考えたテストコードを書きます。

第二段
第20章 テストダブルの活用 20.3 外部システムに依存するテスト

本では、mockitoを利用していましたが、私は、ExternalResourceを使って、getInputメソッドで、ファイルのInputStreamを返す方法をとりました。

テストコード

package ch20.ex03;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.io.InputStream;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;

public class NeworkResourcesTest {

	@Rule
	public NetworkLoaderResource resource = new NetworkLoaderResource();
	
	@Test
	public void NetworkResourcesのloadはHello_Worldを返す() throws Exception {
		NetworkResources sut = new NetworkResources();
		sut.loader = new NetworkLoader() {
			@Override
			public InputStream getInput() {
				return new NetworkLoaderResource().getInput();
			}
		};
		assertThat(sut.load(), is("Hello World"));
	}

	
	class NetworkLoaderResource extends ExternalResource {
		NetworkLoader loader;
		
		@Override
		protected void before() throws Throwable {
			loader = new NetworkLoader();
		}
		
		public InputStream getInput() {
			InputStream in = getClass().getResourceAsStream("input.txt");	
			return in;
		}
	}
}

]

ここで、input.txtファイルは、内容が

Hello World

で、Eclipseのプロジェクトでは src/test/resources/ch20/ex03に配置されます。

JUnit実践入門を読んで:コード補足その1

渡辺修司さん著の「JUnit実践入門」を読んで、写経して、コードが本に載っていなかったり、ダウンロードしたサンプルに入っていなかったりしたので、自分なりに考えて補足しました。

第一段
第20章 テストダブルの活用 20.1 システムに依存するテスト

リスト20.3に対するテストコードがないので、下記に記します。テスト対象コードのクラス名はMonthlyCalendar2とします。テストコードでは、MonthlyCalendar2を継承する無名クラスで、geCalendarメソッドをオーバーライドします。

package ch20.ex01;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.util.Calendar;

import org.junit.Test;


public class MonthlyCalendar2Test {

	@Test
	public void 現在時刻が20120131の場合getRemainingDaysは0を返す() throws Exception {
		MonthlyCalendar2 sut
		= new MonthlyCalendar2() {
			@Override
			Calendar getCalendar() {
				return newCalendar(2012, 1, 31);
			}
		};
		assertThat(sut.getRemainingDays(), is(0));
	}

	@Test
	public void 現在時刻が20120130の場合getRemainingDaysは1を返す() throws Exception {
		MonthlyCalendar2 sut
		= new MonthlyCalendar2() {
			@Override
			Calendar getCalendar() {
				return newCalendar(2012, 1, 30);
			}
		};
		assertThat(sut.getRemainingDays(), is(1));
	}

	@Test
	public void 現在時刻が20120201の場合getRemainingDaysは28を返す() throws Exception {
		MonthlyCalendar2 sut
		= new MonthlyCalendar2() {
			@Override
			Calendar getCalendar() {
				return newCalendar(2012, 2, 1);
			}
		};
		assertThat(sut.getRemainingDays(), is(28));
	}

	static Calendar newCalendar(int yyyy, int mm, int dd) {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.YEAR, yyyy);
		cal.set(Calendar.MONTH, mm - 1);
		cal.set(Calendar.DATE, dd);
		cal.set(Calendar.HOUR, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal;
	}

}

リスト20.4のテストコードを作って、テストをしようとしましたが、リスト20.4のコードだと、私はテストコードが書けませんでした。修正したテスト対象コードは次のようになりました。

テスト対象コード

package ch20.ex01;

import java.util.Calendar;

interface SystemCalendar {
	Calendar getInstance();
}

public class MonthlyCalendar3 implements SystemCalendar {
	@Override
	public Calendar getInstance() {
		return Calendar.getInstance();
	}
}

そして、テストコードは、下記のようになりました。テストで制御できない処理を別のクラスに移譲するのですが、その移譲クラスでgetRemainingDaysメソッドを実装しました。

package ch20.ex01;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.util.Calendar;

import org.junit.Test;

public class MonthlyCalendar3Test {

	@Test
	public void 現在時刻が20120131の場合getRemainingDaysは0を返す() throws Exception {
				
		DelegateObjectExample sut = new DelegateObjectExample();
		sut.setDate(2012, 1, 31);
		assertThat(sut.getRemainingDays(), is(0));
	}
	
	@Test
	public void 現在時刻が20120130の場合getRemainingDaysは1を返す() throws Exception {
		DelegateObjectExample sut = new DelegateObjectExample();
		sut.setDate(2012, 1, 30);
		assertThat(sut.getRemainingDays(), is(1));
	}
	
	@Test
	public void 現在時刻が20120201の場合getRemainingDaysは28を返す() throws Exception {
		DelegateObjectExample sut = new DelegateObjectExample();
		sut.setDate(2012, 2, 1);
		assertThat(sut.getRemainingDays(), is(28));
	}
}

class DelegateObjectExample {
	SystemCalendar sysCal = new MonthlyCalendar3();
	Calendar cal;
			
	public void setDate(int yyyy, int mm, int dd) {
		cal = sysCal.getInstance();
		cal = newCalendar(yyyy, mm, dd);
	}
	
	public int getRemainingDays() {
        return cal.getActualMaximum(Calendar.DATE) - cal.get(Calendar.DATE);
    }
	
	static Calendar newCalendar(int yyyy, int mm, int dd) {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.YEAR, yyyy);
		cal.set(Calendar.MONTH, mm - 1);
		cal.set(Calendar.DATE, dd);
		cal.set(Calendar.HOUR, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal;
	}
}

リファクタリングRubyエディション 6.17のテストコード

今、リファクタリングRubyエディションを読んでいます。
テストコードが載っていないし、コードを補わないと動かなかったり、擬似コードで動かないコードが載っていたりして、ストレスマッハです。
6.17 動的メソッド定義(Dynamic Method Definition)のテストコードが書けたので、記事にします。

リファクタリング対象コードは、本では

	def failure
		self.state = :failure
	end

	def error
		self.state = :error		
	end

	def success
		self.state = :success
	end

となっていますが、stateはクラス変数で、アクセサを定義する必要があります。Ruby クラス変数のゲッタによると、クラス変数に対しては、attrなんとかでアクセサを定義することはできないそうなので、コードを書いて定義しました。リファクタリング対象コードは、次のようになります。

ファイル methodsample.rb

class MethodSample
	def state
		@@state
	end

	def state=(val)
		@@state = val
	end

	def failure
		self.state = :failure
	end

	def error
		self.state = :error		
	end

	def success
		self.state = :success
	end
end

これに対するテストコードは次のようになります。

ファイル test_methodsample.rb

require ‘test/unit’
require_relative ‘methodsample’

class TestMethodSample < Test::Unit::TestCase def setup @methodsample = MethodSample.new end def test_failure @methodsample.failure result = @methodsample.state expected_result = :failure assert_equal expected_result, result end def test_error @methodsample.error result = @methodsample.state expected_result = :error assert_equal expected_result, result end def test_success @methodsample.success result = @methodsample.state expected_result = :success assert_equal expected_result, result end end [/ruby] あと、この節のサンプル:def_eachを使って類似メソッドを定義するで、def_eachメソッドを定義するClassクラスは記述されているんですが、プロダクションコードでどう使うのか書かれていないので、補います。 ファイル methodsample.rb [ruby] require_relative 'class' class MethodSample def state @@state end def state=(val) @@state = val end def_each :failure, :error, :success do |method_name| self.state = method_name end end [/ruby] これは、Module#define_methodを抽象化する話を参考にさせていただきました。