Coder Social home page Coder Social logo

autoparams / autoparams Goto Github PK

View Code? Open in Web Editor NEW
340.0 19.0 57.0 1.01 MB

Enhance your TDD experience! AutoParams is a versatile test data generator designed for parameterized tests in Java and Kotlin, drawing inspiration from AutoFixture.

License: MIT License

Java 90.51% Kotlin 9.49%
unit-testing tdd junit5 java parameterized-tests kotlin

autoparams's Introduction

AutoParams

CI Publish

AutoParams is an arbitrary test data generator designed for parameterized tests in Java, drawing inspiration from AutoFixture.

Manually configuring test data can be cumbersome, especially when certain data is necessary but not critical to a specific test. AutoParams eliminates this hassle by automatically generating test arguments for your parameterized methods, allowing you to focus more on your domain-specific requirements.

Using AutoParams is straightforward. Simply annotate your parameterized test method with the @AutoSource annotation, in the same way you would use the @ValueSource or @CsvSource annotations. Once this is done, AutoParams takes care of generating appropriate test arguments automatically.

@ParameterizedTest
@AutoSource
void parameterizedTest(int a, int b) {
    Calculator sut = new Calculator();
    int actual = sut.add(a, b);
    assertEquals(a + b, actual);
}

In the example above, the automatic generation of test data by AutoParams can potentially eliminate the need for triangulation in tests, streamlining the testing process.

AutoParams also simplifies the writing of test setup code. For instance, if you need to generate multiple review entities for a single product, you can effortlessly accomplish this using the @Freeze annotation.

@AllArgsConstructor
@Getter
public class Product {
    private final UUID id;
    private final String name;
    private final BigDecimal priceAmount;
}
@AllArgsConstructor
@Getter
public class Review {
    private final UUId id;
    private final Product product;
    private final String comment;
}
@ParameterizedTest
@AutoSource
void testMethod(@Freeze Product product, Review[] reviews) {
    for (Review review : reviews) {
        assertSame(product, review.getProduct());
    }
}

That's cool!

Requirements

  • JDK 1.8 or higher

Install

Maven

For Maven, you can add the following dependency to your pom.xml:

<dependency>
  <groupId>io.github.autoparams</groupId>
  <artifactId>autoparams</artifactId>
  <version>8.3.0</version>
</dependency>

Gradle

For Gradle, use:

testImplementation 'io.github.autoparams:autoparams:8.3.0'

Features

Support for Primitive Types

AutoParams effortlessly generates test arguments for primitive data types, such as booleans, integers, and floats.

@ParameterizedTest
@AutoSource
void testMethod(boolean x1, byte x2, int x3, long x4, float x5, double x6, char x7) {
}

Handling Simple Objects

The framework also provides test arguments in the form of simple objects like Strings, UUIDs, and BigIntegers.

@ParameterizedTest
@AutoSource
void testMethod(String x1, UUID x2, BigInteger x3) {
}

Enum Type Support

Enum types are seamlessly integrated as well. AutoParams will randomly select values from the enum for test arguments.

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY
}
@ParameterizedTest
@AutoSource
void testMethod(Day arg) {
}

Generation of Complex Objects

For more complicated objects, AutoParams utilizes the public constructor with arbitrary arguments to generate test data. If the class has multiple public constructors, the framework will select the one with the fewest parameters to initialize the object.

@AllArgsConstructor
@Getter
public class ComplexObject {
    private final int value1;
    private final String value2;
}
@ParameterizedTest
@AutoSource
void testMethod(ComplexObject arg) {
}

Constructor Selection Policy

When AutoParams generates objects of complex types, it adheres to the following selection criteria for constructors:

  1. Priority is given to constructors that are annotated with the @ConstructorProperties annotation.
  2. If no such annotation is present, AutoParams will choose the constructor with the fewest parameters.
@Getter
public class ComplexObject {

    private final int value1;
    private final String value2;
    private final UUID value3;

    @ConstructorProperties({"value1", "value2, value3"})
    public ComplexObject(int value1, String value2, UUID value3) {
        this.value1 = value1;
        this.value2 = value2;
        this.value3 = value3;
    }

    @ConstructorProperties({"value1", "value2"})
    public ComplexObject(int value1, String value2) {
        this(value1, value2, null);
    }

    public ComplexObject(int value1) {
        this(value1, null, null);
    }
}
@ParameterizedTest
@AutoSource
void testMethod(ComplexObject arg) {
    assertNotNull(object.getValue2());
    assertNull(object.getValue3());
}

Support for Generic Types

AutoParams is capable of generating objects of generic types, adhering to its constructor selection policy. When dealing with generic types, AutoParams employs a public constructor with arbitrary arguments for object creation. If multiple public constructors are available, the framework opts for the one with the fewest parameters.

@AllArgsConstructor
@Getter
public class GenericObject<T1, T2> {
    private final T1 value1;
    private final T2 value2;
}
@ParameterizedTest
@AutoSource
void testMethod(
    GenericObject<String, ComplexObject> arg1,
    GenericObject<UUID, GenericObject<String, ComplexObject>> arg2) {
}

Support for Collection Types

AutoParams offers extensive support for various collection types, enhancing its utility for more complex testing scenarios.

Arrays

AutoParams automatically generates array instances, each containing three elements by default.

@ParameterizedTest
@AutoSource
void testMethod(int[] array1, String[] array2) {
}

List Types

Both the List<E> interface and the ArrayList<E> class are supported. AutoParams generates list objects that contain a few elements.

@ParameterizedTest
@AutoSource
void testMethod(List<String> list, ArrayList<UUID> arrayList) {
}

Set Types

AutoParams also supports the Set<E> interface and the HashSet<E> class, generating set objects that contain a few elements.

@ParameterizedTest
@AutoSource
void testMethod(Set<String> set, HashSet<UUID> hashSet) {
}

Map Interface

Support extends to the Map<K, V> interface and the HashMap<K, V> class as well. AutoParams generates map objects containing a few key-value pairs.

@ParameterizedTest
@AutoSource
void testMethod(Map<String, ComplexObject> map, HashMap<UUID, ComplexObject> hashMap) {
}

This comprehensive support for collection types makes AutoParams an exceptionally versatile tool for generating test data, accommodating a wide variety of data structures to ensure thorough testing.

Support for Stream Types

AutoParams extends its versatility by offering support for various types of stream interfaces, further broadening your testing capabilities.

Generic Stream Interface

AutoParams supports the generic Stream<T> interface and generates stream objects containing a few elements.

@ParameterizedTest
@AutoSource
void testMethod(Stream<ComplexObject> stream) {
}

Stream Interfaces of Primitive Types

AutoParams also accommodates stream interfaces that are specific to primitive types, such as IntStream, LongStream, and DoubleStream.

@ParameterizedTest
@AutoSource
void testMethod(IntStream arg1, LongStream arg2, DoubleStream arg3) {
}

This added layer of support for stream types allows AutoParams to be a comprehensive solution for generating test data across a wide range of data structures and types, making your testing more robust and efficient.

Repeat Feature

The @Repeat annotation in AutoParams provides you with a straightforward way to repeat a unit test multiple times, each time with newly generated, arbitrary test data. This can be particularly useful when you want to ensure that a piece of code functions as expected across a variety of input conditions.

How to Use

To use the @Repeat feature, you'll need to attach the @Repeat annotation alongside the @AutoSource annotation on your parameterized test method. The repeat property of the @Repeat annotation takes an integer value, specifying the number of times the test should be repeated.

Here's an example:

@ParameterizedTest
@AutoSource
@Repeat(10)
void testMethod(int a, int b) {
    // This test method will be executed ten times with different sets of 'a' and 'b' each time.
    Calculator sut = new Calculator();
    int actual = sut.add(a, b);
    assertEquals(a + b, actual);
}

In the example above, the test method testMethod will run ten times. Each run will have a new set of randomly generated values for a and b, thanks to the @AutoSource annotation. The @Repeat(10) ensures that this cycle happens ten times, enhancing the test coverage.

Benefits

  1. Diverse Test Data: The @Repeat feature allows you to test your methods across a wider range of automatically generated input values.
  2. Reduced Manual Effort: There's no need to manually specify multiple sets of test data or create loops within your tests.
  3. Increased Test Robustness: Repeating tests multiple times with varying data can uncover edge cases and improve the reliability of your software.

By using the @Repeat feature, you can increase the comprehensiveness and reliability of your test suite with minimal additional effort.

@Freeze Annotation

AutoParams provides a @Freeze annotation to let you fix the value of a generated argument. Once an argument is fixed using the @Freeze annotation, @AutoSource will reuse this fixed value for any subsequent parameters of the same type within the same test method.

How to Use

Simply decorate the parameter in your test method with the @Freeze annotation. This will instruct AutoParams to keep that parameter's value constant while generating new values for other parameters.

Here's an example:

@AllArgsConstructor
@Getter
public class ValueContainer {
    private final String value;
}
@ParameterizedTest
@AutoSource
void testMethod(@Freeze String arg1, String arg2, ValueContainer arg3) {
    assertEquals(arg1, arg2);
    assertEquals(arg1, arg3.getValue());
}

In the above example, the value of arg1 is fixed by the @Freeze annotation. The test verifies that arg1 is equal to arg2 and also equal to the value property of arg3. As a result, arg1, arg2, and arg3.getValue() will all contain the same string value, thanks to the @Freeze annotation.

Benefits

  1. Consistency: @Freeze ensures that certain values stay constant, making it easier to verify relations between different parameters in your tests.
  2. Simpler Test Logic: Reusing a fixed value across multiple parameters simplifies your test logic and makes your tests easier to read and maintain.
  3. Control Over Randomness: While @AutoSource provides the benefit of random value generation, @Freeze gives you control when you need specific values to be consistent throughout a test.

The @Freeze annotation is a powerful feature for scenarios where you need to maintain the consistency of certain test parameters while still benefiting from the randomness and coverage offered by AutoParams.

@ValueAutoSource Annotation

The @ValueAutoSource annotation combines the capabilities of @ValueSource and @AutoSource, providing a more flexible way to generate test arguments. Specifically, it assigns a predefined value to the first parameter and then generates arbitrary values for the remaining parameters.

How to Use

To employ the @ValueAutoSource annotation, add it above your test method and specify the values for the first parameter using the attributes like strings, ints, etc., just as you would with @ValueSource.

Here's an example:

@ParameterizedTest
@ValueAutoSource(strings = {"foo"})
void testMethod(String arg1, String arg2) {
    assertEquals("foo", arg1);
    assertNotEquals(arg1, arg2);
}

In this case, arg1 is set to "foo", while arg2 will receive an automatically generated value. The test confirms that arg1 is exactly "foo" and that arg2 is different from arg1.

Compatibility with @Freeze

The @ValueAutoSource annotation is fully compatible with the @Freeze annotation, allowing you to fix certain parameter values while also providing predefined values for others.

Here is how they can work together:

@AllArgsConstructor
@Getter
public class ValueContainer {
    private final String value;
}
@ParameterizedTest
@ValueAutoSource(strings = {"foo"})
void testMethod(@Freeze String arg1, String arg2, ValueContainer arg3) {
    assertEquals("foo", arg2);
    assertEquals("foo", arg3.getValue());
}

Benefits

  1. Combining Fixed and Random Values: @ValueAutoSource allows you to specify certain parameter values while letting AutoParams generate random values for others, offering the best of both worlds.
  2. Enhanced Flexibility: This feature grants you more control over test data, making it easier to cover a wide range of scenarios.
  3. Reduced Test Complexity: By combining the functionalities of @ValueSource and @AutoSource, you can simplify your test setup, making it easier to read and maintain.

The @ValueAutoSource annotation is a versatile addition to your testing toolkit, giving you greater control over test data while maintaining the benefits of automated random data generation.

@CsvAutoSource Annotation

The @CsvAutoSource annotation merges the features of @CsvSource and @AutoSource, providing you with the flexibility to specify CSV-formatted arguments for some parameters while generating random values for the remaining ones.

How to Use

To use @CsvAutoSource, annotate your test method with it and provide CSV-formatted input values for the initial set of parameters. The remaining parameters will be populated with automatically generated values.

