Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More expectations #31

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 129 additions & 32 deletions Sources/Spectre/Expectation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ open class Expectation<T> : ExpectationType {
let line: Int
let function: String

open var to: Expectation<T> {
return self
}

init(file: String, line: Int, function: String, expression: @escaping () throws -> ValueType?) {
self.file = file
self.line = line
Expand All @@ -44,6 +40,46 @@ open class Expectation<T> : ExpectationType {
}
}

extension ExpectationType {
public var to: ExpectTo<Self> {
return ExpectTo(expectation: self)
}
}

public class ExpectTo<E: ExpectationType> {

public var expression: () throws -> E.ValueType? { return expectation.expression }
open let expectation: E

init(expectation: E) {
self.expectation = expectation
}

public func failure(_ reason: String) -> FailureType {
return expectation.failure(reason)
}

public var not: ExpectNotTo<E> {
return ExpectNotTo(expectation: self.expectation)
}

}

public class ExpectNotTo<E: ExpectationType> {

public var expression: () throws -> E.ValueType? { return expectation.expression }
open let expectation: E

init(expectation: E) {
self.expectation = expectation
}

public func failure(_ reason: String) -> FailureType {
return expectation.failure(reason)
}

}

public func expect<T>( _ expression: @autoclosure @escaping () throws -> T?, file: String = #file, line: Int = #line, function: String = #function) -> Expectation<T> {
return Expectation(file: file, line: line, function: function, expression: expression)
}
Expand Down Expand Up @@ -115,20 +151,58 @@ public func != <Key, Value: Equatable> (lhs: Expectation<[Key: Value]>, rhs: [Ke
}
}

// MARK: Comparable

public func > <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable {
let value = try lhs.expression()
guard value! > rhs else {
throw lhs.failure("\(String(describing: value)) is not more than \(rhs)")
}
}

public func >= <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable {
let value = try lhs.expression()
guard value! >= rhs else {
throw lhs.failure("\(String(describing: value)) is not more than or equal to \(rhs)")
}
}

public func < <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable {
let value = try lhs.expression()
guard value! < rhs else {
throw lhs.failure("\(String(describing: value)) is not less than \(rhs)")
}
}

public func <= <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable {
let value = try lhs.expression()
guard value! <= rhs else {
throw lhs.failure("\(String(describing: value)) is not less than or equal to \(rhs)")
}
}

// MARK: Nil

extension ExpectationType {
extension ExpectTo {
public func beNil() throws {
let value = try expression()
if value != nil {
throw failure("value is not nil")
}
}
}
extension ExpectNotTo {
public func beNil() throws {
let value = try expression()
if value == nil {
throw failure("value is nil")
}
}
}

// MARK: Boolean

extension ExpectationType where ValueType == Bool {
extension ExpectTo where E.ValueType == Bool {
public func beTrue() throws {
let value = try expression()
if value != true {
Expand All @@ -146,7 +220,7 @@ extension ExpectationType where ValueType == Bool {

// Mark: Types

extension ExpectationType {
extension ExpectTo {
public func beOfType(_ expectedType: Any.Type) throws {
guard let value = try expression() else { throw failure("cannot determine type: expression threw an error or value is nil") }
let valueType = Mirror(reflecting: value).subjectType
Expand All @@ -159,7 +233,24 @@ extension ExpectationType {
// MARK: Error Handling

extension ExpectationType {

public func toThrow() throws {
try to.`throw`()
}
public func toThrow<T: Equatable>(_ error: T) throws {
try to.`throw`(error)
}
public func toNotThrow() throws {
try to.not.`throw`()
}
public func toThrow(_ match: (Error) -> Bool) throws {
try to.`throw`(match)
}
}

extension ExpectTo {

public func `throw`() throws {
var didThrow = false

do {
Expand All @@ -173,7 +264,7 @@ extension ExpectationType {
}
}

public func toThrow<T: Equatable>(_ error: T) throws {
public func `throw`<T: Equatable>(_ error: T) throws {
var thrownError: Error? = nil

do {
Expand All @@ -194,34 +285,40 @@ extension ExpectationType {
throw failure("expression did not throw an error")
}
}
}

// MARK: Comparable

public func > <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable {
let value = try lhs.expression()
guard value! > rhs else {
throw lhs.failure("\(String(describing: value)) is not more than \(rhs)")

public func `throw`(_ match: (Error) -> Bool) throws {
var thrownError: Error? = nil

do {
_ = try expression()
} catch {
thrownError = error
}

if let thrownError = thrownError {
if !match(thrownError) {
throw failure("\(thrownError) did not match")
}
} else {
throw failure("expression did not throw an error")
}
}
}

public func >= <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable {
let value = try lhs.expression()
guard value! >= rhs else {
throw lhs.failure("\(String(describing: value)) is not more than or equal to \(rhs)")
}
}

public func < <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable {
let value = try lhs.expression()
guard value! < rhs else {
throw lhs.failure("\(String(describing: value)) is not less than \(rhs)")
}
}
extension ExpectNotTo {

public func <= <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable {
let value = try lhs.expression()
guard value! <= rhs else {
throw lhs.failure("\(String(describing: value)) is not less than or equal to \(rhs)")
public func `throw`() throws {
var didThrow = false

do {
_ = try expression()
} catch {
didThrow = true
}

if didThrow {
throw failure("expression did throw an error")
}
}
}
32 changes: 29 additions & 3 deletions Tests/SpectreTests/ExpectationSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ public func testExpectation() {
$0.it("passes when value is nil") {
try expect(name).to.beNil()
}

$0.it("errors when value is nil") {
try expect("name").to.not.beNil()

do {
try expect(name).to.not.beNil()
fatalError()
} catch {}
}
}

$0.describe("comparison to type") {
Expand Down Expand Up @@ -128,6 +137,7 @@ public func testExpectation() {
case notFound
case noPermission
}
enum AnotherError: Error {}

func throwing() throws {
throw FileError.notFound
Expand All @@ -136,19 +146,35 @@ public func testExpectation() {
func nonThrowing() throws {}

$0.it("doesn't throw if error is the same") {
try expect(try throwing()).toThrow(FileError.notFound)
try expect(throwing()).to.throw(FileError.notFound)
}

$0.it("throws if the error differs") {
do {
try expect(try throwing()).toThrow(FileError.noPermission)
try expect(throwing()).to.throw(FileError.noPermission)
fatalError()
} catch {}
}

$0.it("throws if the error did not match") {
do {
try expect(throwing()).to.throw({ $0 is AnotherError })
fatalError()
} catch {}
}

$0.it("throws if no error was provided") {
do {
try expect(try nonThrowing()).toThrow()
try expect(nonThrowing()).to.throw()
fatalError()
} catch {}
}

$0.it("throws if error when no error expected") {
try expect(nonThrowing()).to.not.throw()

do {
try expect(throwing()).to.not.throw()
fatalError()
} catch {}
}
Expand Down