Skip to content

Done testing exercise and http exercise by Nacchan #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

sobatha
Copy link
Collaborator

@sobatha sobatha commented Apr 21, 2025

This PR adds answers for the http client exercise and the testing exercise.

Screenshots

スクリーンショット 2025-04-21 10 38 20

@sobatha sobatha requested a review from Copilot April 21, 2025 01:50
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds solutions for the HTTP client exercise and testing exercise by implementing a basic HTTP request flow and range validation functionality.

  • Implements a range-checking function with accompanying tests.
  • Adds an HTTP client that fetches user and account data from an external API.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
exercise_answers/nacchan/validator/validator.go Implements a simple range-checking function with clear comments.
exercise_answers/nacchan/validator/validator_test.go Provides comprehensive tests for the IsInRange function.
exercise_answers/nacchan/http_exercise.go Implements an HTTP client to fetch and display user and account data.

)

type UserAttributes struct {
Id int `json:"id"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick about naming; in Go we need to use consistent case (all small, or all capitalized) for initialisms and acronyms. For example, ID, AccountIDs, id (for unexported variable), userID etc.

You can see Go Wiki and the Google Style Guide for more examples!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Google style guide looks very easy to understand! Thanks👏

func main() {
var userId int
reader := bufio.NewReader(os.Stdin)
for {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice loop!

There are many ways to read input in Go, including bufio, fmt.Scanf, and others. I am not sure which one is the most performant or most common, but your solution seems robust!

resp, err := http.Get(URL + strconv.Itoa(id))

if err != nil {
return "", fmt.Errorf("error in making request: %v", err)
Copy link
Collaborator

@mavrikis-kostas mavrikis-kostas Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message looks great! It follows the error strings suggestion of using not-capitalized strings, which is correct. There is only one thing that can be improved here, which is using %w instead of %v for wrapping the errors.

The %w verb creates "wrapped" (nested) errors which can be unwrapped using the errors package. You can check the errors package documentation for more info!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, by wrapping you can use error.Is later to check it holds specific error or not👀 I need to understand about go error handling more, thanks for letting me know!

return nil, fmt.Errorf("error in parsing json: %v", err)
}

var accounts []AccountAttributes = []AccountAttributes{}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many ways to initialize slices in Go, such as:

  • var accounts []AccountAttributes = []AccountAttributes{}
    • creates a slice with 0 length/capacity, appending elements will resize the slice
    • this approach is generally not recommended in Go (instead, use the next approach)
  • var accounts []AccountAttributes
    • creates a nil slice with 0 length/capacity, appending elements will resize the slice
    • this approach is recommended if you do not know (or cannot estimate) the final size of the slice
  • accounts := make([]AccountAttributes, len(res))
    • creates a slice with 0 length, but preconfigured capacity, appending elements will only resize the slice if the capacity is not enough
    • this approach is recommended when you already know (or can estimate) the final size of the slice

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I haven't noticed that there's such difference between []type and []type{}, but I noticed that Intellij blamed me when I have []type {} code! 👀
スクリーンショット 2025-04-24 15 42 51

printAccounts(accounts)
}

func getUser(id int) (string, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great solution!

var res UserResponse
err = json.Unmarshal(body, &res)

if err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comes down to personal preferences, so other people may have different suggestions on this. But a more idiomatic way of retrieving an error and checking if it's nil, is like this:

if err = json.Unmarshal(body, &res); err != nil {
	return "", fmt.Errorf("error in parsing json: %v", err)
}

The main benefit of this approach is that the scope of the err variable is limited to the if statement. This style is used whenever the function that you want to check (in this case, json.Unmarshal()) only returns an error object, and you want to immediately check the error.


Note: your solution is also perfectly valid, since the err variable has already been declared in a previous statement. The code that I recommended does not actually limit the scope of the outer err variable, but it's still considered more "go idiomatic", aka it's more common.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assigning variables at the beginning of an if statement certainly gives it a go-like style!(I never saw this style in other language🧐) While I understand that limiting the variable scope isn't always essential, I agree it’s considered good practice and I'll keep that in mind!

return accounts, nil
}

func printAccounts(accounts []AccountAttributes) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice function!

"testing"
)

func TestIsInRange(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great solution!!

t.Run(tc.name, func(t *testing.T) {
result := IsInRange(tc.num, tc.min, tc.max)
if result != tc.expected {
t.Errorf("expected %v, got %v", tc.expected, result)
Copy link
Collaborator

@mavrikis-kostas mavrikis-kostas Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message is clear, and shows what went wrong in the test!

But some small optional adjustments are:

  • Instead of expected x, got y, Go usually follows the opposite order got x, want y. This is also explained briefly in the "Useful Test Failures" section of the Go Wiki.
  • Mentioning the function name is also a common pattern, so in this case the error would be:
    • t.Errorf("IsInRange() = %v, want %v", result, tt.expected)

Note: in IAA we will probably use the assert package of the testify library, so the error messages will automatically be handled by the library.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this is the auto-generated test from IntelliJ, that follows the general Go conventions:

func TestIsInRange(t *testing.T) {
	type args struct {
		num int
		min int
		max int
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := IsInRange(tt.args.num, tt.args.min, tt.args.max); got != tt.want {
				t.Errorf("IsInRange() = %v, want %v", got, tt.want)
			}
		})
	}
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see—it's a Go convention to include the test's objective in the function name and clearly distinguish between got and want. Thanks for pointing out this helpful reference!

func TestIsInRange(t *testing.T) {
tt := []struct {
name string
num, min, max int
Copy link
Collaborator

@mavrikis-kostas mavrikis-kostas Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your approach is common to separate the input and output parameters of the test! In this case, num, min, max are all input parameters, so they are placed in the same line 👏

Another way to separate them, if we have a lot of input parameters (maybe 4+), is using an extra struct. Check the IntelliJ-generated code that I pasted in the below comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants