mirror of
https://github.com/emilybache/GildedRose-Refactoring-Kata.git
synced 2026-02-15 22:41:30 +00:00
Merge branch 'master' of https://github.com/piperfe/GildedRose-Refactoring-Kata
This commit is contained in:
commit
fb09349e7d
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@ obj
|
|||||||
*.sln.DotSettings.user
|
*.sln.DotSettings.user
|
||||||
.vs
|
.vs
|
||||||
vendor
|
vendor
|
||||||
.idea/
|
.idea
|
||||||
|
*.iml
|
||||||
|
|||||||
@ -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'
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
34
README.md
34
README.md
@ -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
4
cpp-catch2/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/build_meson/
|
||||||
|
/subprojects/Catch2-*/
|
||||||
|
/subprojects/packagecache/
|
||||||
|
/cmake-build-*/
|
||||||
47
cpp-catch2/CMakeLists.txt
Normal file
47
cpp-catch2/CMakeLists.txt
Normal 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
20
cpp-catch2/README.md
Normal 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
|
||||||
|
|
||||||
80
cpp-catch2/src/GildedRose.cc
Normal file
80
cpp-catch2/src/GildedRose.cc
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
cpp-catch2/src/GildedRose.h
Normal file
24
cpp-catch2/src/GildedRose.h
Normal 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();
|
||||||
|
};
|
||||||
|
|
||||||
1
cpp-catch2/test/ApprovalTests.hpp
Normal file
1
cpp-catch2/test/ApprovalTests.hpp
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "ApprovalTests.v.10.0.0.hpp"
|
||||||
21
cpp-catch2/test/gildedrose_catch.cpp
Normal file
21
cpp-catch2/test/gildedrose_catch.cpp
Normal 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
5
cpp-catch2/test/main.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#define APPROVALS_CATCH
|
||||||
|
|
||||||
|
#include "catch2/catch.hpp"
|
||||||
|
#include "ApprovalTests.hpp"
|
||||||
@ -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)
|
||||||
|
|||||||
15
cpp/test/cpp_texttest/CMakeLists.txt
Normal file
15
cpp/test/cpp_texttest/CMakeLists.txt
Normal 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()
|
||||||
44
cpp/test/cpp_texttest/GildedRoseTextTests.cc
Normal file
44
cpp/test/cpp_texttest/GildedRoseTextTests.cc
Normal 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
45
plpgsql/Dockerfile
Normal 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
|
||||||
@ -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.
|
||||||
|
|||||||
22
plpgsql/docker-compose.yml
Normal file
22
plpgsql/docker-compose.yml
Normal 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
|
||||||
13
plpgsql/initializeDatabase.sh
Normal file
13
plpgsql/initializeDatabase.sh
Normal 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
|
||||||
6
plpgsql/pgtap/initialize.sh
Normal file
6
plpgsql/pgtap/initialize.sh
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
echo "Enable extension"
|
||||||
|
psql -d kata -c 'CREATE EXTENSION IF NOT EXISTS pgtap;'
|
||||||
18
plpgsql/pgtap/test_case_update_quality.sql
Normal file
18
plpgsql/pgtap/test_case_update_quality.sql
Normal 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;
|
||||||
@ -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
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
database:
|
|
||||||
build: .
|
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
volumes:
|
|
||||||
- .:/app/:z
|
|
||||||
|
|
||||||
admin:
|
|
||||||
image: adminer
|
|
||||||
links:
|
|
||||||
- database
|
|
||||||
ports:
|
|
||||||
- "8081:8080"
|
|
||||||
@ -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
|
|
||||||
@ -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.
|
|
||||||
@ -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
2
ruby/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.idea
|
||||||
|
*.iml
|
||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
Loading…
Reference in New Issue
Block a user