Here's a simple example:

@ParameterizedTest
@CsvAutoSource({"16, foo"})
void testMethod(int arg1, String arg2, String arg3) {
    assertEquals(16, arg1);
    assertEquals("foo", arg2);
    assertNotEquals(arg2, arg3);
}

In this example, arg1 is set to 16 and arg2 is set to "foo" based on the provided CSV input. Meanwhile, arg3 will be assigned a randomly generated value. The test verifies that arg3 is different from arg2.

Compatibility with @Freeze

Just like @ValueAutoSource, @CsvAutoSource is fully compatible with the @Freeze annotation, allowing you to lock in values for specific parameters while also defining others via CSV.

Here's how they can work together:

@AllArgsConstructor
@Getter
public class ValueContainer {
    private final String value;
}
@ParameterizedTest
@CsvAutoSource({"16, foo"})
void testMethod(int arg1, @Freeze String arg2, ValueContainer arg3) {
    assertEquals("foo", arg3.getValue());
}

Benefits

  1. Dual Control: The ability to specify values via CSV for some parameters while allowing automatic random generation for others provides a high level of control over your test data.
  2. Streamlined Testing: By allowing both manual and automated data input, @CsvAutoSource simplifies the setup of complex tests.
  3. Flexible and Comprehensive: This feature offers you the flexibility to cover a broader range of test scenarios by mixing pre-defined and random values.

The @CsvAutoSource annotation is another versatile tool in your testing suite, adding both precision and coverage to your parameterized tests.

@MethodAutoSource Annotation

The @MethodAutoSource annotation blends the capabilities of @MethodSource and @AutoSource. This allows you to specify the names of factory methods for the initial set of parameters, while the remaining parameters are automatically populated with arbitrary values.

How to Use

To employ @MethodAutoSource, annotate your test method with it and specify the factory method that provides the argument values for the initial parameters. AutoParams will generate values for the remaining parameters.

Here's a straightforward example:

@ParameterizedTest
@MethodAutoSource("factoryMethod")
void testMethod(int arg1, String arg2, String arg3) {
    assertEquals(16, arg1);
    assertEquals("foo", arg2);
    assertNotEquals(arg2, arg3);
}

static Stream<Arguments> factoryMethod() {
    return Stream.of(Arguments.of(16, "foo"));
}

In this instance, arg1 and arg2 are set based on the values provided by the factoryMethod. arg3 will be assigned a randomly generated value, and the test confirms that arg3 is not equal to arg2.

Compatibility with @Freeze

The @MethodAutoSource annotation is also fully compatible with the @Freeze annotation. This lets you lock in values for particular parameters while dynamically populating others through a factory method.

Here's how it works together:

@AllArgsConstructor
@Getter
public class ValueContainer {
    private final String value;
}
@ParameterizedTest
@MethodAutoSource("factoryMethod")
void testMethod(int arg1, @Freeze String arg2, ValueContainer arg3) {
    assertEquals("foo", arg3.getValue());
}

static Stream<Arguments> factoryMethod() {
    return Stream.of(Arguments.of(16, "foo"));
}

Benefits

  1. Customizability: The ability to specify factory methods for some parameters allows for highly tailored test setups.
  2. Simplicity: With automatic value generation for remaining parameters, @MethodAutoSource alleviates the need for extensive manual data preparation.
  3. Flexibility: This feature allows you to test a wider range of scenarios by combining pre-defined and random values.

The @MethodAutoSource annotation enriches your testing toolkit, offering a balanced blend of control and randomness in your parameterized tests.

Setting the Range of Values

You can constrain the range of generated arbitrary values for certain parameters using the @Min and @Max annotations. These annotations allow you to set minimum and maximum bounds, respectively, ensuring that the generated values fall within the specified range.

How to Use

To set the range for a parameter, simply annotate it with @Min to define the minimum value and @Max to define the maximum value.

Here's an example:

@ParameterizedTest
@AutoSource
void testMethod(@Min(1) @Max(10) int value) {
    assertTrue(value >= 1);
    assertTrue(value <= 10);
}

In this test, the value parameter will always be an integer between 1 and 10, inclusive.

Supported types

The @Min and @Max annotations are compatible with the following types:

  • byte
  • Byte
  • short
  • Short
  • int
  • Integer
  • long
  • Long
  • float
  • Float
  • double
  • Double

Benefits

  1. Controlled Randomness: These annotations help you fine-tune the scope of randomness, allowing for more targeted and meaningful tests.
  2. Reduced Test Flakiness: By constraining the range of values, you reduce the risk of encountering edge cases that could make your tests flaky.
  3. Enhanced Readability: Using @Min and @Max makes it clear to readers what range of values are being tested, thereby improving the readability and maintainability of your test code.

By using the @Min and @Max annotations in conjunction with @AutoSource, you can achieve a balanced mix of randomness and predictability, making your parameterized tests both versatile and reliable.

@Customization Annotation

The @Customization annotation allows you to tailor the generation of test data according to specific business rules or requirements. This powerful feature integrates seamlessly with the AutoParams framework, offering the flexibility to apply custom logic to your parameterized tests.

Business Rule Example

Let's consider a Product entity, which has some business rules to follow:

  • The listPriceAmount must be greater than or equal to 100
  • The listPriceAmount must be less than or equal to 1000
  • A 10% discount should be offered, reflected in sellingPriceAmount
@AllArgsConstructor
@Getter
public class Product {
    private final UUID id;
    private final String name;
    private final BigDecimal listPriceAmount;
    private final BigDecimal sellingPriceAmount;
}

Customizing Object Generation

You can implement these rules using the Customizer interface:

public class ProductGenerator extends ObjectGeneratorBase<Product> {

    @Override
    protected Product generateObject(ObjectQuery query, ResolutionContext context) {
        UUID id = context.resolve(UUID.class);
        String name = context.resolve(String.class);

        ThreadLocalRandom random = ThreadLocalRandom.current();
        BigDecimal listPriceAmount = new BigDecimal(random.nextInt(100, 1000 + 1));
        BigDecimal sellingPriceAmount = listPriceAmount.multiply(new BigDecimal(0.9));

        return new Product(id, name, listPriceAmount, sellingPriceAmount);
    }
}

Applying Customization to Test Method

Annotate your test method to apply the customization:

@ParameterizedTest
@AutoSource
@Customization(ProductGenerator.class)
void testMethod(Product arg) {
    assertTrue(arg.getSellingPriceAmount().compareTo(arg.getListPriceAmount()) < 0);
}

Composite Customizer

You can also create a composite customizer to apply multiple custom rules:

public class DomainCustomizer extends CompositeCustomzer {
    public DomainCustomizer() {
        super(
            new EmailGenerator(),
            new UserGenerator(),
            new SupplierGenerator(),
            new ProductGenerator()
        );
    }
}

And use it like this:

@ParameterizedTest
@AutoSource
@Customization(DomainCustomizer.class)
void testMethod(Email email, User user, Supplier supplier, Product product) {
}

Settable Properties

If your object follows the JavaBeans spec and has settable properties, you can use InstancePropertyCustomizer:

@Getter
@Setter
public class User {
    private Long id;
    private String name;
}
@ParameterizedTest
@AutoSource
@Customization(InstancePropertyWriter.class)
void testMethod(User user) {
    assertNotNull(user.getId());
    assertNotNull(user.getName());
}

Customization Scoping

The @Customization annotation can also be applied to individual parameters within a test method. Once applied, the customization will affect all following parameters, unless overridden.

This feature provides a nuanced approach to data generation, enabling highly specialized and context-sensitive test scenarios.

autoparams-mockito

autoparams-mockito is an extension of the AutoParams library that facilitates the creation of mocks for interfaces and abstract classes using Mockito, a popular mocking framework in Java. By integrating these two libraries, you can seamlessly generate mock objects for your tests.

Install

Maven

For Maven, you can add the following dependency to your pom.xml:

<dependency>
  <groupId>io.github.autoparams</groupId>
  <artifactId>autoparams-mockito</artifactId>
  <version>8.3.0</version>
</dependency>

Gradle

For Gradle, use:

testImplementation 'io.github.autoparams:autoparams-mockito:8.3.0'

Generating Test Doubles with Mockito

Consider a situation where you have an interface that abstracts certain services:

public interface Dependency {

    String getName();
}

You also have a system that relies on this Dependency interface:

public class SystemUnderTest {

    private final Dependency dependency;

    public SystemUnderTest(Dependency dependency) {
        this.dependency = dependency;
    }

    public String getMessage() {
        return "Hello " + dependency.getName();
    }
}

To generate mock objects for interfaces or abstract classes, the autoparams-mockito extension provides the MockitoCustomizer. When you decorate your test method with @Customization(MockitoCustomizer.class), the @AutoSource annotation will employ Mockito to create mock values for the specified parameters.

Here's how you can apply this in practice:

@ParameterizedTest
@AutoSource
@Customization(MockitoCustomizer.class)
void testUsingMockito(@Freeze Dependency stub, SystemUnderTest sut) {
    when(stub.getName()).thenReturn("World");
    assertEquals("Hello World", sut.getMessage());
}

In the above example:

  • The stub argument is a mock generated by Mockito, thanks to the MockitoCustomizer.
  • The @Freeze annotation ensures that this mock object (stub) is reused as a parameter for the construction of the SystemUnderTest object (sut).

This integration simplifies the creation of mock objects for parameterized tests, making testing more efficient and straightforward.

autoparams-lombok

autoparams-lombok is an extension for the AutoParams library that makes it easier to work with Project Lombok, a library that reduces boilerplate code in Java applications.

Install

Maven

For Maven, you can add the following dependency to your pom.xml:

<dependency>
  <groupId>io.github.autoparams</groupId>
  <artifactId>autoparams-lombok</artifactId>
  <version>8.3.0</version>
</dependency>

Gradle

For Gradle, use:

testImplementation 'io.github.autoparams:autoparams-lombok:8.3.0'

BuilderCustomizer

When working with @Builder annotation, you can use the BuilderCustomizer to facilitate generating arbitrary objects for your tests. Here's an example:

Suppose you have a User class like so:

import lombok.Builder;
import lombok.Getter;

@Builder
@Getter
public class User {
    private Long id;
    private String name;
    private String email;
}

You can use BuilderCustomizer to create objects of type User for your tests:

@ParameterizedTest
@AutoSource
@Customization(BuilderCustomizer.class)
void testMethod(User user) {
    assertThat(arg.getId()).isNotNull();
    assertThat(arg.getName()).isNotNull();
    assertThat(arg.getEmail()).isNotNull();
}

Custom Method Names

If you've customized the builder method names using builderMethodName and buildMethodName in your Lombok @Builder, you'll need to create a subclass of BuilderCustomizer to handle the custom names:

import lombok.Builder;
import lombok.Getter;

@Builder(builderMethodName = "getBuilder", buildMethodName = "createUser")
@Getter
public class User {
    private Long id;
    private String name;
    private String email;
}

Here's how you can extend BuilderCustomizer:

public class UserBuilderCustomizer extends BuilderCustomizer {

    public UserBuilderCustomizer() {
        super("getBuilder", "createUser");
    }
}

Now, you can use your customized UserBuilderCustomizer in your tests:

@ParameterizedTest
@AutoSource
@Customization(UserBuilderCustomizer.class)
void testMethod(User user) {
    assertThat(arg.getId()).isNotNull();
    assertThat(arg.getName()).isNotNull();
    assertThat(arg.getEmail()).isNotNull();
}

This allows you to keep the benefits of using @Builder annotation while gaining the automatic generation capabilities provided by AutoParams.

autoparams-kotlin

autoparams-kotlin is an extension designed to simplify and enhance the experience when working with Kotlin.

Install

Maven

For Maven, you can add the following dependency to your pom.xml:

<dependency>
  <groupId>io.github.autoparams</groupId>
  <artifactId>autoparams-kotlin</artifactId>
  <version>8.3.0</version>
</dependency>

Gradle

For Gradle, use:

testImplementation 'io.github.autoparams:autoparams-kotlin:8.3.0'

@AutoKotlinSource Annotation

Consider the example of a Point data class in Kotlin:

