Junit tips and tricks @ Cédric Walter | Thursday, Jan 28, 2021 | 4 minutes read | 810 Words | Update at Friday, Dec 29, 2023

Embedded MongoDB

It provide a platform neutral way for running mongodb in Java integration tests. Thanks to this java library you can easily run integration test against a real mongo database. It is best to always mock your dependencies in true unit tests, but sometimes you need to test against the real thing.

  • It will
    • download mongodb (and cache it)
    • extract it (and cache it)
    • java uses its process api to start and monitor the mongo process
    • you run your tests
    • java kills the mongo process

Unit testing

How to use it in your unit tests

Add the dependencies to your project

<dependency>
    <groupId>de.flapdoodle.embed</groupId>
    <artifactId>de.flapdoodle.embed.mongo</artifactId>
    <version>2.2.0</version>
    <scope>test</scope>
</dependency>

Easing the integration

One way to ease the integration is to define your own annotation in MongoDbTest.java

import org.junit.jupiter.api.extension.ExtendWith;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith({
        MongoDbCallback.class
})
public @interface MongoDbTest {
}

And the following MongoDbCallback.java

public class MongoDbCallback implements BeforeAllCallback {
    private static MongodExecutable mongo;
    @Override public void beforeAll(ExtensionContext context) throws Exception {
        if (MONGO != null) {
            System.out.println("MongoDB already up and running");
        } else {
            var version = Version.Main.V4_0;
            var port = 27000;
            var config = new MongodConfigBuilder()
                    .version(version)
                    .net(new Net(port, Network.localhostIsIPv6()))
                    .build();
            mongo = MongodStarter.getDefaultInstance().prepare(config);
            mongo.start();
            System.out.println("Mongo started {} on port {}", version, port);
        }
    }
}

You can now annotate your integration test with @MongoDbTest and use the mongoClient connected to localhost:27000

Other ways

Log4j 2 asserting log entries with Junit

Lets start with the usage in JUnit

@Rule public LogAccessor logAccessor=new LogAccessor();

@Test @LogAccessorLogLevel(level = "DEBUG", category = "com.cedricwalter")
public void act_arrange_assert() {
    // Arrange
    // Act
    // Assert
    logAccessor.assertCount(1).assertLevel(Level.DEBUG).assertMessage("Hellow World");
}

Notes LogAccessorLogLevel is optionnal

Define a new annotation LogAccessorLogLevel .java:

package com.cedricwalter.logging;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
public @interface LogAccessorLogLevel {
    String level() default "ERROR";

    String category() default "";
}

Create a new appender TestAppender.java

package com.cedricwalter.logging;

import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class TestAppender extends AbstractAppender {
    private final List log = new ArrayList<>();

    protected TestAppender(String name, Filter filter, Layout<? extends Serializable> layout) {
        super(name, filter, layout);
    }

    public List getLog() {
        return new ArrayList<>(log);
    }

    @Override
    public void append(LogEvent logEvent) {
        log.add(logEvent);
    }
}

add the Rule class

package com.cedricwalter.logging;

import org.apache.logging.log4j.Level;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;

import java.util.List;

import static com.innoveo.skye.common.utils.matcher.RegexMatcher.matches;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

public class LogAccessor extends TestWatcher {
    private TestAppender appender;
    private LogAccessorLogLevel logAccessorLogLevel;

    private List getLog() {
        return getAppender().getLog();
    }

    public LogAccessor assertCount(int expectedLogEntryCount) {
        assertThat(getLog().size(), is(expectedLogEntryCount));
        return this;
    }

    public LogAccessor assertLevel(Level expectErrorLevel) {
        assertThat(getLog().get(0).getLevel(), is(expectErrorLevel));
        return this;
    }

    public LogAccessor assertMessage(String expectedMessage) {
        assertThat(getLog().get(0).getMessage().getFormattedMessage(), matches(expectedMessage));
        return this;
    }

    @Override
    protected void starting(Description description) {
        appender = new TestAppender("testAppender", null, null);
        Level level = getLevel(description);          /
        /Add appender to root logger
        org.apache.logging.log4j.core.Logger rootLogger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger();
        Configuration configuration = rootLogger.getContext().getConfiguration();
        configuration.addLoggerAppender(rootLogger, appender);
        String logCategory = getLogCategory(description);
        if (logCategory != null) {
            changeLoggerLevel(logCategory, level, appender);
        }
    }

