Tagged: groovy

Скринкаст: Пишем калькулятор на Java, JavaFX, Groovy, Spock, Kotlin

Несколько вечеров за которые я написал калькулятор на Джаве с тестами на Груви а потом переписал на Котлин. Интересно будет только для Stong Junior’ов

Это не туториал! В обычных туториалах всё хорошо и сразу получается, но как только начинающий программист сталкивается с ошибкой то не знает как дальше двигаться. Здесь все по настоящему — часть я знаю, часть изучаю сразу же. На ваших глазах появляются и фиксятся баги, гуглится и копипастится с СтекОверфлоу. Это может быть вам полезно для того чтобы ощутить эвристики которые помогают мне.
Скучно, занудно — но именно так примерно мы и программируем целыми днями. Если вы думаете связать свою жизнь с этим ремеслом, можете прочувствовать себя в моей шкуре 😉

Исходный код:
https://github.com/stokito/javafx-calculator

P.S. Да, я знаю звук отвратительный, винда неудачно обнволялась и с драйвером звука какая-то ерунда.

Реклама
junit, test-ng, mockito, spock

Spock or not to Spock?

В последнем выпуске подкаста Разбор полётов ведущие спорили насколько оправданно применять тестовый фреймворк Spock для всех Java проектов.
Изначально разговор зашёл про спецификации и программные TCK которые проверяют что имплементация соответствует спецификации.
Тут пожалуй стоит сделать отступление, и отметить что обычно также делают referrence implementation и как минимум ещё одну реализацию, поскольку любой API нужно реализовывать всегда как минимум дважды чтобы увидеть не появляется ли повторяющийся вспомогательный код перед вызовом API метода который следовало бы тоже вынести в API.
Например, в Java SE для такой частой операции как чтение текстового файла в строку приходилось постоянно писать код преобразования InputStream в StringBuilder и у него уже брать toString(). И только в седьмой джаве появился метод Files.readAllBytes() тем не менее по прежнему требующий ещё дополнительной строчки преобразования массива байтов в строку с указанием кодировки.

Ещё один важный момент, что TCK являются для реализации внешними тестами, в то время как у неё могут быть свои собственные, тестирующие именно особенности реализации.
Я это всё расписал чтобы иметь более полную картину.

И тут у ведущих возник естественный вопрос: если у нас уже есть программный TCK, то, по сути, он уже и есть вообщем-то спецификацией, только записанной уже в более формальном языке программы.
Это совершенно правильная мысль. Единственное различие в них в том, что спецификация является текстом описывающим систему для людей, на естественном языке, в то время как от TCK требуется только дать однозначный ответ — соответствует реализация спецификации или нет. И очень вероятна ситуация когда по коду TCK мы вообще не сможем понять почему все работает именно так, поскольку детали опущены в коде TCK но расписаны в самой спецификации.
Вообще тут даже можно утверждать что refference implementation и тесты самой реализации тоже так же могут служить заменой или дополнением спецификации.
Это одна из причин почему программисты так любят open source — ты всегда можешь узнать как точно работает система на которую ты полагаешься.

Следовательно идея совместить спецификацию и тесты и сделать их ближе к коду родила BDD.
Обычно в BDD фреймворках пишут сценарий на, хоть и формализованном, но естественном языке а потом к нему пишут тестовый код.
Вот пример такого сценария на языке сценариев Gherkin для фреймворка Cuccumber:

Story: Returns go to stock

In order to keep track of stock
As a store owner
I want to add items back to stock when they're returned

Scenario 1: Refunded items should be returned to stock
Given a customer previously bought a black sweater from me
And I currently have three black sweaters left in stock
When he returns the sweater for a refund
Then I should have four black sweaters in stock

В идеале эти сценарии писались бы не программистами а тестировщиками в месте с продакт овнером. Идея, на сколько я знаю, не очень прижилась из-за того что сценарии всё равно находятся отдельно от кода, что требует дополнительных усилий на синхронизацию.
Поэтому начали искать способы перенести эти сценарии прямо в тестовый код. Но тут же упёрлись в ограниченный синтаксис самих языков программирования непригодный для этой цели.
Проблему пытались обойти аннотациями, но код получался плохо читабельным. Вот например JBehave поверх Java:

private Game game;
private StringRenderer renderer;

@Given("a $width by $height game")
public void theGameIsRunning(int width, int height) {
game = new Game(width, height);
renderer = new StringRenderer();
game.setObserver(renderer);
}

@When("I toggle the cell at ($column, $row)")
public void iToggleTheCellAt(int column, int row) {
game.toggleCellAt(column, row);
}

@Then("the grid should look like $grid")
public void theGridShouldLookLike(String grid) {
assertThat(renderer.asString(), equalTo(grid));
}

Но например для Groovy, динамического языка программирования поверх JVM многих ограничений Java нет. И поверх него был создан прекрасный фреймворк Spock. Вот пример теста\спецификации:

class HelloSpock extends spock.lang.Specification {
def "length of Spock's and his friends' names"() {
expect:
name.size() == length

where:
name | length
"Spock" | 5
"Kirk" | 4
"Scotty" | 6
}
}

Это data driven тест с двумя секциями expect и where. В секции expect мы пишем выражение которое проверяем, а в секции where мы объявили набор данных из трёх имён которые будут последовательно подставляться в это проверочное выражение expect.
Имя тестового метода объявлено в кавычках и может содержать пробелы и записано в человекочитаемом виде.

Он оказался настолько хорош, что один из ведущих, Барух, выдвинул тезис что именно Spock следует применять во всех проектах на Java для тестирования.
Из моего опыта я могу полностью подтвердить что это качественно новый уровень удобства тестирования недоступный другим фреймворкам и я полностью согласен с Барухом.

Но тут возникает несколько опасений.
1. Не все захотят изучать новый язык программирования Groovy для всего лишь тестов.
Я считаю что Groovy, в оличие от например Scala, это просто диалект Java. Почти любой код на Java является валидным кодом на Groovy. Можно прям взять файл и поменять расширение с .java на .groovy и он скомпилится. Поэтому по сути программистам можно даже не выучивать его для написания тестов.
Groovy очень простой и выразительный язык, имеющий знакомый всем синтаксис, довольно прямолинейный и обладающий отличной «вкуриваемостью».
Есть совсем небольшой гайд для тех кто переходит с Джавы и несколько статей на подобии Groovy за 15 минут. Как по мне, самое главное и что стоит выучить поскольку оно моментально сокращает код в разы (а то и в десятки раз!) — это литералы [] для коллекций и [:] для map, работа с коллекциями, null safe navigration и Groovy Truth. Выучив только эти вещи у вас будет Quick Win.

У меня был случай когда я устроился на работу Java девелопером где в описании вакансии было скромно сказано «будет плюсом знание Groovy & Grails». Я даже особо внимание не обратил, подумал что может где-то у них есть проект и может меня доучат и попросят там помочь. На собеседовании были стандартные вопросы по Джаве.
И в первый же рабочий день мне дали задание написать небольшой функционал на Groovy. Уже через час мой код был оттестирован, проревьювлен и закомичен.
Конечно, очень много времени потом ушло на разборки с всплывшими подводными камнями, всякие тонкости и выработки оптимально стиля кодирования. Также мне потребовалось изучить механизмы метапрограммирования для создания всевозможных DSL’ов.
Но, тестовый код — он всегда простой. Обычно тебе всё что нужно это вызвать метод и сверить с ожидаемым результатом. Поэтому если у вас продакшен код написан на стандартной Джаве а тесты на Груви то вы почти наверняка не столкнётесь с никакими проблемами и глубже его изучать не будет нужды.
Груви подключается как обычная dependency к проекту и имеет отличную поддержку в IntelliJ Idea. А для Спока в неё ещё есть плагин который правда непонятно зачем нужен.
По своему опыту могу сказать что мне было намного труднее вникнуть в стримы восьмой Джавы чем выучить Груви. Учитыая количество подводных камней даже JavaScript в разы будет сложнее Груви.
Так что если какой-то из разработчиков не сможет постигнуть премудрости Груви для написания тестов то он просто совсем некомпетентен.

2. Груви динамический язык, и хотя и есть возможность помечать классы как @CompileStatic но это не работает в Spock
Да, это так и потенциально есть вероятность что если, например, вы поменяли в продакшен коде имя метода а тестах забыли то в момент компиляции ошибка останется незамеченной.
Но, это совершенно не критично — вы сразу же увидите ошибку когда запустите тест и он завалится.
Кроме того, если пользоваться автоматическим рефакторингом в Idea то таких ошибок даже не возникнет.
Так что проблема совершенно надуманная.