data class Point(val x: Int = 0, val y: Int = 0)

In typical test scenarios, you might want to ensure the parameters passed to your test methods aren't just default or predictable values. The autoparams-kotlin extension can assist in such cases.

Below is a demonstration using the @AutoKotlinSource annotation provided by autoparams-kotlin. This annotation automatically generates random parameters for your test methods:

@ParameterizedTest
@AutoKotlinSource
fun testMethod(point: Point) {
    assertThat(point.x).isNotEqualTo(0)
    assertThat(point.y).isNotEqualTo(0)
}

In this example, the testMethod doesn't receive a hardcoded Point object. Instead, thanks to the @AutoKotlinSource annotation, a randomized Point object is passed in, making the test more robust by ensuring it isn't biased by predetermined values.

autoparams's People

Contributors

1chz avatar amondnet avatar cg4jins avatar ehdrylang avatar gyuwon avatar jaceshim avatar jujinpark avatar jujubebat avatar jwchung avatar lichking-lee avatar lulumeya avatar nallwhy avatar raphael-shin avatar rebwon avatar sogoagain avatar wplong11 avatar yoonpunk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

autoparams's Issues

Customization API 공개

제공되는 기능만으로는 실무의 다양한 도메인을 반영하는 데이터를 생성하기 어렵다. 결국 각 사용자의 도메인을 반영하는 데이터를 생성할 수 있도록 customization API가 제공되어야 한다.

하지만 customization API는 강력한 기능이기 때문에 breaking change를 막으려면 초기에 깊이 고민하고 설계되어야 한다. 이 이슈에서 customization API와 관련된 아이디어를 수집한다.

DoubleStream 값 생성 지원

@AutoSource가 아래 예저 코드처럼 DoubleStream 형식을 지원하도록 한다.

@ParameterizedTest
@AutoSource
void test(DoubleStream doubleStream) {
}

생성된 DoubleStream 개체는 3개 요소를 제공한다.

java.net.URI 생성 지원

@AutoSource가 아래 예저 코드처럼 java.net.URI 형식을 지원하도록 한다,.

@ParameterizedTest
@AutoSource
void autoParamsSupportsUriValue(URI uri) {
}

byte 값 생성 지원

@AutoSource가 아래 예저 코드처럼 byte, Byte 형식을 지원하도록 한다,.

@ParameterizedTest
@AutoSource
void test(byte b1, Byte b2) {
}

생성자가 N개인 클래스의 생성자 선택 전략 구현

ComplexObjectConstructorResolver 에서는 findFirst 로 생성자를 선택하고 있어서 어떤 생성자가 선택될지 알 수 없습니다.
#33 (comment)

우선 1개의 생성자를 가진 클래스만 객체 생성하는걸로 지원하는걸로 제약한 후 고민하는 것도 좋을거 같습니다.

생성자 선택 전략은 다음을 참고할 수 있습니다.
(아래 방법을 사용하자는 것은 아니고 다른 라이브러리에서 사용하는 방법 참고용 입니다.)

  1. Jackson @JsonCreator
  2. java beans @ConstructorProperties
    • java beans 기본 애노테이션으로 생성자와 args 를 지정한다.
    • Jackson 에서 @JsonCreator 외에 @ConstructorProperties 를 인식해서 복호화를 지원한다.
    • lombok.config 에 lombok.anyConstructor.addConstructorProperties=true 를 설정하면 lombok @AllArgsConstructor 등으로 자동 생성되는 생성자에 @ConstructorProperties 를 자동으로 선언해준다.
    • https://blog.benelog.net/jackson-with-constructor.html
  3. Spring Data @PersistenceConstructor

위에 3가지 전부 마음에 드는 방법은 아닙니다만, 2번 @ConstructorProperties 를 지원하는게 가장 무난하다고 생각됩니다.
애노테이션을 피하기 위해 사용자가 Constructor 를 선택하는 DSL 같은거를 만들어주거나,
ComplexObjectConstructorResolver 의 확장 포인트를 열어주는 방법이 필요할 수도 있습니다.

이 이슈는 다양한 객체 생성 전략들을 제공하는 방법들을 고민하는 이슈로 확장될 수 있습니다.

JaCoCo 설정

Java Code Coverage Library 인 JaCoCo 설정 여부를 논의합니다.
https://www.eclemma.org/jacoco/

  • 테스트 커버리지 리포트가 필요한가?
  • JaCoCo 를 적용한다면, 결과 리포트 화면 출력을 Github Actions 와 어떻게 연동해야 하나?

SNAPSHOT 배포

릴리즈 후에 버전 정보를 {nextVersion}-SNAPSHOT 으로 바꾸고,

main 에 PR 이 merge 되면 github Actions 에서 SNAPSHOT 을 자동으로 배포해주면 어떨까요?

Custom Generator 지원

비지니스에서 필요한 특수한 값의 형식과 범위를 일반적인 옵션의 제공으로 커버하기에는 무리가 있을 것으로 생각됩니다.
사용자에게 ObjectGenerator 를 구현할 수 있도록 제공한다면 좋지 않을까 생각합니다.

build.gradle에 maven repository 추가

현재 build.gradle에 maven repository는 jcenter() 만 설정되어 있으며
해당 repository가 장애상황에서 AutoParams build를 실행하면 의존 라이브러리 호출시 에러가 발생할 여지가 있음
이에 maven repository를 추가 설정한다. ex) mavenCentral

테스트 클래스 재편

테스트 케이스가 많아지면서, 테스트 클래스로 분류하는 작업이 필요합니다. 다음과 같이 값 타입 기준으로 테스트 클래스를 만들어 테스트 케이스를 분류하면 어떨까 합니다.

  • IntergerGenerationSpecs
  • StringGenerationSpecs
  • ListGenerationSpecs
  • CollectionGenerationSpecs

ObjectGenerator 인터페이스에 템플릿 메소드 패턴 도입 제안

ObjectGenerator 인터페이스를 구현하는 모든 클래스는 generate를 구현해야 하고
구현은 은닉대상이 맞으나 현재 구현된 모든 Generator가 Object를 생성하기 위해 수행되는 공통 행위는 두가지 입니다.

  • 대상 type 확인
  • Object 생성

type을 체크하고 factory를 호출하는 이 로직 부분을 ObjectGenerator 공개 책임으로 도출하고
이를 Java에서 제공하는 default method를 통해서 ObjectGenerator 인터페이스 내에 Object생성 rule을 일반화 시키는건 어떨까요?

interface ObjectGenerator {

    ThreadLocalRandom RANDOM = ThreadLocalRandom.current();

    default Optional<Object> generate(ObjectQuery query, ObjectGenerationContext context) {
        if (isSuitableType(query)) {
            return factory(query, context);
        }
        return Optional.empty();
    }

    boolean isSuitableType(ObjectQuery query);

    Optional<Object> factory(ObjectQuery query, ObjectGenerationContext context);
}

아니면 ObjectGenerator 는 현재처럼 유지하되
AbstractObjectGenerator 를 도입하여 아래와 같이 처리하는건 어떨까요?

abstract class AbstractObjectGenerator implements ObjectGenerator {

    @Override
    public Optional<Object> generate(ObjectQuery query,
        ObjectGenerationContext context) {
        if (isSuitableType(query)) {
            return factory(query, context);
        }
        return Optional.empty();
    }

    abstract boolean isSuitableType(ObjectQuery query);

    abstract Optional<Object> factory(ObjectQuery query, ObjectGenerationContext context);
}

개인적으론 변경의 여파가 적은 후자가 더 좋을것 같습니다.

고견 부탁드립니다.

P.S. 본인은 패턴 빠는 아닙니다. 😇

@Fixed 애노테이션 추가

다음 테스트가 통과하도록 @Fixed 애노테이션을 추가한다.

@Fixed 애노테이션은 AutoFixture의 [Frozen] 특성과 유사한 기능을 제공한다.

class ValueContainer {

    private final String value;

    public ValueContainer(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

}

@ParameterizedTest
@AutoSource
void sut_reuses_generated_value_for_same_type(@Fixed String value, ValueContainer container) {
    assertEquals(value, container.getValue());
}

IntStream 지원

@AutoSource가 아래 예제 코드처럼 IntStream 형식을 지원하도록 한다,.

@ParameterizedTest
@AutoSource
void testWithIntStream(IntStream stream) {
}

생성된 IntStream 개체는 3개의 int 요소를 제공한다.

@Nullable 컨벤션 논의

  • null 이 반환 가능하거나, null 이 파라미터로 들어올 수 있는 대상에 @javax.annotation.Nullable 을 선언하는 컨벤션에 대해 논의합니다.
  • @Nullable 이 선언된 반환 값을 검사 없이 사용하거나, @Nullable 이 선언되지 않은 파라미터에 null 을 넘기면, SpotBugs 에서 NPE 가능성을 분석하고 레포트합니다.
  • 추가로 kotlin 에서 java library 를 사용할 때 @Nullable 이 붙은 대상은 ? 로 타입 추론 및 컴파일 지원 합니다.

@Nullable 컨벤션에 동의가 되면 아래 JSR 305 라이브러리 의존성을 추가합니다.
https://mvnrepository.com/artifact/com.google.code.findbugs/jsr305

@CsvAutoSource 애노테이션 추가

@CsvSource@AutoSource를 통합한 기능을 구현한다. @CsvAutoSource를 사용하는 테스트 메서드의 인자는 애노테이션 속성으로 지정된 값이 우선 대입되고 지정되지 않은 값들은 자동으로 생성된다.

@ParameterizedTest
@CsvAutoSource({ "1, foo", "2, bar" })
void testMethod(int specifiedInt, String specifiedString, UUID generatedUUID) {
    // This test method is performed two times with { "1, foo", "2, bar" }.
}

테스트 케이스

@ParameterizedTest
@CsvAutoSource({ "1, foo" })
void sut_correctly_fills_arguments(int value1, String value2) {
    assertEquals(1, value1);
    assertEquals("foo", value2);
}
@ParameterizedTest
@CsvAutoSource({ "1, foo" })
void sut_correctly_fills_forepart_arguments(int value1, String value2, UUID value3) {
    assertEquals(1, value1);
    assertEquals("foo", value2);
}
@ParameterizedTest
@CsvAutoSource({ "1, foo" })
void sut_arbitrarily_generates_remaining_arguments(int value1, String value2, UUID value3, UUID value4) {
    assertNotNull(value3);
    assertNotNull(value4);
    assertNotEquals(value3, value4);
}

반복 실행 지원

@AutoSource가 하나 이상의 테스트 데이터 집합을 대상으로 테스트 메서드를 실행하도록 설계를 확장한다.

@ParameterizedTest
@AutoSource(repeat = 10)
void myTestMethod(int x) {
}

기본 생성자 런타임 피드백

클래스가 매개변수가 없는 기본 생성자를 포함해 여러 개의 공개(public) 생성자를 가질 때 #43 논리에 의해 기본 생성자가 선택되는데, 프레임워크에 의해 기본 생성자가 유도된 상황이라면 AutoParams가 기본 생성자를 선택하는 것은 바람직하지 않을 수 있다.

이 때 런타임 피드백(예외)를 제공할 필요가 있는지 논의할 필요가 있다.

다양한 객체 생성 전략 지원

#56 (comment) 에서 객체 생성 방법에 대한 이야기로 확장되고 있어서 별도의 이슈로 만듭니다.

사용자들이 Class 를 정의하고 객체를 생성하는 방법은 다양한 방법이 있을 수 있습니다.

앞서 언급한 시나리오처럼 JPA 로 복원되는 시나리오, Jackson 에 의해 복원되는 시나리오, mybatis 에 의해 복원되는 시나리오도 있습니다. 이런 케이스의 클래스의 객체 복원의 주요 사용자는 JPA, Jackson, mybatis 가 됩니다.

그래서 AutoParams 의 기능이 고도화 된다면 특정 프레임워크/라이브러리도 하나의 사용자로서 그 특징에 맞는 객체 생성 전략을 선택해서 지원할 수 있게 할 필요도 있을 수 있을거 같습니다.

이 전략들을 구현하는 것보다 Customization API 공개 가 우선순위가 높다고 생각합니다.
그래서 Customization API 설계시 같이 검토해볼 수 있을거 같습니다.


아래는 제가 사내에서 만든 라이브러리에서 지원하는 객체 생성 전략들입니다.
간단히 참고만 하시기 바랍니다.

