Tool for generating Acceptance Tests in Xcode, inspired by Fitnesse

Overview

License Language Build Issues Twitter

AcceptanceMark is a tool for generating Acceptance Tests in Xcode, inspired by Fitnesse.

Read this blog post for a full introduction to AcceptanceMark.

Fitnesse advantages

  • Easy to write business rules in tabular form in Markdown files.
  • All shareholders can write Fitnesse tests.
  • Convenient Test Report.

Fitnesse disadvantages

  • Does not integrate well with XCTest.
  • Requires to run a separate server.
  • Difficult to configure and run locally / on CI.

The solution: AcceptanceMark

AcceptanceMark is the ideal tool to write Fitnesse-style acceptance tests that integrate seamlessly with XCTest:

  • Write your tests inputs and expected values in markdown tables.
  • AcceptanceMark generates XCTest test classes with strong-typed input/outputs.
  • Write test runners to evaluate the system under test with the given inputs.
  • Run the chosen test target (Unit Tests supported, UI Tests will be supported) and get a test report.

How does this work?

Write your own test sets, like so:

image-tests.md

## Image Loading

| name:String   || loaded:Bool  |
| ------------- || ------------ |
| available.png || true         |
| missing.png   || false        |

Run amtool manually or as an Xcode pre-compilation phase:

amtool -i image-tests.md

This generates an XCTestCase test class:

/*
 * File Auto-Generated by AcceptanceMark - DO NOT EDIT
 * input file: ImageTests.md
 * generated file: ImageTests_ImageLoadingTests.swift
 *
 * -- Test Specification -- 
 *
 * ## Image Loading
 * | name:String   || loaded:Bool  |
 * | ------------- || ------------ |
 * | available.png || true         |
 * | missing.png   || false        |
 */

//// Don't forget to create a test runner: 
//
//class ImageTests_ImageLoadingRunner: ImageTests_ImageLoadingRunnable {
//
//	func run(input: ImageTests_ImageLoadingInput) throws -> ImageTests_ImageLoadingOutput {
//		return ImageTests_ImageLoadingOutput(<#parameters#>)
//	}
//}

import XCTest

struct ImageTests_ImageLoadingInput {
	let name: String
}

struct ImageTests_ImageLoadingOutput: Equatable {
	let loaded: Bool
}

protocol ImageTests_ImageLoadingRunnable {
	func run(input: ImageTests_ImageLoadingInput) throws -> ImageTests_ImageLoadingOutput
}
class ImageTests_ImageLoadingTests: XCTestCase {

	var testRunner: ImageTests_ImageLoadingRunnable!

	override func setUp() {
		// MARK: Implement the ImageTests_ImageLoadingRunner() class!
		testRunner = ImageTests_ImageLoadingRunner()
	}

	func testImageLoading_row1() {
		let input = ImageTests_ImageLoadingInput(name: "available.png")
		let expected = ImageTests_ImageLoadingOutput(loaded: true)
		let result = try! testRunner.run(input: input)
		XCTAssertEqual(expected, result)
	}

	func testImageLoading_row2() {
		let input = ImageTests_ImageLoadingInput(name: "missing.png")
		let expected = ImageTests_ImageLoadingOutput(loaded: false)
		let result = try! testRunner.run(input: input)
		XCTAssertEqual(expected, result)
	}

}

func == (lhs: ImageTests_ImageLoadingOutput, rhs: ImageTests_ImageLoadingOutput) -> Bool {
	return
		lhs.loaded == rhs.loaded
}

Write your test runner:

// User generated file. Put your test runner implementation here.
class ImageTests_ImageLoadingTestRunner: ImageTests_ImageLoadingTestRunnable {

    func run(input: ImageTests_ImageLoadingInput) throws -> ImageTests_ImageLoadingResult {
        // Your business logic here
        return ImageTests_ImageLoadingResult(loaded: true)
    }
}

Add your generated test classes and test runners to your Xcode test target and run the tests.

Notes

  • Note the functional style of the test runner. It is simply a method that takes a stronly-typed input value, and returns a strongly-typed output value. No state, no side effects.

  • XCTestCase sublasses can specify a setUp() method to configure an initial state that is shared across all unit tests. This is deliberately not supported with AcceptanceMark test runners, and state-less tests are preferred and encouraged instead.

Installation

AcceptanceMark includes amtool, a command line tool used to generate unit tests or UI tests.

Pre-compiled binary

The quickest way to install amtool is to download the pre-compiled executable from the project releases page.

Once dowloaded, don't forget to add execute permission to the binary:

chmod +x amtool

Compile manually

Xcode 8 is required as amtool is written in Swift 3. To compile, clone this repo and run the script:

git clone https://github.com/bizz84/AcceptanceMark
cd AcceptanceMark
./scripts/build-amtool.sh

Once the build finishes, amtool can be found at this location:

./build/Release/amtool

For convenience, amtool can be copied to a folder in your $PATH:

export PATH=$PATH:/your/path/to/amtool

amtool command line options

amtool -i <input-file.md> [-l swift2|swift3]
  • Use -i to specify the input file
  • Use -l to specify the output language. Currently Swift 2 and Swift 3 are supported. Objective-C and other languages may be added in the future.
  • Use --version to print the version number

FAQ

  • Q: I want to have more than one table for each .md file. Is this possible?

  • A: Yes, as long as the file is structured as [ Heading, Table, Heading, Table, ... ], AcceptanceMark will generate multiple swift test files named <filename>_<heading>Tests.swift. This way, each test set gets its own swift classes all in one file. Note that heading names should be unique per-file. Whitespaces and punctuation will be stripped from headings.

  • Q: I want to preload application data/state for each test in a table (this is done with builders in Fitnesse). Can I do that?

  • A: This is in the future roadmap. While the specification for this may change, one possible way of doing this is by allowing more than one table for each heading, with the convention that the last table represents the input/output set, while all previous tables represent data to be preloaded. Until this is implemented, all preloading must be done directly in the test runner's run() method. Preloading example:

## Image Loading

// Preloaded data
| Country:String | Code:Bool |
| -------------- | --------- |
| United Kingdom | GB        |
| Italy          | IT        |
| Germany        | DE        |
 
// Test runner data
| name:String   || loaded:Bool  |
| ------------- || ------------ |
| available.png || true         |
| missing.png   || false        |
  • Q: I want to preload a JSON file for all tests running on a given table. Can I do that?
  • A: You could do that directly by adding the JSON loading code directly in the test runner's run() method. For extra configurability you could specify the JSON file name as an input parameter of your test set, and have your test runner load that file from the bundle.

LICENSE

MIT License. See the license file for details.

Comments
  • Rename default unit test name to test<Heading>_rowX

    Rename default unit test name to test_rowX

    And make tests index start from 1, not 0.

    This is so that tests read like this:

    func testMethod_run1()
    func testMethod_run2()
    ...
    

    rather than like this:

    func testMethod_0()
    func testMethod_1()
    ...
    
    opened by bizz84 1
  • Change `try!` to `try?` in test generator

    Change `try!` to `try?` in test generator

    Force try in the generated tests has the potential to crash. It would be preferable to fail the tests rather than crashing, so force try has been changed to optional try try?

    Also fixes failing unit test.

    Additionally this makes generated code compliant with strict swift lint rules.

    opened by BradenHancock 0
  • Add a description column to be used as test name or comment to describe what each test does

    Add a description column to be used as test name or comment to describe what each test does

    Currently, AcceptanceMark does not provide a way of describing what each test does. It would be useful to add a description column to describe what the test will do. Example:

    | year:Int | month:Int | day:Int | language | dateStyle | | localizedDate | | description | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 2016 | 9 | 16 | en | short | | 9/16/16, 5:30 PM | | formats the date in English | | 2016 | 9 | 16 | fr | short | | 16/09/2016 17:30 | | formats the date in French | | 2016 | 9 | 16 | it | short | | 16/09/16, 17:30 | | formats the date in Italian |

    One way to do this is to add an additional optional column separator after the outputs. The additional description column could be used to name the test so that the generated method could change from this:

    func testDateFormatting_row1()
    

    to this:

    func testDateFormatting_row1_formatsTheDateInEnglish()
    
    enhancement 
    opened by bizz84 0
  • Empty separator convention for inputs/outputs is invalid table markdown syntax

    Empty separator convention for inputs/outputs is invalid table markdown syntax

    This is a valid markdown table:

    | input1 | input2 | | output | | --- | --- | --- | --- | | 1 | 2 | | 3 |

    This isn't.

    | input1 |input2|| output | | --- | -------|| ------- | | 1 | 2 || 3 |

    It would be desirable to ensure all tables are rendered correctly, while preserving the same rules for input/output fields

    opened by bizz84 0
  • Include all generated XCTests into an Xcode test target

    Include all generated XCTests into an Xcode test target

    Purpose: Keep AcceptanceMark Test target up to date as input files are modified.

    By running amtool, all the required test files are written into an output folder.

    Unless amtool keeps the test target up to date, the user is responsible for making sure that all the test files are included in the test target, which is error prone and a time drain.

    We need a way of hooking into the client Xcode project to keep the test target up to date.

    enhancement 
    opened by bizz84 0
