diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000000000000000000000000000000000000..06f7dd167397da9f48c4fa3ff96cc67e91a91571
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,7 @@
+require "rake/testtask"
+Rake::TestTask.new do |t|
+  t.libs << "test"
+  t.test_files = Dir["test/lib/**/*.rb"]
+end
+
+task :default => :test
diff --git a/test/helper.rb b/test/helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8d1b4fc4c8ffac45d374b6da5647e679d686eb85
--- /dev/null
+++ b/test/helper.rb
@@ -0,0 +1,20 @@
+if ENV["SIMPLECOV"]
+  begin
+    require 'simplecov'
+    SimpleCov.start
+  rescue LoadError
+  end
+end
+
+unless Object.const_defined? 'Cinch'
+  $:.unshift File.expand_path('../../lib', __FILE__)
+  require 'cinch'
+end
+
+require 'minitest/autorun'
+
+class TestCase < MiniTest::Unit::TestCase
+  def self.test(name, &block)
+    define_method("test_" + name, &block) if block
+  end
+end
diff --git a/test/lib/cinch/mask.rb b/test/lib/cinch/mask.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e918926dff775be19f17a18037c034a098083f0f
--- /dev/null
+++ b/test/lib/cinch/mask.rb
@@ -0,0 +1,67 @@
+require "helper"
+
+class MaskTest < TestCase
+  DefaultMask = "foo*!bar?@baz"
+  def setup
+    @mask = Cinch::Mask.new(DefaultMask.dup)
+  end
+  test "Two equal masks should be equal" do
+    mask2 = Cinch::Mask.new(DefaultMask.dup)
+
+    assert @mask == mask2
+    assert @mask.eql?(mask2)
+  end
+
+  test "A Mask's hash should depend on the mask" do
+    mask2 = Cinch::Mask.new(DefaultMask.dup)
+
+    assert_equal @mask.hash, mask2.hash
+  end
+
+  test "A Mask should match a User only if it has matching attributes" do
+    user = Cinch::User.new("foo", nil)
+    user2 = Cinch::User.new("foobar", nil)
+    user3 = Cinch::User.new("barfoo", nil)
+
+    # bar? -> bar, baz -> baz
+    user.end_of_whois(user: "bar", host: "baz")
+    assert @mask.match(user)
+
+    # bar? -> bar2, baz -> baz
+    user.end_of_whois(user: "bar2", host: "baz")
+    assert @mask.match(user)
+
+    # bar? !-> bar22, baz -> baz
+    user.end_of_whois(user: "bar22", host: "baz")
+    assert !@mask.match(user)
+
+    # bar? -> bar, baz !-> meow
+    user.end_of_whois(user: "bar", host: "meow")
+    assert !@mask.match(user)
+
+    # foo* -> foobar
+    user2.end_of_whois(user: "bar", host: "baz")
+    assert @mask.match(user2)
+
+    # foo* !-> barfoo
+    user3.end_of_whois(user: "bar", host: "baz")
+    assert !@mask.match(user3)
+  end
+
+  test "A mask's string representation should equal the original mask string" do
+    assert_equal DefaultMask.dup, @mask.to_s
+  end
+
+  test "A Mask can be created from Strings" do
+    assert_equal @mask, Cinch::Mask.from(DefaultMask.dup)
+  end
+
+  test "A Mask can be created from objects that have a mask" do
+    mask = Cinch::Mask.new("foo!bar@baz")
+    user = Cinch::User.new("foo", nil)
+    user.end_of_whois(user: "bar", host: "baz")
+    new_mask = Cinch::Mask.from(user)
+
+    assert_equal mask, new_mask
+  end
+end
diff --git a/test/lib/cinch/plugin.rb b/test/lib/cinch/plugin.rb
new file mode 100644
index 0000000000000000000000000000000000000000..462435ccfc182ce5dbebaf3ba591614502c35e3c
--- /dev/null
+++ b/test/lib/cinch/plugin.rb
@@ -0,0 +1,170 @@
+require "helper"
+
+module Cinch
+  class CinchTestPluginWithoutName
+    include Cinch::Plugin
+  end
+end
+
+class PluginTest < TestCase
+  def setup
+    @bot = Cinch::Bot.new {
+      self.loggers.clear
+    }
+    @plugin = Class.new { include Cinch::Plugin }
+    @bot.config.plugins.options = {@plugin => {:key => :value}}
+
+    @plugin.plugin_name = "testplugin"
+    @plugin_instance = @plugin.new(@bot)
+  end
+
+  test "should be able to specify matchers" do
+    @plugin.match(/pattern/)
+    matcher = @plugin.matchers.last
+
+    assert_equal(1, @plugin.matchers.size, "Shoult not forget existing matchers")
+    assert_equal Cinch::Plugin::ClassMethods::Matcher.new(/pattern/, true, true, :execute), matcher
+
+    matcher = @plugin.match(/pattern/, use_prefix: false, use_suffix: false, method: :some_method)
+    assert_equal Cinch::Plugin::ClassMethods::Matcher.new(/pattern/, false, false, :some_method), matcher
+  end
+
+  test "should be able to listen to events" do
+    @plugin.listen_to(:event1, :event2)
+    @plugin.listen_to(:event3, method: :some_method)
+
+    listeners = @plugin.listeners
+    assert_equal 3, listeners.size
+    assert_equal [:event1, :event2, :event3], listeners.map(&:event)
+    assert_equal [:listen, :listen, :some_method], listeners.map(&:method)
+  end
+
+  test "should be able to create CTCP commands" do
+    @plugin.ctcp("FOO")
+    @plugin.ctcp("BAR")
+
+    assert_equal 2, @plugin.ctcps.size
+    assert_equal ["FOO", "BAR"], @plugin.ctcps
+  end
+
+  test "CTCP commands should always be uppercase" do
+    @plugin.ctcp("foo")
+    assert_equal "FOO", @plugin.ctcps.last
+  end
+
+  test "should return an empty array of timers" do
+    assert_equal [], @plugin.timers
+  end
+
+  test "should return an empty array of listeners" do
+    assert_equal [], @plugin.listeners
+  end
+
+  test "should return an empty array of CTCPs" do
+    assert_equal [], @plugin.ctcps
+  end
+
+  test "should be able to set timers" do
+    @plugin.timer(1, method: :foo)
+    @plugin.timer(2, method: :bar, :threaded => false)
+
+    timers = @plugin.timers
+    assert_equal 2, timers.size
+    assert_equal [1, 2], timers.map(&:interval)
+    assert_equal [:foo, :bar], timers.map {|t| t.options[:method]}
+    assert_equal [true, false], timers.map {|t| t.options[:threaded]}
+  end
+
+  test "should be able to register hooks" do
+    @plugin.hook(:pre)
+    @plugin.hook(:post, :for => [:match])
+    @plugin.hook(:post, :method => :some_method)
+
+    hooks = @plugin.hooks.values.flatten
+    assert_equal [:pre, :post, :post], hooks.map(&:type)
+    assert_equal [:match], hooks[1].for
+    assert_equal :some_method, hooks.last.method
+    assert_equal :hook, hooks.first.method
+  end
+
+  test "should have access to plugin configuration" do
+    assert_equal :value, @plugin_instance.config[:key]
+  end
+
+  test "should be able to set a prefix with a block" do
+    block = lambda {|m| "^"}
+    @plugin.prefix = block
+    assert_equal block, @plugin.prefix
+  end
+
+  test "should be able to set a suffix with a block" do
+    block = lambda {|m| "^"}
+    @plugin.suffix = block
+    assert_equal block, @plugin.suffix
+  end
+
+  test "should support `set(key, value)`" do
+    @plugin.set :help,        "some help message"
+    @plugin.set :prefix,      "some prefix"
+    @plugin.set :suffix,      "some suffix"
+    @plugin.set :plugin_name, "some plugin"
+    @plugin.set :reacting_on,    :event1
+
+    assert_equal "some help message", @plugin.help
+    assert_equal "some prefix",       @plugin.prefix
+    assert_equal "some suffix",       @plugin.suffix
+    assert_equal "some plugin",       @plugin.plugin_name
+    assert_equal :event1,             @plugin.reacting_on
+  end
+
+  test "should support `set(key => value, key => value, ...)`" do
+    @plugin.set(:help        => "some help message",
+                :prefix      => "some prefix",
+                :suffix      => "some suffix",
+                :plugin_name => "some plugin",
+                :reacting_on    => :event1)
+
+    assert_equal "some help message", @plugin.help
+    assert_equal "some prefix",       @plugin.prefix
+    assert_equal "some suffix",       @plugin.suffix
+    assert_equal "some plugin",       @plugin.plugin_name
+    assert_equal :event1,             @plugin.reacting_on
+  end
+
+  test "should support `self.key = value`" do
+    @plugin.help        = "some help message"
+    @plugin.prefix      = "some prefix"
+    @plugin.suffix      = "some suffix"
+    @plugin.plugin_name = "some plugin"
+    @plugin.reacting_on    = :event1
+
+    assert_equal "some help message", @plugin.help
+    assert_equal "some prefix",       @plugin.prefix
+    assert_equal "some suffix",       @plugin.suffix
+    assert_equal "some plugin",       @plugin.plugin_name
+    assert_equal :event1,             @plugin.reacting_on
+  end
+
+  test "should support querying attributes" do
+    @plugin.plugin_name = "foo"
+    @plugin.help = "I am a help message"
+    @plugin.prefix = "^"
+    @plugin.suffix = "!"
+    @plugin.react_on(:event1)
+
+    assert_equal "foo", @plugin.plugin_name
+    assert_equal "I am a help message", @plugin.help
+    assert_equal "^", @plugin.prefix
+    assert_equal "!", @plugin.suffix
+    assert_equal :event1, @plugin.reacting_on
+  end
+
+  test "should have a default name" do
+    assert_equal "cinchtestpluginwithoutname", Cinch::CinchTestPluginWithoutName.plugin_name
+  end
+
+  test "should check for the right number of arguments for `set`" do
+    assert_raises(ArgumentError) { @plugin.set() }
+    assert_raises(ArgumentError) { @plugin.set(1, 2, 3) }
+  end
+end