3. Что такого даёт Спок что ради него стоит заморачиваться?

Спок тебя строго заставляет писать тесты в правильной структуре, например строго выдерживать секции given:, when:, then:. Я очень часто видел в тестах на JUnit когда эти логические секции то и дело смешиваются и программисты ленятся выносить их отдельный тест кейс.
Кроме того, все строки в секции then: автоматически являются assert и в совокупности с Groovy Truth это убирает кучу ненужного кода и читабельность невероятно возрастает.

И тут нужно внимательно остановится на асертах. В Споке появились так называемые Power Asserts — штука настолько потрясающая, что её позже перенесли в сам Groovy а позже и на другие языки программирования, но к сожалению кроме Джавы.
Вместо тысячи бестолковых assertEquals(), assertTrue() и всяких матчеров типа Hamcrest в Груви достаточно просто написать ключевое слово assert и вы получите подробный вывод что и где пошло не так:

assert [[1,2,3,3,3,3,4]].first().unique() == [1,2,3]

// Output:
//
// Assertion failed:
// assert [[1,2,3,3,3,3,4]].first().unique() == [1,2,3]
// | | |
// | | false
// | [1, 2, 3, 4]
// [1, 2, 3, 4]

На практике это просто Вау фича! Причём теперь фича самого Груви, так что даже обычный JUnit тест на Груви будет в тысячу раз приятнее.

Ну и лично моя самая любимая фича, это data driven tests. Спок даёт простой и удобный DSL для быстрого написания набора тестовых наборов. Я уже приводил пример выше, но у Николая Алименкова возникло сомнение насчёт их идеалогической правильности.
Когда один из наборов из where: не проходит то весь тест кейс помечается как failed, даже если остальные проходили.
В результате репортинг становится не очень полезным.

Если вы хотите видеть детальный отчёт какой именно тестовый набор провалился, вы можете пометить тест кейс аннотацией @Unroll.
Но Николай вполне справедливо заметил: хорошо, теперь я буду знать какой конкретно тестовый метод не прошёл, но я не буду знать почему, ведь комментария нету.
Следовательно для того чтобы оставить комментарий всё равно придётся дата дривен тест разбивать на несколько тест кейсов.

Когда я столкнулся с точно такой же проблемой я тоже расстроился и хотел было уже разбивать несколько тест кейсов когда смекнул как это красиво решить.
К аннотации @Unroll можно передать шаблон в который будут вставляться значения из data table. И ничто нам не мешает в дата тейбле сделать ещё одно поле с комментарием.
Например, допустим у нас есть валидатор пароля и тест на него может выглядеть так:

@Unroll
def 'checkPassword(#password) valid=#valid : #comment'() {
given:
PasswordValidator validator = new PasswordValidator()
expect:
validator.validate(password) == valid
where:
password | valid | comment
'pwd' | false | 'too short'
'very long password' | false | 'too long'
'password' | false | 'Not enought strength'
'h!Z7abcd' | true | 'valid password'
}

Чётко, кратко и по делу. Такой же тест на JUnit был бы больше в четыре раза и в десять раз менее читабельнее.
Легкость создания дата дривен тестов тебя прям подталкивает их писать — обычно нужно всего-то лишь добавить одну строчку.

В JUnit для этого приходится мучаться параметризированными тестами и почти всегда никто этим просто не заморачиватся.
В результате множество кейсов остаются непокрытыми и множество багов проходят на продакшен.

Я например писал тесты для Grails Cookies плагина и просто посмотрите сколько кейсов в нём покрыто CookieResponseSpec.groovy.

Кроме этого в Споке есть ещё множество приятностей, например моки которые делают жизнь намного лучше. В нём всё построенно на DSL с которым придётся немного подразобратся, но его немного и он легко запоминается.

Вообщем, подытожу немного перефразировав Баруха: Если вы пишете тесты не на Споке — вы занимаетесь хернёй.

Кстати сегодня выйдет пятая версия JUnit с поддержкой лямбд. Я хоть и донейтил даже на этот проект, но посмотрел на документацию и взгрустнул.
В обозримом будущем в мире Джавы пока ещё даже не предвидеться ничего лучше чем Спок.

Power Assert in Vim

Power Assert for Java and others

Peter Niederwieser, creator of Spock, the best testing framework ever, also contributed to Groovy programming language a very handy feature: Power Asserts.

int x = 1;
assert x == 2;

// Output:
//
// Assertion failed:
// assert x == 2
//        | |
//        1 false

As you can see, we can see each value of each statement, and this is very cool, especially for unit tests.
It makes all this JUnit bullshit like assertEquals(), assertTrue, assertNull, and all matchers like Hamcrest absolutely unneeded.
I really hate all matchers like FEST or Hamcest, because they aren’t natural and repeat code itself.

So now you can breathe freely without any repeating yourself.

You can see a talk where Peter Niederwieser present Spock specifications
https://vimeo.com/33947244

Also Peter created Power Asserts lib for Scala
https://github.com/pniederw/expecty

But,
Unfortunately, you can’t make a Power Asserts in Java, because it doesn’t have an AST preprocessor.
UPD Now we have the library https://github.com/jkschneider/java-power-assert but with limitations

If you, like me, have a big project with tests written in Java+JUnit, the only one way that I found, is convert them to Groovy (in 99% just change file extension from Java to Groovy) or you can compile your tests with groovyc — it’s Groovy Compiler but compiles Java too as well.
After compiling with groovyc asserts in Java behaves like in Groovy. Maybe this is the only one case when something written in Java works differently when compiled with Groovy.

For example here is simple test in Java

import org.junit.Test

class UserTest {
@Test
public void testName() {
String username = "olololo";
assert username.equals("trololo");
}

}

After compiling with groovyc it’s decompiled bytecode will looks like:

// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.codehaus.groovy.runtime.powerassert.AssertionRenderer;
import org.codehaus.groovy.runtime.powerassert.ValueRecorder;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.junit.Test;

public class UserTest implements GroovyObject {
public UserTestGroovy() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}

@Test
public void testName() {
CallSite[] var1 = $getCallSiteArray();
String username = "olololo";
ValueRecorder var3 = new ValueRecorder();

try {
CallSite var10000 = var1[0];
var3.record(username, 8);
Object var6 = var10000.call(username, "trololo");
var3.record(var6, 17);
if(DefaultTypeTransformation.booleanUnbox(var6)) {
var3.clear();
} else {
ScriptBytecodeAdapter.assertFailed(AssertionRenderer.render("assert username.equals(\"trololo\")", var3), (Object)null);
}

} catch (Throwable var5) {
var3.clear();
throw var5;
}
}
}

As you can see, it uses ValueRecorder to store all evaluated expressions and it will render a source code line. And it may be executed as class file (may require groovy-all dependency in classpath):

Assertion failed:

assert username.equals("trololo")
|        |
olololo  false

at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:402)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(ScriptBytecodeAdapter.java:650)
at com.github.stokito.UserTest.testName(UserTest.java:9)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)

Unfortunately currently IntelliJ can’t compile Java files with Groovyc. It always compiles *.java files with javac.
I created an issue Allow groovyc as compiler for Java classes, please vote for it.

Power Asserts in other languages

Kotlin

does not exist but authors know about Power Assertions and had intentions to do them.

.NET languages

https://github.com/PowerAssert/PowerAssert.Net

JavaScript

https://github.com/power-assert-js/power-assert
https://github.com/power-assert-js/espower-typescript for TypeScript
https://github.com/power-assert-js/espower-babel
https://github.com/power-assert-js/babel-plugin-espower
https://github.com/twada/battlefield-sourcemaps
https://github.com/deepblue-will/coffee-power-assert for Cofee Script
https://github.com/twada/fly-espower

Ruby

There is a gem https://github.com/k-tsj/power_assert and here is presentation from author:
https://speakerdeck.com/k_tsj/power-assert-in-ruby

https://github.com/joker1007/rspec-power_assert
https://rubygems.org/gems/power_assert/versions/0.2.4
https://github.com/hsbt/minitest-power_assert
https://github.com/yui-knk/pry-power_assert for Pry REPL

Others

https://github.com/keygx/PAssert XCTest for Swift
https://github.com/rosylilly/power_assert.cr for Crystal (static Ruby)
https://github.com/ma2gedev/power_assert_ex for Elixir
https://github.com/gifnksm/power-assert-rs for Rust
https://github.com/haya14busa/vital-power-assert for Vim script
https://github.com/ToQoz/gopwt for Go

TODO: write about power assets in https://en.wikipedia.org/wiki/Assertion_(software_development)

Python или Java? Хм… а может Groovy?

На DOU новый холивар Что выбрать бывшему учёному — Python или Java?

Хочу оставить позицию младшего научного сотрудника и переквалифицироваться в разработчика. Всё высшее образование было профильным — информатика. Коммерческого опыта в программировании нет, только эксперименты и учебные проекты. В последнее время предпочитаю писать их на Python, для него есть хорошие научные библиотеки.
Цель — найти позицию разработчика, которая могла бы мне дать начальный опыт. В перспективе — вывести на более наукоёмкие позиции (анализ данных, машинное обучение, обработка естественных языков).
Город большого значения не имеет, особых требований по начальной зарплате нет — лишь бы хватало на съёмную квартиру и проживание.
Работу начну искать где-то с весны, поэтому остаётся время на подготовку. Осталось решить, в каком направлении мне развиваться.
Некоторые соображения (рынок я знаю плохо, поэтому можете смело поправлять):
1) По Java вакансий намного больше, чем по Python. Кроме того, можно официально подтвердить знания Java, сдав какой-нибудь начальный экзамен Oracle — это может привлечь внимание HR.
2) С другой стороны, в Java есть множество неизвестных мне корпоративных технологий и фреймворков, в которых можно надолго утонуть. В Python с этим проще — если я освою базовый Django (может, Flask, Bottle) и сделаю на них несколько личных проектов, то это должно покрыть начальные требования.
3) Java — это в основном аутсорс, Python — это в основном продуктовые компании (или у меня сложилось такое впечатление).
Что будет лучшим стартом, если мне хочется дорасти до профессиональной работы с анализом данных, прогнозированием, построением рекомендательных систем и т.п.?

Как и следовало ожидать разгорелся сильный холивар, но почему-то не упомянули Groovy — язык который как раз ииногда и называют помесью Питона и Джавы.
Я решил ответить:

Пока единственный известный мне настоящий специалист по Data Science поработавший в Германии на суперкомпьютере, работал таки на Java, и я вообще сомневаюсь как что-то действительно большее Hello world можно написать на Python.
Хотя, конечно, хорошие программисты могут писать на чём угодно.

Язык вроде как выразительный, но большой проект тупо невозможно написать без статической типизации, поэтому его любят для коротких программ. Идеальной областью где нужны эти два свойства это учебники по алгоритмам далёким от жизни из-за чего у Питона и появилась репутация языка учёных.

И всё таки лучше не забывать что Питон обманчиво кажется простым: он довольно своебразен и уводит в свою вселеную.

Мне кажется, что пока лучше выбирать JVM, как платформу. Если не будет хватать возможностей Java то можно попробовать другие JVM языки: тот же JPython, Scala или Groovy.

Scala это отличный язык который довольно гармонично совместил в себе главные достижения других языков программирования. Но Scala статически компилируемый язык и это затрудняет использовании магии.

В случае, если захочется большей выразительности, DSL или динамики то есть Groovy — великолепный язык программирования, обратно совместимый с Java и обладающий наверное всеми теми качествами за которые тут так сильно хвалят Питон.

На Groovy кстати есть даже всякий научный софт, например GroovyLab:

GroovyLab is a MATLAB-like scientific programming environment based on Groovy with extensive plotting support, default access to the best Java scientific libraries, user-friendly interface, easy installation of toolboxes, Computer Algebra support and many other features.

Вообщем, могу сказать точно что выбрав джаву не прогадаешь
joseph-ducreux-meme-disregard-python-acquire-java-080b2b


Не поленитесь, и почитайте ещё несколько очень важных моих статей для начинающих программистов, студентов и джуниоров.
Также не забудьте вступить в группу IT Juniors куда я пытаюсь собирать ссылки на другие полезные статьи для вас и анонсы курсов и интернатуры в компаниях.

toString() contract

Today I read great article of Fabian Kessler Java toString(): the Program Logic vs. Debug Dilemma.

In short, it is not obvious how to override method and what exactly means «string representation of object»:

Hrm. So there are mainly 2 uses:
String representation: toString() returns the object’s value «as string» as close as possible.
It is absolutely required to override toString(), and to do it in this way.

Debug information: the object’s values for the human.
For example IntelliJ IDEA’s default toString() template generates this kind.
It’s just nice to have.

JavaDoc says:

Returns a string representation of the object. In general, the
toString method returns a string that
«textually represents» this object. The result should
be a concise but informative representation that is easy for a
person to read.
It is recommended that all subclasses override this method.

Quotation from the «Effective Java» book:

One important decision you’ll have to make when implementing a toString
method is whether to specify the format of the return value in the documentation.
It is recommended that you do this for value classes, such as phone numbers or
matrices. The advantage of specifying the format is that it serves as a standard,
unambiguous, human-readable representation of the object. This representation
can be used for input and output and in persistent human-readable data objects,
such as XML documents. If you specify the format, it’s usually a good idea to pro-
vide a matching static factory or constructor so programmers can easily translate
back and forth between the object and its string representation. This approach is
taken by many value classes in the Java platform libraries, including BigInteger ,
BigDecimal , and most of the boxed primitive classes.

I found that most of code that I saw uses some unwritten «toString() contact». The contract mostly based on difference between Entity and Value objects.

1. «String typization» mechanism

The Delphi programming language has a great conception of Variant type. It is difficult to me to explain this in few words. It is like an universal container for the primitive types.

This is like a def keyword in Groovy: it mean any type. But any primitive type, not an basic Object.
And there is convention like a «Groovy Truth» how one primitive type should be converted to another.

Unfortunately Java hasn’t Variant type, so it uses String as it replacement. Hence, any primitive type you can convert to sting presentation: numbers, dates and boolean.
This is called StringlyTyped pattern and actually it makes code understanding painfully.

String typization

That’s why we should look on toString() as on data converter «as string».
And it should work only with Value classes and primitive datatypes wrappers, i.e. Integer, Boolean, Double, Date, Phone Numbers etc.

Important thing here is that this resulting string representation of value can be parsed verse to get an original value.

2. Serialization

In more common sense toString() it is a kind of serialization.
Even more, toString() can return JSON on CSV value.
But you should remember that deserialization is not granted by method contract.
That’s why you should use standard approach and implement Serializable interface.

But you still can use toString() as serialization for basic types that already contains written toString(), parse() and valueOf() methods.

3. Human readable representation of object

Another thing is when we need to show the object to a user. In this case it may be better to create another method called like a getDisplayName(), getTitle() or getCaption().

For example User class can contain getFullName() that return First Name and Second Name with space between them.

public class User {
   String firstName;
   String lastName;
   
   String getFullName() {
       return firstName + " " + lastName;
   }
}

Why it is better? First of all human readable names can be different and one string representation method may be not enough.

For example lets take a look on Facebook. In the private mail conversation between two users we can just write first name. At the public profile page we can display «First name + (nickname) Second name».

Also, usually this kind of display names can be localized. For example name of month or book title.

4. Log/Debug representation

In all other cases toString() used for logging or debug output. Just to make developer life easier.

The Groovy has a helpful @ToString annotation that generates this method in runtime.
For example class User can contain toString() method that return user login or email:

@ToString(includeNames = true, includes = 'email,firstName')
class User {
   String email
   String firstName
}

...

def user = new User(email: "admin@example.com", firstName: "Administrator")
assert user.toString() == "User(email:admin@example.com, firstName:Administrator)"

...
log.info("User ${user} logged in")

Here log.info() will call toString() method that is not good. I’m totally agree with Fabian and such method should be named toDebug() or dump(), but not toString().

But you should always remember that logging can cause problems for security and performance.
Not always you need to write to log everything that object contains.
For example if a hacker got an access to logs he can see a lot of stuff like user password and other credentials or even credit card number.
So you should take care to exclude from logs sensitive information.
Always mask credit card numbers and users personal info.

Another big problem is log injection vulnerability when some XSS scripts from logs can be executed in dashboard. Please read carefully OWASP Logging Cheat Sheet

Conclusion

Using this conventions may make your code better and safer.
I would be very thankful if you share conventions from your experience 🙂