  1. 생성자 지원
    • 생성자로 객체 생성
  2. JavaBeans 스펙
    • 주요 사용자: mybatis 와 같이 JavaBeans 스펙을 강제하는 프레임워크 및 setter 를 사용하는 사용자들
    • public NoArgsConsturctor 로 객체를 생성하고 공개된 setter 들을 호출해 속성을 채운다,
  3. @Builder 스펙
    • 주요 사용자: lombok 의 @Builder 를 사용해 객체를 생성하는 사용자들
    • lombok 의 @Builder 의 스펙에 맞춰 Builder 생성, 속성 등록, build 호출로 객체 생성한다.
  4. Field Reflection
    • 주요 사용자: JPA 로 객체 복원
    • NoArgsConstructor 로 객체를 생성하고 Field Reflection 으로 속성 값을 채운다.
  5. Jackson
    • 주요 사용자: Jackson 을 사용해 JSON 을 객체로 Deserialize
    • 클래스 속성들 값을 생성 한 후 JSON 으로 만들고 Jackson 으로 Deserialize 해서 객체를 생성

@ValueAutoSource 애노테이션 추가

@ValueSource@AutoSource를 통합한 기능을 구현한다. @ValueAutoSource를 사용하는 테스트 메서드의 첫번째 인자는 애노테이션 속성으로 지정하고 두번째 인자부터는 자동으로 생성된다.

@ParameterizedTest
@ValueAutoSource(ints = { 1, 2, 3 })
void testMethod(int specifiedValue, String generatedString, UUID generatedUUID) {
    // This test method is performed three times with { 1, 2, 3 }.
}

BigInteger 생성 지원

#77 에서 처리되고 있습니다.

@AutoSource가 아래 예저 코드처럼 BigInteger 형식을 지원하도록 한다,.

@ParameterizedTest
@AutoSource
void test(BigInteger bigInteger) {
}

gradle test 실행 시 스타일 검사

@mhyeon-lee @jwChung PR 올려주시는 분들이 스타일이 깨지는 커밋을 만드시는 경우가 종종 있습니다. 이후에 스타일 수정 커밋을 만드십니다. 그래서 제가 빌드가 실패하는 커밋이 없도록 리베이스를 요청드리는데요, 아마 커밋 만들기 전에 테스트는 모두 실행하실 것 같으니 gradle test 실행 시 스타일 검사를 하도록 하면 어떨까요?

LocalDateTime, LocalDate 타입 지원이 필요합니다.

@ParameterizedTest
@AutoSource
void testWithLocalDateTime(LocalDateTime localDateTime) {
}

@ParameterizedTest
@AutoSource
void testWithLocalDate(LocalDate localDate) {
}

LocalDateTime, LocalDate는 현재 날짜, 시각을 반환해야 합니다.
LocalDateTime, LocalDate는 범위를 지정할 수 있어야 합니다.

gradle 멀티 모듈 구성

이후 kotlin 이나 특정 라이브러리 애노테이션 등을 서포트 하는 플러그인이 필요할 때 확장 모듈이 필요할 가능성이 높을거 같습니다.
별도의 repo 를 파는 것도 방법이지만, 하나의 repo 에서 multi module 을 구성하는게 장점이 더 많다고 생각됩니다.

현시점에서 멀티 모듈을 논의하는 시점이 이를 수도 있지만, kotlin 모듈의 필요성은 곧 이슈화 될거 같다는 생각도 들어서 미리 멀티 모듈로 구성을 해놓는건 어떨지 제안해봅니다.

ObjectGenerationContext.getGenerator -> generate로 변경

ObjectGenerationContext 클래스에 아래 메소드를 추가하면 objectGenerationContext.genearte(query) 와 같이 바로 사용할 수 있을 것 같은데요.

public Optional<Object> generate(ObjectQuery query) {
    return this.generator.generate(query, this);
}

objectGenerationContext.getGenerator().generate(query, objectGenerationContext) 이렇게 objectGenerationContext 를 지정하는 이유가 있을까요?

builder fix 기능 구현

아래 시나리오와 같이 특정 타입 범위 안에서 값을 고정 시키는 기능을 구현한다.

이 이슈는 특정 타입 범위란 점에서 #15(테스트 메소드 범위)와 차이를 보이며, #3 custom API 의 사용자 시나리오이다.

@ParameterizedTest
@AutoSource
public void test(Builder<Bar> barBuilder) {
    Bar bar = barBuilder.fix(int.class, 123).build();

    // bar.value == 123
    // bar.getFoo().getValues() == [123, 123, 123]
}

public class Foo {
    private final int[] values;

    public Foo(int[] values) {
        this.values = values;
    }

    ...
}

public class Bar {
    Foo foo;
    int value;

    public Bar(Foo foo, int value) {
        this.foo = foo;
        this.value = value;
    }

    ...
}

checkstyle 및 formatter 설정

  • checkstyle 및 formatter 설정에 대해 논의합니다.
  • 우선 checkstyle 및 formatter 가 필요할지에 대해 의견 부탁드립니다.
    • checkstyle 은 gradle 로 검사할 수 있지만, checkstyle 에 맞는 formatter 를 셋팅하는건 간단하지만은 않습니다.
    • 보통 Intellij formatter 로 맞추지만 vs code 등 다른 툴을 호환하려면 각 툴에 맞는 formatter 가 각각 정의되어야 합니다.

시간/시각 타입 값 생성 지원

#9 에 이어서 다양한 시간/시각 타입 값 생성을 지원합니다.

  • Date, Calendar, Instant, ZonedDateTime, ZoneId, Duration, Period, OffsetDateTime 등
@ParameterizedTest
@AutoSource
void test(Date date, Calendar cal, Instant in, ZonedDateTime zdt, ZoneId zoneId, Duration duration, Period period, OffsetDateTime offsetDateTime) {
}

시각

  • Date 값 생성
  • Calendar 값 생성
  • Instant 값 생성
  • ZonedDateTime 값 생성
  • OffsetDateTime 값 생성

시간

  • Duration 값 생성
  • Period 값 생성

Zone 정보

  • ZoneId 값 생성

CharacterGenerator 구현

char/Character 값 생성 기능을 구현합니다. 예상 테스트 시나리오는 다음과 같습니다.

@ParameterizedTest
@AutoSource
void sut_correctly_generates_characters(char char, Character character) {
    assertThat(character).isNotNull();
}

short 값 생성 지원

@AutoSource가 아래 예저 코드처럼 short, Short 형식을 지원하도록 한다,.

@ParameterizedTest
@AutoSource
void test(short s1, Short s2) {
}

protected, package-private 생성자 지원 논의

@jwChung 님이 아래에서 package-private 생성자 언급을 하셔서 이슈로 남깁니다.
#43 (comment)

protected 나 package-private 생성자는 지원하지 않고 public 만 선택되도록 지원할 것인가?
만약 그렇다면 public 생성자가 불필요한 클래스도 AutoParams 를 사용하기 위해서는 public 으로 공개해야 하는게 가이드 방향성이 될 것인가?

LongStream 값 생성 지원

@AutoSource가 아래 예저 코드처럼 LongStream 형식을 지원하도록 한다.

@ParameterizedTest
@AutoSource
void test(LongStream longStream) {
}

생성된 LongStream 개체는 3개 요소를 제공한다.

junit5 관련 기능 별도 모듈 분리

AutoParams는 현재 junit5에 의존하고 있습니다. 계획한 것으로 이해됩니다만, gradle 멀티 모듈(#19)로 구성된다면 다음과 같은 이유에서 junit5 관련 기능을 별도 모듈로 분리할 것을 제안합니다.

  • junit4 지원할 수 있는 확장 포인트가 생김
  • junit6 이 나오면 junit5와 함께 병렬지원 가능
  • junit 왜 다른 test framework에서 사용가능

라벨 규칙

논의 중 이슈 -> question 라벨
기능 구현이 확정된 이슈 -> enhancement 라벨
버그로 확인된 이슈 -> bug 라벨
아직 내용 파악이 안된 이슈 -> 무 라벨

현재 어떤 이슈가 채택된 것인지 어떤 이슈가 논의 중인지 잘 안보여서, 이런 식의 라벨 규칙이 있으면 좋을 것 같습니다.

infinite value generator 지원

infinite value generator 를 지원하여 사용자가 특정 조건의 값을 골라 사용할 수 있도록 한다.

@ParameterizedTest
@AutoSource
void sut_supports_infinite_value_generator(InfiniteStream<Integer> stream) {
    Stream<Integer> values = stream.filter(x -> x > 0).distinct().limit(10);
    ...
}

@AutoParameterizedTest 어노테이션

@ParameterizedTest 대신 @AutoParameterizedTest 어노테이션을 만들어 junit에서 제공하는 data source 기능은 그대로 사용하는 것은 어떨까요? 지정하지 않는 파라메타 값에 대해서만 익명(anonymous) 값을 넘겨 주는 것으로요.

AutoArgumentsProvider를 source 타입 별로 지정하면, customizatoin(#3) 할 때 새 AutoArgumentsProvider 이 만들어질테고 이것을 사용하기 위해 data soucrce를 타입별로 새로 정의해야하는 번거로움을 해결할 수 있을 것 같습니다.

빌드 오류

다음 메시지와 함께 빌드가 실패합니다.

* What went wrong:
A problem occurred configuring root project 'JavaUnit-AutoParams'.
> Could not resolve all dependencies for configuration ':classpath'.
   > Could not determine artifacts for org.ec4j.maven:ec4j-lint-api:0.0.7: Skipped due to earlier error

https://github.com/JavaUnit/AutoParams/runs/2113851747

up-for-grabs 등록

jump in을 활성화하기 위해 https://up-for-grabs.net/ 에 등록합니다. 등록 방법 및 내용은 https://github.com/up-for-grabs/up-for-grabs.net/blob/gh-pages/docs/list-a-project.md 여기에서 확인하실 수 있습니다. 리뷰를 통과할 수 있을지 모르겠지만 통과하면 자바 최초 등록이 되겠네요.

등록하려면 다음과 같은 작업이 필요합니다.

  • up-for-grabs 라벨 지정: 현재 good first issue 라벨이 어울리는데 이름을 그대로 갈지 수정할지 의견 부탁드립니다.
  • up-for-grabs 의미를 readme에 작성하는 것이 좋을 것 같습니다. 리뷰에도 영향을 줄 것 같고요.
  • PR 작성

ObjectGenerator generate 의 지원하지 않음과 null 반환을 구분

현재 ObjectGenerator 는 아래와 같이 Optional 을 반환하는 generate 메소드가 제공됩니다.

interface ObjectGenerator {

    Optional<Object> generate(ObjectQuery query, ObjectGenerationContext context);

}

Generator 구현체가 ObjectQuery 의 지원 대상이 아니라면 Optional.empty 를 반환합니다.
Generator 가 null 을 반환할 수 있다면 지원하지 않음null 반환을 구분할 필요가 생깁니다.

우선 아래 2가지 중에 하나를 선택할 수 있을거 같은데요. 더 나은 방법이 있을까요? 의견 부탁드립니다.

  1. ObjectGenerator 가 ObjectQuery 지원 가능을 판단하는 API 를 추가한다.
  2. generate 가 ObjectQuery 를 지원하지 않는다면, Exception 을 던진다.

Tasks

  • GenerationResult 타입 도입
  • 모든 generator GenerationResult generateObject(...) 구현
  • 모든 generator에서 Optional<Object> generate(...) 메소드 삭제
  • generateObject -> generate 로 메소드 이름 변경

임의의 값을 생성하지 못할 경우 예외 발생

다음 시나리오와 같이 AutoParams이 임의의 값을 생성하지 못할 경우 적절한 예외 메세지를 노출한다.

@ParameterizedTest
@AutoSource
void test(Builder<Closeable> builder) {
    assertThatThrownBy<CannotBeGeneartedException>(() -> {
        builder.build(); // An interface value cannot be generated. 
    });
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.