This commit is contained in:
piperfe 2020-07-20 17:51:46 -04:00
commit fb09349e7d
35 changed files with 486 additions and 123 deletions

3
.gitignore vendored
View File

@ -3,4 +3,5 @@ obj
*.sln.DotSettings.user *.sln.DotSettings.user
.vs .vs
vendor vendor
.idea/ .idea
*.iml

View File

@ -3,13 +3,14 @@ plugins {
} }
repositories { repositories {
maven { mavenCentral()
url = 'https://repo.maven.apache.org/maven2'
}
} }
dependencies { dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.6.2'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.6.2'
testImplementation 'com.approvaltests:approvaltests:5.0.0'
} }
group = 'com.gildedrose' group = 'com.gildedrose'

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -11,8 +11,8 @@
<properties> <properties>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<junit.jupiter.version>5.5.2</junit.jupiter.version> <junit.jupiter.version>5.6.2</junit.jupiter.version>
<maven.maven-surefire-plugin.version>2.22.2</maven.maven-surefire-plugin.version> <maven.maven-surefire-plugin.version>3.0.0-M4</maven.maven-surefire-plugin.version>
</properties> </properties>
<dependencies> <dependencies>

View File

@ -22,25 +22,29 @@ Whichever testing approach you choose, the idea of the exercise is to do some de
## Text-Based Approval Testing ## Text-Based Approval Testing
This is a testing approach which is very useful when refactoring legacy code. Before you change the code, you run it, and gather the output of the code as a plain text file. You review the text, and if it correctly describes the behaviour as you understand it, you can "approve" it, and save it as a "Golden Master". Then after you change the code, you run it again, and compare the new output against the Golden Master. Any differences, and the test fails. This code comes with comprehensive tests that use this approach. For information about how to run them, see the [texttests README](https://github.com/emilybache/GildedRose-Refactoring-Kata/tree/master/texttests)
It's basically the same idea as "assertEquals(expected, actual)" in a unit test, except the text you are comparing is typically much longer, and the "expected" value is saved from actual output, rather than being defined in advance.
Typically a piece of legacy code may not produce suitable textual output from the start, so you may need to modify it before you can write your first text-based approval test. That could involve inserting log statements into the code, or just writing a "main" method that executes the code and prints out what the result is afterwards. It's this latter approach we are using here to test GildedRose.
The Text-Based tests in this repository are designed to be used with the tool "TextTest" (http://texttest.org). This tool helps you to organize and run text-based tests. There is more information in the README file in the "texttests" subdirectory.
## Get going quickly using Cyber-Dojo ## Get going quickly using Cyber-Dojo
I've also set this kata up on [cyber-dojo](http://cyber-dojo.org) for several languages, so you can get going really quickly: I've also set this kata up on [cyber-dojo](https://cyber-dojo.org) for several languages, so you can get going really quickly:
- [JUnit, Java](http://cyber-dojo.org/forker/fork/751DD02C4C?avatar=snake&tag=8) To create an *individual* exercise:
- [C#](http://cyber-dojo.org/forker/fork/5C5AC766B0?avatar=koala&tag=3) - [C#, NUnit](https://cyber-dojo.org/forker/fork_individual/Fz4xFX?index=3)
- [C++](http://cyber-dojo.org/forker/fork/AA86ECBCC9?avatar=rhino&tag=7) - [C++ (g++), GoogleTest](https://cyber-dojo.org/forker/fork_individual/qPPrZy?index=7)
- [Ruby](http://cyber-dojo.org/forker/fork/A8943EAF92?avatar=hippo&tag=9) - [Java, Cucumber](https://cyber-dojo.org/forker/fork_individual/SvUf30?index=2) - for this one I've also written some step definitions for you
- [RSpec, Ruby](http://cyber-dojo.org/forker/fork/8E58B0AD16?avatar=raccoon&tag=3) - [Java, JUnit](https://cyber-dojo.org/forker/fork_individual/aJJEN4?index=2)
- [Python](http://cyber-dojo.org/forker/fork/297041AA7A?avatar=lion&tag=4) - [Python, unittest](https://cyber-dojo.org/forker/fork_individual/NFgFys?index=2)
- [Cucumber, Java](http://cyber-dojo.org/forker/fork/0F82D4BA89?avatar=gorilla&tag=48) - for this one I've also written some step definitions for you - [Ruby, RSpec](https://cyber-dojo.org/forker/fork_individual/D3xbUV?index=3)
- [Ruby, testunit](https://cyber-dojo.org/forker/fork_individual/zlElgj?index=9)
To create a *group* exercise:
- [C#, NUnit](https://cyber-dojo.org/forker/fork_group/Fz4xFX?index=3)
- [C++ (g++), GoogleTest](https://cyber-dojo.org/forker/fork_group/qPPrZy?index=7)
- [Java, Cucumber](https://cyber-dojo.org/forker/fork_group/SvUf30?index=2) - for this one I've also written some step definitions for you
- [Java, JUnit](https://cyber-dojo.org/forker/fork_group/aJJEN4?index=2)
- [Python, unittest](https://cyber-dojo.org/forker/fork_group/NFgFys?index=2)
- [Ruby, RSpec](https://cyber-dojo.org/forker/fork_group/D3xbUV?index=3)
- [Ruby, testunit](https://cyber-dojo.org/forker/fork_group/zlElgj?index=9)
## Better Code Hub ## Better Code Hub

4
cpp-catch2/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/build_meson/
/subprojects/Catch2-*/
/subprojects/packagecache/
/cmake-build-*/

47
cpp-catch2/CMakeLists.txt Normal file
View File

@ -0,0 +1,47 @@
cmake_minimum_required(VERSION 3.14..3.16)
set(CMAKE_VERBOSE_MAKEFILE ON)
project(GildedRoseKata VERSION 1.0
DESCRIPTION "The GildedRose Refactoring kata for an approval testing approach"
LANGUAGES CXX)
include(FetchContent)
FetchContent_Declare(
catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v2.12.2
)
FetchContent_MakeAvailable(catch2)
LIST(APPEND CMAKE_MODULE_PATH
${catch2_SOURCE_DIR}/contrib
)
FetchContent_Declare(
approvaltests_ho
URL https://github.com/approvals/ApprovalTests.cpp/releases/download/v.10.0.0/ApprovalTests.v.10.0.0.hpp
DOWNLOAD_NO_EXTRACT TRUE
DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR}/approvaltests
DOWNLOAD_NAME ApprovalTests.v.10.0.0.hpp
)
FetchContent_GetProperties(approvaltests_ho)
if (NOT approvaltests_ho_POPULATED)
FetchContent_Populate(approvaltests_ho)
endif ()
add_library(approvaltests INTERFACE)
target_include_directories(approvaltests
INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/approvaltests
INTERFACE ${catch2_SOURCE_DIR}/single_include/catch2
)
add_executable(gildedrose_catch2
src/GildedRose.h
src/GildedRose.cc
test/gildedrose_catch.cpp
test/main.cpp)
set_target_properties(gildedrose_catch2 PROPERTIES CXX_STANDARD 11)
target_include_directories(gildedrose_catch2
PUBLIC src)
target_link_libraries(gildedrose_catch2 Catch2::Catch2 approvaltests)
include(CTest)
include(ParseAndAddCatchTests)
ParseAndAddCatchTests(gildedrose_catch2)

20
cpp-catch2/README.md Normal file
View File

@ -0,0 +1,20 @@
C++ version of Gilded Rose with Catch 2 and Approvals
======================================================
This is a C++ start of the ApprovalTest version of the Gilded Rose Refactoring Kata. See
the [top level readme](https://github.com/emilybache/GildedRose-Refactoring-Kata)
for a general description of the exercise.
CMake
-----
CMake is included in CLion from JetBrains. Without CMake files
CLion has a hard time to handle c-projects.
To install CMake (if you don't use CLion) on macOS using brew
brew install cmake
Tested on CMake 3.15.3 (included with CLion 2019.3) on macOS

View File

@ -0,0 +1,80 @@
#include "GildedRose.h"
GildedRose::GildedRose(vector<Item> & items) : items(items)
{}
void GildedRose::updateQuality()
{
for (int i = 0; i < items.size(); i++)
{
if (items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert")
{
if (items[i].quality > 0)
{
if (items[i].name != "Sulfuras, Hand of Ragnaros")
{
items[i].quality = items[i].quality - 1;
}
}
}
else
{
if (items[i].quality < 50)
{
items[i].quality = items[i].quality + 1;
if (items[i].name == "Backstage passes to a TAFKAL80ETC concert")
{
if (items[i].sellIn < 11)
{
if (items[i].quality < 50)
{
items[i].quality = items[i].quality + 1;
}
}
if (items[i].sellIn < 6)
{
if (items[i].quality < 50)
{
items[i].quality = items[i].quality + 1;
}
}
}
}
}
if (items[i].name != "Sulfuras, Hand of Ragnaros")
{
items[i].sellIn = items[i].sellIn - 1;
}
if (items[i].sellIn < 0)
{
if (items[i].name != "Aged Brie")
{
if (items[i].name != "Backstage passes to a TAFKAL80ETC concert")
{
if (items[i].quality > 0)
{
if (items[i].name != "Sulfuras, Hand of Ragnaros")
{
items[i].quality = items[i].quality - 1;
}
}
}
else
{
items[i].quality = items[i].quality - items[i].quality;
}
}
else
{
if (items[i].quality < 50)
{
items[i].quality = items[i].quality + 1;
}
}
}
}
}

View File

@ -0,0 +1,24 @@
#include <string>
#include <vector>
using namespace std;
class Item
{
public:
string name;
int sellIn;
int quality;
Item(string name, int sellIn, int quality) : name(name), sellIn(sellIn), quality(quality)
{}
};
class GildedRose
{
public:
vector<Item> & items;
GildedRose(vector<Item> & items);
void updateQuality();
};

View File

@ -0,0 +1 @@
#include "ApprovalTests.v.10.0.0.hpp"

View File

@ -0,0 +1,21 @@
#include <catch2/catch.hpp>
#include "ApprovalTests.hpp"
#include "GildedRose.h"
std::ostream& operator<<(std::ostream& os, const Item& obj)
{
return os
<< "name: " << obj.name
<< ", sellIn: " << obj.sellIn
<< ", quality: " << obj.quality;
}
TEST_CASE("UpdateQuality") {
vector<Item> items;
items.push_back(Item("foo", 0, 0));
GildedRose app(items);
app.updateQuality();
REQUIRE("fixme" == app.items[0].name);
}

5
cpp-catch2/test/main.cpp Normal file
View File

@ -0,0 +1,5 @@
#define CATCH_CONFIG_MAIN
#define APPROVALS_CATCH
#include "catch2/catch.hpp"
#include "ApprovalTests.hpp"

View File

@ -2,3 +2,4 @@ add_subdirectory(cpp_catch2_approvaltest)
add_subdirectory(cpp_catch2_unittest) add_subdirectory(cpp_catch2_unittest)
add_subdirectory(cpp_googletest_approvaltest) add_subdirectory(cpp_googletest_approvaltest)
add_subdirectory(cpp_googletest_unittest) add_subdirectory(cpp_googletest_unittest)
add_subdirectory(cpp_texttest)

View File

@ -0,0 +1,15 @@
set(TEST_NAME GildedRoseTextTests)
add_executable(${TEST_NAME} GildedRoseTextTests.cc)
target_sources(${TEST_NAME} PRIVATE GildedRoseTextTests.cc)
target_link_libraries(${TEST_NAME} lib src)
set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 11)
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
# Set compiler option /FC for Visual Studio to to make the __FILE__ macro expand to full path.
# The __FILE__ macro is used by Catch2 to get the path to current test file.
# Links:
# * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019
# * https://docs.microsoft.com/en-us/cpp/build/reference/fc-full-path-of-source-code-file-in-diagnostics?view=vs-2019
if (MSVC)
target_compile_options(${TEST_NAME} PRIVATE "/FC")
endif()

View File

@ -0,0 +1,44 @@
#include <cstdio>
#include "GildedRose.h"
int
print_item(Item *item)
{
return printf("%s, %d, %d\n", item->name.c_str(), item->sellIn, item->quality);
}
int main()
{
vector<Item> items;
items.emplace_back("+5 Dexterity Vest", 10, 20);
items.emplace_back("Aged Brie", 2, 0);
items.emplace_back("Elixir of the Mongoose", 5, 7);
items.emplace_back("Sulfuras, Hand of Ragnaros", 0, 80);
items.emplace_back("Sulfuras, Hand of Ragnaros", -1, 80);
items.emplace_back("Backstage passes to a TAFKAL80ETC concert", 15, 20);
items.emplace_back("Backstage passes to a TAFKAL80ETC concert", 10, 49);
items.emplace_back("Backstage passes to a TAFKAL80ETC concert", 5, 49);
// this Conjured item doesn't yet work properly
items.emplace_back("Conjured Mana Cake", 3, 6);
puts("OMGHAI!");
GildedRose app(items);
for (int day = 0; day <= 30; day++)
{
printf("-------- day %d --------\n", day);
printf("name, sellIn, quality\n");
for (auto & item : items)
{
print_item(&item);
}
printf("\n");
app.updateQuality();
}
return 0;
}

45
plpgsql/Dockerfile Normal file
View File

@ -0,0 +1,45 @@
FROM postgres:12.1 as base
WORKDIR /app
ENV PGHOST=localhost
ENV PGDATABASE=kata
ENV PGPASSWORD=admin
ENV PGUSER=postgres
ENV POSTGRES_PASSWORD=admin
ENV PGDATA /var/lib/postgresql/data_local
RUN mkdir -p "$PGDATA" && chown -R postgres:postgres "$PGDATA" && chmod 777 "$PGDATA"
ADD ./*.sh /app/
ADD ./src/item.sql /app/src/
ADD ./src/new_item.sql /app/src/
# PGUNIT
FROM base as pgunit
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates wget \
&& rm -rf /var/lib/apt/lists/*
ADD ./pgunit/initialize.sh /app/
ADD ./pgunit/*.sql /app/
RUN chmod +x ./*.sh \
&& ./initializeDocker.sh
# PGTAP
FROM base as pgtap
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates build-essential git-core libv8-dev curl postgresql-server-dev-12 \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p /tmp/pgtap \
&& cd /tmp/pgtap \
&& git clone https://github.com/theory/pgtap.git /tmp/pgtap \
&& make \
&& make install \
&& cpan TAP::Parser::SourceHandler::pgTAP
ADD ./pgtap/initialize.sh /app/
RUN chmod +x ./*.sh \
&& ./initializeDocker.sh

View File

@ -6,36 +6,58 @@ You'll need:
To use remote / local dockerized database, add ``` --host --port --username``` parameters to plsql invocation. To use remote / local dockerized database, add ``` --host --port --username``` parameters to plsql invocation.
# Setup # Setup
## With docker
Run `docker-compose up -d <TEST_FRAMEWORK>` to start, and `docker-compose exec <TEST_FRAMEWORK> bash` to enter in container.
`<TEST_FRAMEWORK>` is the testing framework name. Two values are possible: `pgunit` or `pgtap`
## Without docker
In shell: In shell:
- create database: ```createdb gilded_rose``` - create database: ```createdb kata```
- create item table (structure): ```psql -d gilded_rose -f ./structure/create.sql``` - create item table (structure): ```psql -d kata -f ./src/item.sql```
- load code into database: ```psql -d gilded_rose -f ./src/update_quality.sql ``` - create procedure to help to add item: ```psql -d kata -f ./src/new_item.sql```
- load code into database: ```psql -d kata -f ./src/update_quality.sql ```
If you get this message```LINE 1: CREATE OR REPLACE PROCEDURE public.update_quality()```, your PostgreSQL version may under 11, consider upgrading. If you get this message```LINE 1: CREATE OR REPLACE PROCEDURE public.update_quality()```, your PostgreSQL version may under 11, consider upgrading.
# Run # Run
In shell: In shell:
- load manual test data into database: ```psql -d gilded_rose -f ./test/manual/load.sql``` - connect to CLI: ```psql -d kata```
- connect to CLI: ```psql -d gilded_rose``` - add item: ```CALL new_item('+5 Dexterity Vest', 10, 20);```
- check item state: ```SELECT * FROM item;``` - check item state: ```SELECT * FROM item;```
- execute item update: ```CALL update_quality();``` - execute item update: ```CALL update_quality();```
- check item state: ```SELECT * FROM item;``` - check item state: ```SELECT * FROM item;```
- empty table : ```TRUNCATE TABLE item;``` - empty table : ```TRUNCATE TABLE item;```
# Kata
`src/update_quality.sql` contains code to refactor.
# Test # Test
## Using pgTAP ## Using pgTAP
### Requirements ### Requirements
Install pgTAP [instructions here](https://pgtap.org/documentation.html#installation) Install pgTAP [instructions here](https://pgtap.org/documentation.html#installation)
It's already installed with docker
```item``` table is supposed to be empty. ```item``` table is supposed to be empty.
If not, it would cause a (false positive)[https://en.wikipedia.org/wiki/False_positives_and_false_negatives] If not, it would cause a (false positive)[https://en.wikipedia.org/wiki/False_positives_and_false_negatives]
### Execute ### Execute
In shell, execute ```pg_prove --dbname gilded_rose test/pgtap/*.sql```. Run `docker-compose up -d pgtap` to start, and `docker-compose exec pgtap bash` to enter in container.
You should get ```test/pgtap/template.sql .. ok All tests successful.``` In shell, execute ```psql -d kata -f src/update_quality.sql && pg_prove pgtap/test_*.sql```.
You should get ```pgtap/test_case_update_quality.sql .. ok All tests successful.```
If you get this message ```ERROR: function plan(integer) does not exist LINE 1: SELECT PLAN(1);```, pgTAP is not working => check your pgTAP installation. If you get this message ```ERROR: function plan(integer) does not exist LINE 1: SELECT PLAN(1);```, pgTAP is not working => check your pgTAP installation.
If you get this message ```Failed test: 2 Parse errors: Bad plan. You planned 1 tests but ran 2.```, the item table contains data, interfering with the test => empty it, then run test again. If you get this message ```Failed test: 2 Parse errors: Bad plan. You planned 1 tests but ran 2.```, the item table contains data, interfering with the test => empty it, then run test again.
`pgtap/test_case_update_quality.sql` contains test examples.
## Using pgunit
### Requirement
Unit test framework used : pgunit (https://github.com/adrianandrei-ca/pgunit)
It's already installed with docker
### Execute
Run `docker-compose up -d pgunit` to start, and `docker-compose exec pgunit bash` to enter in container.
You can run `cat src/update_quality.sql pgunit/run_tests.sql | psql -d kata -f -`
`pgunit/run_tests.sql` contains test examples.

View File

@ -0,0 +1,22 @@
version: '3.4'
services:
pgunit:
# image: fpellet/gildedrose-refactoring-kata:pgunit
build:
context: .
target: pgunit
ports:
- "5432:5432"
volumes:
- .:/app/:z
pgtap:
# image: fpellet/gildedrose-refactoring-kata:pgtap
build:
context: .
target: pgtap
ports:
- "5432:5432"
volumes:
- .:/app/:z

View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -ex
echo "Create database"
psql -d postgres -c 'DROP DATABASE IF EXISTS kata;'
psql -d postgres -c 'CREATE DATABASE kata;'
./initialize.sh
echo "Add current code"
psql -d kata -f src/item.sql
psql -d kata -f src/new_item.sql

View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -ex
echo "Enable extension"
psql -d kata -c 'CREATE EXTENSION IF NOT EXISTS pgtap;'

View File

@ -0,0 +1,18 @@
BEGIN;
-- Plan count should match the number of tests. If it does not then pg_prove will fail the test
SELECT plan(1);
-- Run the tests.
-- Given
TRUNCATE TABLE item;
CALL new_item('foo', 0, 0);
-- When
CALL update_quality();
-- Then
SELECT is( name, 'fixme', 'name did change' ) FROM item;
-- Finish the tests and clean up.
SELECT * FROM finish();
ROLLBACK;

View File

@ -1,20 +0,0 @@
FROM postgres:12.1
WORKDIR /app
ENV PGHOST=localhost
ENV PGDATABASE=kata
ENV PGPASSWORD=admin
ENV PGUSER=postgres
ENV POSTGRES_PASSWORD=admin
ENV PGDATA /var/lib/postgresql/data_local
RUN mkdir -p "$PGDATA" && chown -R postgres:postgres "$PGDATA" && chmod 777 "$PGDATA"
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates wget \
&& rm -rf /var/lib/apt/lists/*
ADD ./*.sh /app/
ADD ./*.sql /app/
RUN chmod +x ./*.sh \
&& ./initializeDocker.sh

View File

@ -1,16 +0,0 @@
version: '3'
services:
database:
build: .
ports:
- "5432:5432"
volumes:
- .:/app/:z
admin:
image: adminer
links:
- database
ports:
- "8081:8080"

View File

@ -2,9 +2,7 @@
set -ex set -ex
echo "Create database" echo "Enable DBLINK"
psql -d postgres -c 'DROP DATABASE IF EXISTS kata;'
psql -d postgres -c 'CREATE DATABASE kata;'
psql -d kata -c 'CREATE EXTENSION DBLINK;' psql -d kata -c 'CREATE EXTENSION DBLINK;'
echo "Initialize test framework" echo "Initialize test framework"
@ -14,8 +12,3 @@ wget https://raw.githubusercontent.com/adrianandrei-ca/pgunit/bc69dfc526ec3db55f
echo "Initialize custom asserts" echo "Initialize custom asserts"
psql -d kata -f asserts.sql psql -d kata -f asserts.sql
echo "Add current code"
psql -d kata -f item.sql
psql -d kata -f new_item.sql
psql -d kata -f update_quality.sql

View File

@ -1,10 +0,0 @@
## Requirement
Testing on postgres 12
Unit test framework used : pgunit (https://github.com/adrianandrei-ca/pgunit)
## Setup
Run `docker-compose up -d` to start, and `docker-compose exec database bash` to enter in container.
You can run `cat update_quality.sql run_tests.sql | psql -d kata -f -`
## Kata
`update_quality.sql` contains code to refactor, and `run_tests.sql` contains test examples.

View File

@ -1,5 +0,0 @@
CREATE TABLE item (
name CHARACTER VARYING(100) NOT NULL,
sellIn INTEGER,
quality INTEGER NOT NULL
);

2
ruby/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea
*.iml

View File

@ -1,24 +1,46 @@
# TextTest regression tests # TextTest regression tests
This folder contains Text-Based tests for the GildedRose Refactoring Kata. This folder contains Text-Based Approval tests for the GildedRose Refactoring Kata. They are fairly comprehensive and well worth using if you'd prefer to go straight to the refactoring without writing your own tests first.
These tests are designed to be used with the open source testing tool "TextTest", available from http://texttest.org You can run them without it too though, see below. These tests are designed to be used with the open source testing tool "TextTest", available from [http://texttest.org](http://texttest.org).
## Install TextTest
There are install instructions on the [texttests website](http://texttest.sourceforge.net/index.php?page=documentation_4_0&n=install_texttest). If you are happy to run without the Graphical User Interface, then you only need python3 and pip:
> pip install texttest
## Configure language version
Before you can run the tests you need to tell texttest which language version of GildedRose you plan to refactor. Open the file 'config.gr' and edit it. Several languages are supported. All lines starting with '#' are comments in this file. Find the lines referring to the language you want, and uncomment them. (Note some languages like Java need several lines uncommented)
While you're here, change the settings for editor and diff program to match your preferences. By default it uses 'subl' and 'meld'. It will accept any editors or diff programs that you can run from the command line.
## running TextTest
Start texttest from the folder above the one this file is in. Texttest detects the current working directory and uses that as the variable $TEXTTEST_HOME in the config.gr file.
# replace this path with wherever you cloned this repo
> cd /home/ec2-user/workspace/GildedRose-Refactoring-Kata
> texttest &
This should start the GUI for the TextTest tool. Select the test case "ThirtyDays" and press the "Run" button. This will open a new 'runner' window for each test run.
If the texttest GUI doesn't work, or you prefer to use the command line, use this instead:
> texttest -con
That will run all the test cases it finds and report the results.
## Running without TextTest ## Running without TextTest
This should be perfectly possible, but is probably less convenient than using TextTest. This should be perfectly possible, but is probably less convenient than using TextTest.
Write a script that will execute the SUT (see "config.gr" for details of the executables), giving the commandline options listed in "options.gr". Collect the output from standard output in a file, and diff that against the golden copy "stdout.gr". Any diff is a test failure. Write a script that will execute the system under test (see "config.gr" for details of the executables), giving the commandline options listed in "options.gr". Collect the output from standard output in a file, and diff that against the golden copy "stdout.gr". Any diff is a test failure.
## Running with TextTest ## Explaining TextTest test cases
- Install TextTest (see http://texttest.org) Each test case has it's own subdirectory. The name of the directory is the name of the test - in this case "ThirtyDays". The "Golden Master" of the output for that test case is kept in that directory. In this case we have three files:
- set $TEXTTEST_HOME environment variable to point at the "texttests" folder
- run texttest using a command like "python texttest.py -a gr"
This should start the GUI for the TextTest tool.
Each test case has it's own subdirectory. The name of the directory is the name of the test - in this case "ThirtyDays". The "Golden Copy" of the output for that test case is kept in that directory. In this case we have three files:
- __stderr.gr__ - the expected output to Standard Error (stderr) - __stderr.gr__ - the expected output to Standard Error (stderr)
- __stdout.gr__ - the expected output to Standard Output (stdout) - __stdout.gr__ - the expected output to Standard Output (stdout)
@ -34,4 +56,11 @@ To run a test, click on it in the GUI and select "Run". TextTest will run it in
If you run into difficulties with TextTest, there is documentation available on [texttest.org](http://texttest.org), or you can ask a question on the [mailing list](https://lists.sourceforge.net/lists/listinfo/texttest-users). If you run into difficulties with TextTest, there is documentation available on [texttest.org](http://texttest.org), or you can ask a question on the [mailing list](https://lists.sourceforge.net/lists/listinfo/texttest-users).
## Introduction to Text-Based Approval Testing
This is a testing approach which is very useful when refactoring legacy code. Before you change the code, you run it, and gather the output of the code as a plain text file. You review the text, and if it correctly describes the behaviour as you understand it, you can "approve" it, and save it as a "Golden Master". Then after you change the code, you run it again, and compare the new output against the Golden Master. Any differences, and the test fails.
It's basically the same idea as "assertEquals(expected, actual)" in a unit test, except the text you are comparing is typically much longer, and the "expected" value is saved from actual output, rather than being defined in advance.
Typically a piece of legacy code may not produce suitable textual output from the start, so you may need to modify it before you can write your first text-based approval test. One way to do that is to write a "main" method that executes the code and prints out what the result is afterwards. Each language version has implemented a texttest 'fixture' that does this. It runs the GildedRose 'update_quality' method once each day for 30 days, printing the item state each day.

View File

@ -1,35 +1,31 @@
full_name:Gilded Rose Refactoring Kata full_name:Gilded Rose Refactoring Kata
# location where you have your clone # set your preferred editor and diff tool.
default_checkout:/Users/emily/training_materials/Refactoring-Katas/GildedRose view_program:subl
diff_program:meld
# Settings for the Java version
executable:com.gildedrose.TexttestFixture
interpreter:java
# note you'll also need to update the file environment.gr with your classpath if you keep your classfiles somewhere unusual
# Settings for the Python version # Settings for the Python version
#executable:${TEXTTEST_CHECKOUT}/python/texttest_fixture.py #executable:${TEXTTEST_HOME}/python/texttest_fixture.py
#interpreter:python #interpreter:python
# Settings for the C++ version # Settings for the cpp version
#executable:${TEXTTEST_CHECKOUT}/cpp/GildedRoseTextTests #executable:${TEXTTEST_HOME}/cpp/cmake-build-debug/test/cpp_texttest/GildedRoseTextTests
# Settings for the Java version
#executable:com.gildedrose.TexttestFixture
#interpreter:java
# note you'll also need to update the file environment.gr with your classpath if you keep your classfiles somewhere unusual
# Settings for the Ruby version # Settings for the Ruby version
#executable:${TEXTTEST_CHECKOUT}/ruby/texttest_fixture.rb #executable:${TEXTTEST_HOME}/ruby/texttest_fixture.rb
#interpreter:ruby #interpreter:ruby
# Settings for the C# version # Settings for the C# version
#executable:${TEXTTEST_CHECKOUT}/GildedRose.exe #executable:${TEXTTEST_HOME}/GildedRose.exe
# Settings for the Perl version # Settings for the Perl version
#executable:${TEXTTEST_CHECKOUT}/perl/texttest_fixture.pl #executable:${TEXTTEST_HOME}/perl/texttest_fixture.pl
#interpreter:perl #interpreter:perl
# turn on one of these if you prefer them to notepad or emacs.
[view_program]
*:mate
#*:gedit
[end]
filename_convention_scheme:standard filename_convention_scheme:standard

View File

@ -1,3 +1,3 @@
# If your .class files are somewhere else, add the path to the list # If your .class files are somewhere else, add the path to the list
CLASSPATH:${TEXTTEST_CHECKOUT}/Java:${TEXTTEST_CHECKOUT}/Java/bin CLASSPATH:${TEXTTEST_HOME}/Java:${TEXTTEST_HOME}/Java/bin
PERL5OPT:-I${TEXTTEST_CHECKOUT}/perl PERL5OPT:-I${TEXTTEST_HOME}/perl