Releases(0.2.3)
Owner
Andrea Bizzotto
Flutter GDE ❖ Creator of codewithandrea.com ❖ YouTube: nnbd.me/yt ❖ Complete Dart Course: nnbd.me/dart
Andrea Bizzotto
Erik is an headless browser based on WebKit. An headless browser allow to run functional tests, to access and manipulate webpages using javascript.

Erik Erik is a headless browser based on WebKit and HTML parser Kanna. An headless browser allow to run functional tests, to access and manipulate web

Eric Marchand 544 Dec 30, 2022
Mockit is a Tasty mocking framework for unit tests in Swift 5.0

Mockit Introduction Mockit is a Tasty mocking framework for unit tests in Swift 5.0. It's at an early stage of development, but its current features a

Syed Sabir Salman-Al-Musawi 118 Oct 17, 2022
Swift Package with examples of how to tests iOS apps

GimmeTheCodeTDD A swift package with examples of unit tests in iOS development. Requirements Xcode 13 Content Dependency Injection Constructor Injecti

Dominik Hauser 8 Oct 11, 2021
Trying to implement Unit Tests for @Binding properties in a ViewModel

BindingTester Trying to implement Unit Tests for @Binding properties in a ViewModel ViewModel to be tested class SheetViewModel: ObservableObject {

Raphael Guye 0 Oct 22, 2021
Library for unifying the approach to network mocking in iOS unit- & UI-tests.

TinkoffMockStrapping Example To run the example project, clone the repo, and run pod install from the Example directory first. Requirements Installati

Online financial ecosystem 22 Jan 3, 2023
Catching fatal errors in unit tests

Precondition Catching When running tests which hit fatal errors, often preconditions the built-in support with XCTest. One package which supports cach

Brennan Stehling 0 Nov 28, 2021
Small library to easily run your tests directly within a Playground

[] (https://developer.apple.com/swift/) Build Status Branch Status master develop About PlaygroundTDD enables you to use TDD directly on Xcode Playgro

Whiskerz AB 317 Nov 22, 2022
Write unit tests which test the layout of a view in multiple configurations

Overview This library enables you to write unit tests which test the layout of a view in multiple configurations. It tests the view with different dat

LinkedIn 565 Nov 16, 2022
UITest helper library for creating readable and maintainable tests

UITestHelper General information When creating UI tests you will see that you are often repeating the same pieces of code. The UITestHelper library wi

Edwin Vermeer 56 Feb 20, 2022
Swift framework containing a set of helpful XCTest extensions for writing UI automation tests

AutoMate • AppBuddy • Templates • ModelGenie AutoMate AutoMate is a Swift framework containing a set of helpful XCTest extensions for writing UI autom

PGS Software 274 Dec 30, 2022
Upload failing iOS snapshot tests cases to S3

Second Curtain If you're using the cool FBSnapshotTestCase to test your iOS view logic, awesome! Even better if you have continuous integration, like

Ash Furrow 129 Sep 6, 2022
A collection of useful test helpers designed to ease the burden of writing tests for iOS applications.

MetovaTestKit is a collection of useful test helpers designed to ease the burden of writing tests for iOS applications. Requirements Installation Usag

null 23 Aug 29, 2021
Snapshot view unit tests for iOS

iOSSnapshotTestCase (previously FBSnapshotTestCase) What it does A "snapshot test case" takes a configured UIView or CALayer and uses the necessary UI

Uber Open Source 1.7k Jan 4, 2023
Freezer is a library that allows your Swift tests to travel through time by mocking NSDate class.

Freezer Freezer is a library that allows your Swift tests to travel through time by mocking NSDate class. Usage Once Freezer.start() has been invoked,

Sergey Petrov 8 Sep 24, 2022
TestSchedulerDemo - Demonstration code for iOS Unit Tests and Asynchronous

TestSchedulerDemo This repository contains demonstration code for my Medium arti

Carsten Wenderdel 0 Mar 19, 2022
Marvel - Marvel Characters App using MVVM, and including unit tests

Marvel About The purpose of this project is to develop a app using MVVM, and inc

null 1 Mar 20, 2022
Detailed explanations and implementations of various maths concepts for writing high performance code/algorithms backed with Unit tests.

Detailed explanations and implementations of various maths concepts which can help software Engineers write high performance code/algorithms backed with Unit tests.

Mussa Charles 2 Sep 25, 2022
PlaygroundTester enables you to easily run tests for your iPad Playgrounds 4 project.

PlaygroundTester PlaygroundTester is a package that enables you to add tests to your iPad Swift Playgrounds project. Installation Just add PlaygroundT

Paweł Łopusiński 36 Dec 13, 2022
Gauntlet is a collection of testing utility methods that aims to make writing great tests easier.

Gauntlet What is Gauntlet? Gauntlet is a collection of testing utility methods that aims to make writing great tests easier and with more helpful and

null 11 Dec 17, 2022