Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Joseph Heyburn
Joseph Heyburn

Posted on • Edited on • Originally published atjdheyburn.co.uk

     

Extending Gotests for Strict Error Tests

This is my first post on dev.to, X-posted from my new personal blog which can be foundhere. Hopefully I'll have dev.to publish from RSS
feeds once I've worked it out!

Happy to receive any feedback you may have! 😃


Strict Error Tests in Java

I love confirming the stability of my code through writing tests and practicing Test-driven development (TDD). For Java, JUnit was my preferred testing framework of choice. When writing tests to confirm an exception had been thrown, I used the optional parameterexpected for the annotation@Test, however I quickly found that this solution would not work for methods where I raised the same exception class multiple times for different error messages, and testing on those messages.

This is commonly found in writing a validation method such as the one below, which will take in a name of a dog and return a boolean if it is valid.

publicstaticbooleanvalidateDogName(StringdogName)throwsDogValidationException{if(containsSymbols(dogName)){thrownewDogValidationException("Dogs cannot have symbols in their name!");}if(dogName.length>100){thrownewDogValidationException("Who has a name for a dog that long?!");}returntrue;}

For this method, just using@Test(expected = DogValidationException.class) on our test method is not sufficient; how can we determine that the exception was raised for a dogName.length breach and not for containing symbols?

In order for me to resolve this, I came across theExpectedException class for JUnit onBaeldung which enables us to specify the error message expected. Here it is applied to the test case for this method:

@RulepublicExpectedExceptionexceptionRule=ExpectedException.none();@TestpublicvoidshouldHandleDogNameWithSymbols(){exceptionRule.expect(DogValidationException.class);exceptionRule.expectMessage("Dogs cannot have symbols in their name!");validateDogName("GoodestBoy#1");}

Applying to Golang

Back to Golang, there is a built-in library aptly namedtesting which enables us to assert on test conditions. When combined withGotests - a tool for generating Go tests from your code - writing tests could not be easier! I love how this is bundled in with the Go extension for VSCode, my text editor of choice (for now...).

Converting the above JavavalidateDogName method to Golang will produce something like:

funcvalidateDogName(namestring)(bool,error){ifcontainsSymbols(name){returnfalse,errors.New("dog cannot have symbols in their name")}iflen(name)>100{returnfalse,errors.New("who has a name for a dog that long")}returntrue,nil}

If you have a Go method that returns theerror interface, then gotests will generate a test that look like this:

funcTest_validateDogName(t*testing.T){typeargsstruct{namestring}tests:=[]struct{namestringargsargswantboolwantErrbool}{name:"Test error was thrown for dog name with symbols",args:args{name:"GoodestBoy#1",},want:false,wantErr:true,}for_,tt:=rangetests{t.Run(tt.name,func(t*testing.T){got,err:=validateDogName(tt.args.name)if(err!=nil)!=tt.wantErr{t.Errorf("validateDogName() error = %v, wantErr %v",err,tt.wantErr)return}ifgot!=tt.want{t.Errorf("validateDogName() = %v, want %v",got,tt.want)}})}}

From the above we are limited to what error we can assert for, hereany error returned will pass the test. This is equivalent to using@Test(expected=Exception.class) in JUnit! But there is another way...

Modifying the Generated Test

We only need to make a few simple changes to the generated test to give us the ability to assert on test error message...

funcTest_validateDogName(t*testing.T){typeargsstruct{namestring}tests:=[]struct{namestringargsargswantboolwantErrerror}{name:"Test error was thrown for dog name with symbols",args:args{name:"GoodestBoy#1",},want:false,wantErr:errors.New("dog cannot have symbols in their name"),}for_,tt:=rangetests{t.Run(tt.name,func(t*testing.T){got,err:=validateDogName(tt.args.name)iftt.wantErr!=nil&&!reflect.DeepEqual(err,tt.wantErr){t.Errorf("validateDogName() error = %v, wantErr %v",err,tt.wantErr)return}ifgot!=tt.want{t.Errorf("validateDogName() = %v, want %v",got,tt.want)}})}}

From the above there are three changes, let's go over them individually:

  1. wantErr error
    • we are changing this frombool so that we can make a comparison against the error returned from the function
  2. wantErr: errors.New("dog cannot have symbols in their name"),
    • this is the error struct that we are expecting
  3. if tt.wantErr != nil && !reflect.DeepEqual(err, tt.wantErr) {
    • check to make sure the test is expected an error, if so then compare it against the returned error

Point 3 provides additional support if there was a test case that did not expect an error. Note howwantErr is omitted entirely from the test case below.

{name:"Should return true for valid dog name",args:args{name:"Benedict Cumberland the Sausage Dog",},want:true,}

Customising Gotests Generated Test

Gotests gives us the ability to provide our own templates for generating tests, and can easily be integrated into your text editor of choice. I'll show you how this can be done in VSCode.

  1. Check out gotests and copy the templates directory to a place of your choosing

    • git clone https://github.com/cweill/gotests.git
    • cp -R gotests/internal/render/templates ~/scratch/gotests
  2. Overwrite the contents of function.tmpl withthe contents of this Gist

  3. Add the following setting to VSCode's settings.json

    • "go.generateTestsFlags": ["--template_dir=~/scratch/templates"]

Once you have done that, future tests will now generate with stricter error testing! 🎉

Closing

I understand that the recommendations above will make your code more fragile, as the code is subject to any changing of the error message of say a downstream library. However for myself, I prefer to write tests that are strict and minimalise the chance of other errors contaminating tests.

I also understand that GoodestBoy#1 is probably a valid name for a dog! 🐶

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Engineer with experience in softwarey, devopsy, and cloudy stuff!
  • Location
    London, UK
  • Work
    Senior DevOps Engineer
  • Joined

More fromJoseph Heyburn

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp