tifyty

pure Java, what else ?

Kategória archívok: testing

[E] A little bit of testing

Behaviour Driven Development usually says that the test structure consists of

  • GIVEN
  • WHEN
  • THEN

Even Mockito has an DBBMockito façade implementation that fits to this habit. If you want a big shot you can have JBehave. However for now let us starts with some simple unit test:

    private static final Configuration config = new BasicConfiguration();
    private static final String key = "key";
    private static final String value = "value";
    private static final String badValue = "badValue";
...
    public void testGetConfigValueWhenEnvironmentKeyExists() {
        String actual;
        Properties props;
        // GIVEN
        props = PowerMockito.mock(Properties.class);
        PowerMockito.mockStatic(System.class);
        config.setConfigProperties(props);
        Mockito.when(props.containsKey(key)).thenReturn(true);
        Mockito.when(props.getProperty(key)).thenReturn(badValue);
        Mockito.when(System.getenv("sb4j." + key)).thenReturn(value);
        // WHEN
        actual = config.getConfigValue(key);
        // THEN
        Mockito.verify(props).containsKey(key);
        Mockito.verify(props).getProperty(key);
        PowerMockito.verifyStatic();
        System.getenv("sb4j." + key);
        Assert.assertEquals(value, actual);
    }

Do you feel something under you skin that this is not the real one? What are those comments? Whenever I feel the need to write comments into a method I start to think about better variable and method names and refactoring. Why is this method so complex that this is hard to understand without the comments? Yes, sure: I can remove those three comment lines, but even then it is not really an improvement.

What if we could create a simple Business class and we could book our ticked from the economy class to business class? We are not even forced to finish the class, it is OK half way ready:

public abstract class Business {

    abstract public void given();
    abstract public void when();
    abstract public void then();
    public void execute(){
        given();
        when();
        then();
    }

}

It is so small that it just fits any project. Whenever we have it the test code looks much better:

    @Test
    public void testGetConfigValueWhenEnvironmentKeyExists() {
        new Business() {
            private String actual;
            private Properties props;

            @Override
            public void given() {
                props = PowerMockito.mock(Properties.class);
                PowerMockito.mockStatic(System.class);
                config.setConfigProperties(props);
                Mockito.when(props.containsKey(key)).thenReturn(true);
                Mockito.when(props.getProperty(key)).thenReturn(badValue);
                Mockito.when(System.getenv("sb4j." + key)).thenReturn(value);
            }

            @Override
            public void when() {
                actual = config.getConfigValue(key);
            }

            @Override
            public void then() {
                Mockito.verify(props).containsKey(key);
                Mockito.verify(props).getProperty(key);
                PowerMockito.verifyStatic();
                System.getenv("sb4j." + key);
                Assert.assertEquals(value, actual);
            }

        }.execute();
    }

Much better, is it. The only question remaining: who in the hell could we mock the System class? Believe me: the code above is copy/paste without modification from a real live test and it works. Stay tuned for some coming posts where I detail how it was made.

Egy kis tesztelés

A Behaviour Driven Development módszertanban szokás, hogy a teszt kód alapja

  • GIVEN
  • WHEN
  • THEN

alakú. Még a Mockitónak is van DBBMockito façade implementációja, ami ehhez a szokáshoz igazodik. Sőt ha valaki akar egy nagyágyút, nos ott a JBehave. De most csak egy sima kis unit teszt:

    private static final Configuration config = new BasicConfiguration();
    private static final String key = "key";
    private static final String value = "value";
    private static final String badValue = "badValue";
...
    public void testGetConfigValueWhenEnvironmentKeyExists() {
        String actual;
        Properties props;
        // GIVEN
        props = PowerMockito.mock(Properties.class);
        PowerMockito.mockStatic(System.class);
        config.setConfigProperties(props);
        Mockito.when(props.containsKey(key)).thenReturn(true);
        Mockito.when(props.getProperty(key)).thenReturn(badValue);
        Mockito.when(System.getenv("sb4j." + key)).thenReturn(value);
        // WHEN
        actual = config.getConfigValue(key);
        // THEN
        Mockito.verify(props).containsKey(key);
        Mockito.verify(props).getProperty(key);
        PowerMockito.verifyStatic();
        System.getenv("sb4j." + key);
        Assert.assertEquals(value, actual);
    }

Nem érzel valami bizsergést, hogy ez így nem az igazi? Mi az a három komment? Ha egy metódusba kommentet akarok írni, bele a belsejébe, akkor mindig elgondolkodom rajta, hogy miért is? Mi történt? Miért olyan bonyolult ez a metódus, hogy fel kell kommentezni? Persze, ja, hogyne… ki is vehetem azt a három sort, de attól nem lesz jobb, maximum átmenetileg tisztább, szárazabb az érzés, de a görcs megmarad.

Mi lenne, ha egy baromi egyszerű, kis Business class-t készítenénk, és átszállnánk a turista osztályról. Nem is kell teljesen elkészíteni, félbe is hagyhatjuk:

public abstract class Business {

    abstract public void given();
    abstract public void when();
    abstract public void then();
    public void execute(){
        given();
        when();
        then();
    }
    
}

Ez igazán nem sok, minden projektbe belefér. És ha ez megvan, akkor a teszt már sokkal szebb:

    @Test
    public void testGetConfigValueWhenEnvironmentKeyExists() {
        new Business() {
            private String actual;
            private Properties props;

            @Override
            public void given() {
                props = PowerMockito.mock(Properties.class);
                PowerMockito.mockStatic(System.class);
                config.setConfigProperties(props);
                Mockito.when(props.containsKey(key)).thenReturn(true);
                Mockito.when(props.getProperty(key)).thenReturn(badValue);
                Mockito.when(System.getenv("sb4j." + key)).thenReturn(value);
            }

            @Override
            public void when() {
                actual = config.getConfigValue(key);
            }

            @Override
            public void then() {
                Mockito.verify(props).containsKey(key);
                Mockito.verify(props).getProperty(key);
                PowerMockito.verifyStatic();
                System.getenv("sb4j." + key);
                Assert.assertEquals(value, actual);
            }

        }.execute();
    }

Ugye mennyivel szebb? Már csak egy kérdés maradt: hogy sikerült a System final osztályt statikusan mockolni a powermock-kal? De ez már egy másik történet, ráment négy óra. Most hétvégére legyen csak egy ilyen könnyű cikk, jövő hétre a csőben van egy kis kivételkezelési sebesség, de utána elmondom, hogy hogyan is tud működni ez a teszt. Lesz benne classloader, maven, eclipse … “és kakast is!”.