    /
     * The problem was with the getLoggerConfig() call;
     * if the module you are trying to give a new level is not yet
     * * registered, this method returns the root logger (or any intermediate sub path registered), and thus instead
     * * of altering the level for com.mycompany you will alter root or com level. That's why you have to add a new
     * * LoggerConfig in case the module to alter is not yet registered.
     * *
     * * @param module
     * * @param level      * @param appender
     */
    private static void changeLoggerLevel(final String module, final Level level, TestAppender appender) {
        LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
        AbstractConfiguration configuration = (AbstractConfiguration) ctx.getConfiguration();
        LoggerConfig loggerConfig = configuration.getLogger(module);
        if (loggerConfig != null) {
            org.apache.logging.log4j.core.Logger logger = (org.apache.logging.log4j.core.Logger) LogManager.getLogger(module);
            configuration.addLoggerAppender(logger, appender);
            loggerConfig.setLevel(level);
        } else {
            loggerConfig = new LoggerConfig(module, level, true);
            configuration.addLogger(module, loggerConfig);
            ctx.updateLoggers(configuration);
            org.apache.logging.log4j.core.Logger logger = (org.apache.logging.log4j.core.Logger) LogManager.getLogger(module);
            configuration.addLoggerAppender(logger, appender);
            loggerConfig.setLevel(level);
        }
        ctx.updateLoggers(configuration);
    }

    @Override
    protected void finished(Description description) {
        removeAppender(LogManager.ROOT_LOGGER_NAME);
        String logCategory = getLogCategory(description);
        if (logCategory != null) {
            removeAppender(logCategory);
        }
    }

    private void removeAppender(String loggerName) {
        org.apache.logging.log4j.core.Logger logger = (org.apache.logging.log4j.core.Logger) LogManager.getLogger(loggerName);
        Configuration configuration = logger.getContext().getConfiguration();
        LoggerConfig loggerConfig = configuration.getLoggerConfig(loggerName);
        loggerConfig.removeAppender(appender.getName());
    }

    private TestAppender getAppender() {
        return appender;
    }

    private Level getLevel(Description description) {
        logAccessorLogLevel = description.getAnnotation(LogAccessorLogLevel.class);
        if (logAccessorLogLevel != null) {
            return Level.toLevel(logAccessorLogLevel.level());
        }
        return Level.ERROR;
    }

    private String getLogCategory(Description description) {
        logAccessorLogLevel = description.getAnnotation(LogAccessorLogLevel.class);
        if (logAccessorLogLevel != null) {
            return logAccessorLogLevel.category().getLoggerName();
        }
        return null;
    }

    @Override
    public String toString() {
        LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
        StringBuilder str = new StringBuilder();
        for (LoggerConfig logger : ctx.getConfiguration().getLoggers().values()) {
            final String loggerName = LogManager.ROOT_LOGGER_NAME.equals(logger.getName()) ? "Root Logger" : logger.getName();
            str.append("Found logger '" + loggerName + "' with level " + logger.getLevel());
        }
        return str.toString();
    }
}

Related content

Maven tips and tricks

Tuesday, May 30, 2017

Maven tips and tricks. When working with many feature/release/bugfix/hotfix branches, it is a bad idea to start changing the pom version as this will create merge conflicts using pull request.
3 minutes read
Update the Nirvana or FlySky NV14 to latest EdgeTX firmware

Update the Nirvana or FlySky NV14 to latest EdgeTX firmware

Sunday, Oct 8, 2023

EdgeTX is a popular open-source firmware for RC (Radio Control) transmitters. It offers a wide range of features and customization options for hobbyists and enthusiasts in the RC community. Keeping your EdgeTX installation up to date ensures you have access to the latest features, improvements, and bug fixes. One crucial aspect of updating EdgeTX is updating the SD card contents. In this blog post, we will guide you through the process of updating EdgeTX and its SD card to the latest version.
6 minutes read
Building a Plex Media Server on Raspberry Pi 4: Your Ultimate DIY Guide

Building a Plex Media Server on Raspberry Pi 4: Your Ultimate DIY Guide

Monday, Aug 28, 2023

In the era of digital media consumption, having a centralized hub to manage and stream your media content has become increasingly important. Enter the Plex Media Server—an incredibly versatile platform that allows you to organize, access, and enjoy your movies, TV shows, music, and photos from any device, anywhere. And what ’s even better? You can set up your very own Plex Media Server using a Raspberry Pi 4, a budget-friendly and energy-efficient solution that ’s perfect for DIY enthusiasts. In this guide, we ’ll walk you through the steps to create your very own Plex server on a Raspberry Pi 4.
3 minutes read
Lactofermentation

Lactofermentation

Friday, Aug 18, 2023

Lactofermentation, also known as lactic acid fermentation, is a natural process in which microorganisms, primarily lactic acid bacteria, convert sugars into lactic acid. This process is commonly used to preserve and transform various foods, especially vegetables and fruits. The lactic acid produced during lactofermentation acts as a natural preservative, inhibiting the growth of harmful bacteria and helping to extend the shelf life of the fermented foods.
5 minutes read

© 1997 - 2024 Cédric Walter blog

Powered by Open Sources technologies

avatar

Cédric WalterA true selfless act always sparks another

6s a1 acide-hyaluronique acma adaptability advocate-for-change ai airplane algorand alice-hlidkova-author alpine alps altruism-vs-commercialization antique-scooters antiseptic-rinse apache arcade arcade-gaming armattan art artemis artemis-viper artistic-expression atlassian authenticity-in-writing authenticity-matters avis bag bambulab bash bean bennu bernardet bestwishes betaflight betruger beware bien-vivre bien-être bien-être-physique bio bioethics bitcoin blessures-sportives blockchain blockchain-consensus-encyclopedia blockchain-systems blog book-review books bots Bought box brand-authenticity brand-integrity brand-protection breaking-barriers business-management business-milestones business-strategy business-success business-transformation businessbooks byzantine-fault-tolerance calculator calibre calibre-web camera case-studies cc2500 cgm-next challenges changement-de-vie channel-setup cheaper cherry-blossoms chirurgie-orthopédique choosing-fbl-gyro ci/cd classic-games classic-scooters classic-vespa climb climbing codefest collectible-scooters collectibles collection collector color competition consensus-algorithms consensus-mechanisms console consommation-responsable consumer-awareness containerization contest control-surfaces controller copy corticostéroïdes counterfeit-awareness counterfeit-culture counterfeit-market counterfeit-vs-authentic covid19 creating croissance-personnelle cryptocurrency cultural-experience cultural-richness curve-adjustments customer-discovery cve-issues dance-dreams death decentralization decentralized dental-hygiene dependency Design development devfest devops distributed-ledger-technology diverse-perspectives diy-dental diy-health dji docker docker-compose docker-hosting docker-networking docker-registry docker-security dont-buy dotnet Download downloading dreams-and-reality drone dynamic-ip désencombrement développement-personnel développement-spirituel ecology edgetx elrs elta emotional-challenges emotional-hurdles empowering-narrative endpoints engelberg Ensitm entrepreneurial-lessons entrepreneurial-mindset entrepreneurs entrepreneurship entrepreneurship-books Essaim essentially ethereum ethical-dilemmas evoque execution exercices-de-renforcement exercise-form facebook failure-analysis failure-stigma failure-to-success fake fake-apparel fake-brands fake-goods family family-building family-dynamics fashion-ethics fashion-fraud fbl-controllers fbl-system-compatibility fbl-system-features fbl-system-reviews fertility-struggles finance-books finances-personnelles financial-modeling financiallanning firearm firmware-customization firmware-issues fissure-horizontale fitness-routine fitness-tips flexibilité flight-controller flybarless-advantages flybarless-systems foss fpv frame France freestyle fresh-breath friendship-goals front gallery game-music gameplay-mechanics gamer-community games gaming-culture gaming-enthusiast gaming-history gaming-legacy gaming-nostalgia generative-ai genou gestion-de-ladouleur gestion-du-temps git global-impact google green-tea green-tea-mouthwash growth-hacking-books growth-mindset guide hackathon hackday hackfest health-and-wellness helicopter helicopter-community helicopter-gyro helicopter-tuning herbal-mouthwash hewlettpackard historical-scooters hobbies hobby hobbyist-blog holidays holistic-oralcare hollidays home-remedy home-workouts homelab homemade-oralcare honda honesty honey hornet how-to howTo https hugo human-connection hygiene-routine icecream iconic-scooters iflight iflightnazgulevoque immich indoor industrial-shit industry injections-intra-articulaires injury-prevention innovation innovation-books innovation-journey ios japan-travel japanese-cuisine jar java jdk11 jellyfin joint-health junit jupiter kitchen knee-rehabilitation knee-stability knockoff-alert kyoto lacoste lacoste-counterfeit lambretta landmarks leadership leadership-books lean-startup learning-from-failure leg-day leg-workouts legal-complexities legit-fashion let's-encrypt libération life-transformations link linux llm local-traditions m2evo macos magical-adventure magician-lord main make manurhin manurhin-sm75 mapping marathon market-research marketing-books maven me medical medical-advancements metakernel miami-entertainment mid-century-scooters migration mindset-shifts minimalisme minimum-viable-product minty-fresh mixer-settings mk3 mk4 mobilité model-setup modern-family modern-motherhood moon moral-encounters motherhood-dilemmas motorcycle mount mountain mountains mouth-rinse mouthwash-ingredients mouthwash-recipe Mulhouse muscle-activation music mvs mycollection ménisque NASA natural-mouthwash nature nazgul neo-geo-aes neogeo network new-bookrelease nginx-proxy north-face north-face-replica nostalgic-scooters nv14 objectifs old-school-scooters omphobby open-source open-source-rc opensource opentx openvpn oral-care oral-health organizer osaka oss overcoming-challenges p1p p1s parental-rights parenthood-reflections parts passion patella-health persistence personal-relationships photos physical-therapy physiothérapie pivot-strategy pixel-art planet plasma-riche-en-plaquettes platform plex pluto pretty-girl-complex privacy product-market-fit productivity-books proof-of-stake proof-of-work protect-your-style prusa prusa-research public-image quadcopter quadriceps-strength radio-control radio-programming radiomaster rare-scooters raspberrypi raspbian rates-configuration rc rc-community rc-configuration rc-firmware RC helicopter rc-helicopter-electronics rc-helicopter-enthusiasts rc-helicopter-setup rc-helicopter-technology rc-helicopter-tips rc-helicopters rc-modeling rc-simulator realdebrid realflight receiver reflex-xtr refreshing-breath rehabilitation-exercises relations-personnelles relationship-complexities released remote remote-control-flying reproductive-ethics resilience-in-business resilient-women restored-scooters retro-gaming retro-gaming-community retro-gaming-console retro-scooters reverse-proxy rhythms-of-life risk-management robotic router rx réadaptation rééducation sab sab-raw-420 sab-raw-580 sab-raw-700 sales-books santé-articulaire santé-mentale scooter-enthusiast scooter-memorabilia scooters security-nightmare self-leveling-helicopter server-configuration servo-config skydiving snk snk-corporation snk neo geo soap social-issues solex space spams sport ssl-termination ssl/tls startup-books startup-failure static-code-generator steam strategic-networking streaming strength-training success-stories sun support surrogacy-agency surrogacy-journey surrogacy-narratives swiftui swiss switzerland team team-building team-dynamics teeth-cleaning temples-and-shrines tendermint terrot thérapie-physique tokyo torvol traefik traitement-des-fissures transmitter transmitter-firmware travel travel-tips trouver-du-sens tunnel turning-setbacks-into-success tutorial tx unconventional-strategies vacation velosolex vespa viaferrata video video-game-review vintage vintage-scooters vintage-two-wheelers vintage-vespa vintagegaming vmo-exercises warez web-security wind winner winterthur women-supporting-women wordpress workout-progression x1c zurich zyxel zyxel-avoid zyxel-not-serious-with-security zyxel-outdated zyxel-router-not-good équilibre
Me

Cédric Walter is a French-Swiss entrepreneur, investor, and software engineer based in Zurich, Switzerland. He spent his career developing software applications for Swiss insurance companies to handle billions of dollars in premiums. He cofounded Innoveo AG and as the software architect developed the no-code platform designed to reduce the manual coding that powers many software apps. As an active participant in the European hacking community, he works on many open source projects including blockchain. Cédric is a winner of multiple hackathons. His expertise include designing back end, event-based, and blockchain systems. Cédric is also the founded Disruptr GmbH, a software development company that offers full spectrum of services for businesses of all sizes.

JAVA full-stack developer since 2000, in Blockchain since 2017, Certified Scrum Master 2012, Corda Certified Developer in 2019, Ethereum smart contract expert in the SWISS Blockchain Security working group

Hackathons

  • HackZurich 2022 – Level Up in top 25 finalist among 134 submissions
  • SBHACK21 – SwiFi winner of best Solution on Algorand, overall Winner 3rd Prize, CV Labs Fast Track Ticket
  • HackZurich 2020 Europe’s Biggest Hackathon winner in category Migros
  • SBHACK19 – LendIt winner of Swiss biggest Blockchain Hackathon. On chain insurance and ledger for agricultural land soil.
  • Member of the Bitcoin Association Switzerland and Cryptovalley association Switzerland,

PGP: DF52 ADDA C81A 08A6

Copyright information

All editorial content and graphics on our sites are protected by U.S. copyright, international treaties, and other applicable copyright laws and may not be copied without the express permission of Cedric Walter, which reserves all rights. Reuse of any of Cedric Walter editorial content and graphics for any purpose without The author ’s permission is strictly prohibited.

DO NOT copy or adapt the HTML or other code that this site creates to generate pages. It also is covered by copyright.

Reproduction without explicit permission is prohibited. All Rights Reserved. All photos remain copyright © their rightful owners. No copyright infringement is intended.

Disclaimer: The editor(s) reserve the right to edit any comments that are found to be abusive, offensive, contain profanity, serves as spam, is largely self-promotional, or displaying attempts to harbour irrelevant text links for any purpose.

Others

If you like my work or find it helpful, please consider buying me a cup of coffee ☕️. It inspires me to create and maintain more projects in the future. 🦾

It is better to attach some information or leave a message so that I can record the donation 📝 , thank you very much 🙏.

Reproduction without explicit permission is prohibited. All Rights Reserved. All photos remain copyright © their rightful owners. No copyright infringement is intended.