All Projects → gregorriegler → seamer

gregorriegler / seamer

Licence: MIT license
refactoring tool that aims at making it easy to create characterization tests

Programming Languages

java
68154 projects - #9 most used programming language

Projects that are alternatives of or similar to seamer

churn
Find refactoring candidates in your Elixir project easily with Churn 🧹
Stars: ✭ 87 (+443.75%)
Mutual labels:  refactoring, refactoring-tools
Piranha
A tool for refactoring code related to feature flag APIs
Stars: ✭ 1,840 (+11400%)
Mutual labels:  refactoring, refactoring-tools
liquigraph
Migrations for Neo4j
Stars: ✭ 122 (+662.5%)
Mutual labels:  refactoring, refactoring-tools
All Repos
Clone all your repositories and apply sweeping changes.
Stars: ✭ 197 (+1131.25%)
Mutual labels:  refactoring
Learning Oop In Php
A collection of resources to learn object-oriented programming and related concepts for PHP developers.
Stars: ✭ 2,359 (+14643.75%)
Mutual labels:  refactoring
looong
Discovery of Long Parameter List
Stars: ✭ 16 (+0%)
Mutual labels:  refactoring
IntelliJDeodorant
The project is not actively supported.
Stars: ✭ 53 (+231.25%)
Mutual labels:  refactoring
Migration
《系统重构与迁移指南》手把手教你分析、评估现有系统、制定重构策略、探索可行重构方案、搭建测试防护网、进行系统架构重构、服务架构重构、模块重构、代码重构、数据库重构、重构后的架构守护
Stars: ✭ 2,753 (+17106.25%)
Mutual labels:  refactoring
awesome-programming-books
List of good programming books for beginners and professionals
Stars: ✭ 68 (+325%)
Mutual labels:  refactoring
rubocop-auto-correct
Auto-correct ruby source code by using rubocop in Atom.
Stars: ✭ 29 (+81.25%)
Mutual labels:  refactoring
atom-refactoring
Atom package that provides refactoring capabilities for your PHP source code.
Stars: ✭ 16 (+0%)
Mutual labels:  refactoring
Escape From Callback Mountain
Example Project & Guide for mastering Promises in Node/JavaScript. Feat. proposed 'Functional River' pattern
Stars: ✭ 249 (+1456.25%)
Mutual labels:  refactoring
Refactor Nrepl
nREPL middleware to support refactorings in an editor agnostic way
Stars: ✭ 213 (+1231.25%)
Mutual labels:  refactoring
refren
A language agnostic, code-style aware, refactoring/renaming tool.
Stars: ✭ 19 (+18.75%)
Mutual labels:  refactoring
Abracadabra
Automated refactorings for VS Code (JS & TS) ✨ It's magic ✨
Stars: ✭ 204 (+1175%)
Mutual labels:  refactoring
pro.fessional.wings
WingsBoot=BKB+飞鞋+SpringBoot。其核心价值是:①使团队快速实现业务目标;②快速偿还技术债务;③安全的面向程序和业务重构。
Stars: ✭ 78 (+387.5%)
Mutual labels:  refactoring
Js Refactor
JS Refactoring tool for Visual Studio Code
Stars: ✭ 195 (+1118.75%)
Mutual labels:  refactoring
Best Of Python Dev
🏆 A ranked list of awesome python developer tools and libraries. Updated weekly.
Stars: ✭ 243 (+1418.75%)
Mutual labels:  refactoring
redux-usage-report
A Redux Devtools monitor to audit your app's usage of the store
Stars: ✭ 41 (+156.25%)
Mutual labels:  refactoring
refactoringtopatterns
A place to practice Refactoring To Patterns that Kerievsky wrote about in his book
Stars: ✭ 46 (+187.5%)
Mutual labels:  refactoring

Seamer - gets your legacy code under test

Build Status

Seamer aims at making it easy for you to create characterization tests.

Usage

Suppose you have a code like the following:

String result = someReallyComplicatedLegacyMethod(String param1, Integer param2);

doSometingWith(result);

You have no idea what someReallyComplicatedLegacyMethod is doing so you want to refactor it safely. This is where Seamer comes in handy. Seamer allows you to intercept the method and record all invocations and results like the following:

String result = Seamer.create()
    .define("MySeam",  // this is just an id of your choice
        (InvokableWith2Arguments<String, Integer, String>) this::someReallyComplicatedLegacyMethod)
    .invokeAndRecord(param1, param2);

doSometingWith(result);

Now the code still does the same thing, plus it will record all invocations of the Seam.

Taking advantage of closures

As the lambda expression results in a closure that captures its surrounding state, the recordings of this seam will be repeatable even if they depend on surrounding state. Suppose the method is incrementing an int field of the surrounding class, that side effect will be repeated when we verify the seam.

Check out the ClosureSeamTest which demonstrates this behavior.

Recording some invocations

You may now run your application. You would click around the UI and have this thing invoked a couple times with realistic arguments. Or, you would record some invocations using the following code.

// shuffles given argument candidates, records all possible scenarios and its results.
Seamer.create()
    .customRecordings("MySeam")
    .addArgCandidates(0, "hello", "world", null)
    .addArgCandidates(1, () -> asList(1, 2, 3))
    .shuffleArgsAndExecute();

Verifying the Seam in a test-harness

You may now setup a test that replays all recorded invocations and verifies if the code is still doing what it is supposed to do.

Verifying the results using Object equality
@Test
void verify_everything_still_works() {
    Seamer.create().get("MySeam").verify();
}
Verifying the results comparing field by field
@Test
void verify_everything_still_works() {
    Seamer.create().get("MySeam").verifyComparingFields();
}
Verifying the results comparing the toString representations
@Test
void verify_everything_still_works() {
    Seamer.create().get("MySeam").verifyComparingToString();
}
Verifying the results providing a custom comparator
@Test
void verify_everything_still_works() {
    // lambda signature:
    // BiConsumer<ObjectAssert<T>, Object> verification
    Seamer.create().get("MySeam").verify(AbstractAssert::isEqualTo);
}

And that's it. you may now refactor the code using this test as a feedback tool.

Persistence Configuration

The default

By default Seamer is using a file-based persistence that stores its data into src/test/java/seamer

Custom Path

If you wanted to store Seamers recorded data somewhere else, just define the path like so

Seamer.create("/tmp/seamer")
Sqlite

If you wanted to store Seamers recorded data in an Sqlite DB, create Seamer like the following

Seamer.create(new SqlitePersistence("jdbc:sqlite:/tmp/seamer"))

or even shorter:

Seamer.create(SqlitePersistence.atTmp())

Create a seam via Proxy

Spring+AspectJ
public static class AspectJDemo {

    @Seam("MySeam")
    public String legacyMethod(String arg1, Integer arg2) {
        return arg1 + arg2;
    }
}

Make sure to enable the SeamerAspect

@EnableAspectJAutoProxy
@Configuration
class SeamerConfig {

    @Bean
    public SeamerAspect seamerAspect() {
        return new SeamerAspect();
    }   
}
cglib
SeamerCglibFactory.createProxySeam(ClassCaputringTheSeam.class, "legacyMethod", "MySeam")

Limitations

Seamer needs objects to have at least private no-arg constructors for deserialization. final fields that are initialized in another no-arg constructor won't be able to be properly initialized.

TBD, allow users to register ObjectInstantiators

Suture

This tool is inspired by https://github.com/testdouble/suture which does a similar thing in ruby.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].