Skip to content

Latest commit

 

History

History
212 lines (148 loc) · 8.81 KB

README.md

File metadata and controls

212 lines (148 loc) · 8.81 KB

Mock StatsD

Maven Central Java CI with Gradle CodeQL GitHub license Kotlin

TL;DR The Mock StatsD library simplifies the testing process for Kotlin/Java applications that send metrics to a StatsD server. It provides detailed verification capabilities for all types of StatsD metrics and automates port management for ease of use. It integrates smoothly with JUnit 5 for effortless unit testing and leverages Kotlin features for an idiomatic and intuitive experience. Ultimately, Mock StatsD promotes software quality and reliability by enabling thorough and efficient metrics testing.

What is StatsD?

StatsD is a simple daemon for easy stats aggregation which is commonly used for monitoring applications. The basic idea is to send different types of metrics (like counters, timers, gauges) from your application to StatsD, which then periodically aggregates the metrics and pushes them to Graphite (or some other defined backend).

Mock StatsD Server library in Kotlin

The Mock StatsD library offers several benefits for Kotlin and Java developers, especially those working on applications that send metrics to a StatsD server:

  1. Simplified Testing: By providing a mock StatsD server, this tool makes it easier to write unit tests for your application's metrics-related functionality. Instead of having to manage a real StatsD server for testing purposes, you can simply start and stop the mock server as needed.

  2. Detailed Verification: Mock StatsD allows you to verify the exact metrics sent by your application, including the type, name, and value of each metric. This makes it possible to catch subtle bugs in your metrics code.

  3. Support for All StatsD Metric Types: Whether your application uses counters, timers, gauges, or histograms, you can test them all with Mock StatsD.

  4. Automatic Port Management: If you don't specify a port number when creating the mock server, it automatically selects an available port. This is especially useful in environments where many tests are running concurrently and may be using network resources.

  5. Easy Integration with JUnit 5: The library provides a JUnit 5 extension, which automatically manages the lifecycle of the mock server. This means the server will start before your tests run and stop after they complete, minimizing the boilerplate code in your tests.

  6. Built with Kotlin: As a Kotlin library, Mock StatsD integrates seamlessly with your Kotlin codebase. It takes advantage of Kotlin features like extension functions, and its API is designed to be idiomatic and intuitive for Kotlin developers.

By simplifying the process of testing your application's metrics code and providing detailed, easy-to-use verification methods, Mock StatsD helps ensure the quality and reliability of your software.

Getting Started

  1. Add dependency in pom.xml:

    <dependency>
        <groupId>com.github.kpavlov.mocks.statsd</groupId>
        <artifactId>mock-statsd</artifactId>
        <version>${mock-statsd.version}</version>
        <scope>test</scope>
    </dependency>

    or in build.gradle.kts:

    dependencies {
        testImplementation("com.github.kpavlov.mocks.statsd:mock-statsd:$mockStatsdVersion")
    }

    Check latest version in Maven Central repository

  2. To use this library, add the following import statement to your Kotlin file:

    import me.kpavlov.mocks.statsd.server.MockStatsDServer
  3. Create a new instance of MockStatsDServer with, specifying the port number. Use RANDOM_PORT to automatically select an available port:

    val mockStatsD = MockStatsDServer(RANDOM_PORT)
  4. Start the server:

    mockStatsD.start()

    You can now send metrics to the server and then verify that they were received correctly.

  5. Cleaning Up

    mockStatsD.reset()

    Cleans up collected metrics and recorded calls.

  6. Stopping server

    When you're done with the server, stop it with the stop method:

    mockStatsD.stop()

    This ensures that the port used by the server is freed up and can be used by other processes.

JUnit5 Extension

You can also register MockStatsDServer as JUnit 5 extension. It will automatically create and start a single instance of MockStatsDServer to use in all tests. It will be stopped on JVM shutdown, when test execution is finished.

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(StatsDJUnit5Extension::class)
internal class Junit5ExtensionTest : BaseStatsDServerTest() {

    lateinit var mockStatsD: MockStatsDServer
    lateinit var client: StatsDClient

    @BeforeAll
    fun beforeAll() {
        mockStatsD = StatsDJUnit5Extension.statsDServer()
        client = StatsDClient(port = mockStatsD.port())
    }
}

Now you can use MockStatsD server and statsD client in tests.

Sending Metrics

The server can capture different types of metrics: Time, Counter, Gauge, and Histogram. Here's how to send each type of metric:

Time

val name = "timeMetric"
val value = 31L
client.time(name, value)

Counter

val name = "counterMetric"
client.incrementCounter(name) // increment by 1
client.incrementCounter(name, 3) // increment by 3
client.incrementCounter(name, -2) // decrement by 2
client.decrementCounter(name) // decrement by 1

Gauge

val name = "gaugeMetric"
val value = 42.0
client.gauge(name, value)

Histogram:

val name = "histogramMetric"
val value = 42.0
client.histogram(name, value)

Set

val name = "histogramMetric"
client.set(name, 42.0)
client.set(name, 128.0)
client.set(name, 42.0)

println(mockStatsD.metricContents(name)?.asList()?.sorted()) // [42.0, 128.0]

Multi-Metric Packets

MockStatsD supports receiving multiple metrics in a single packet by separating them with a newline characters (\n).

batchGauge:333.0|g\nbatchCounter:42.0|c

Each message could therefore represent a batch of metrics sent from the client in a single UDP packet. This is useful for reducing network load when sending multiple metrics, as it can all be done in one network operation.

The batch support is achieved by first splitting the batched message into individual metrics and then handling each metric separately, e.g.:

    @Test
    fun `Server should handle multi-metric packets`() {
        val gaugeName = "batchGauge"
        val counterName = "batchCounter"

        // Set initial value
        val gaugeValue = 333.0
        val counterValue = 42.0
        client.send("$gaugeName:$gaugeValue|g\n$counterName:$counterValue|c")
        await untilAsserted {
            assertThat(statsd.metric(gaugeName)).isEqualTo(gaugeValue)
        }
        await untilAsserted {
            assertThat(statsd.metric(counterName)).isEqualTo(counterValue)
        }
    }

Verifying Metrics

After sending metrics, you can verify that the server captured them correctly. Use the metric method to retrieve a metric value, and verifyCall to verify that a specific call was made. For example:

await untilAsserted {
    assertThat(mockStatsD.metric(name)).isEqualTo(value.toDouble())
}
println(mockStatsD.calls()) // prints all calls to console
mockStatsD.verifyCall("$name:$value|ms")
mockStatsD.verifyNoMoreCalls("$name:$value|ms")

This example checks that the server received a Time metric with the specified name and value, and that no more calls with the same name and value were made.

Complete Example

Check out the StatsDServerTest.kt file in the test directory for a complete example of how to use the StatsDServer. This test class demonstrates how to set up a server, send different types of metrics, verify the captured metrics, and clean up the server.