<p>I'm still new and trying to figure out how to do integration tests that require DB access for the past 2-3 days already and I've got nowhere. I dont know how to structure my code for such tests.</p>
<p>This is how I usually structure my code if I dont do tests and it sticked from a codebase I found on github:</p>
<p><a href="https://play.golang.org/p/xUwpgnIU2Ko">https://play.golang.org/p/xUwpgnIU2Ko</a></p>
<p>If anyone can give me some advice like how to structure it better, make it easier for testing then I'd really appreciate it. I'm self learning and having bit hard time with testing</p>
<hr/>**评论:**<br/><br/>sacrehubert: <pre><p>Try using the <a href="https://en.wikipedia.org/wiki/Data_access_object">DAO pattern</a>. The basic idea is that your database object should be wrapped in an interface that exposes business-logic operations. For instance:</p>
<pre><code>type DAO interface {
FetchWidget() (*Widget, error)
NewWidget() (*Widget, error)
StoreWidget(*Widget) error
}
</code></pre>
<p>You would then implement something like a <code>postgresDAO</code> that satsifies <code>DAO</code>. </p>
<p>The win for you is that you can create implementations of your DAO for testing. These don't have to be backed with databases at all (which is preferable in many cases).</p></pre>hybsuns: <pre><p>I believe there are some serious issues in the architecture of your project which contribute to the difficulty of testing.</p>
<p>Firstly, you put you entity <code>Movie</code> and the data access code <code>func (m *movieRepo) Read() ([]Movie, error)</code> in the same layer of the system. This is generally a bad design since <code>Movie</code> is the business logic and <code>func (m *movieRepo) Read() ([]Movie, error)</code> is a detail (data access). They should be put into separated packages (at least).</p>
<p>The second issue is that your data access code is tightly coupled with package <code>app</code>. This is not desirable since your data access code is already a low level module and deals with I/O. In your case, you cannot instantiate a repository unless you instantiate an <code>App</code>. However, you <code>App</code> is really just a connection to the database. I would question if adding such a layer of abstraction is necessary.</p>
<p>I believe your data access code should be structured like this:</p>
<p><code>package dataaccess</code></p>
<p><code>type PostgresMovieRepository struct {</code></p>
<p><code>Database *sql.DB</code></p>
<p><code>}</code></p>
<p><code>func (m PostgresMovieRepository) Read() ([]Movie, error) {</code> </p>
<p><code>// query movies</code></p>
<p><code>return movies, nil</code></p>
<p><code>}</code></p>
<p>In your test code, you can do something like this: </p>
<p><code>package dataaccess_test</code></p>
<p><code>var movieRepo = dataaccess.PostgresMovieRepository{</code><br/>
<code>Database: CreateYourDBConnectionHere(),</code><br/>
<code>}</code><br/>
<code>func TestPostgresMovieRepository_Read(t *testing.T) {</code></p>
<p><code>movies, err :=</code> <a href="https://movieRepo.Read"><code>movieRepo.Read</code></a><code>()</code></p>
<p><code>// add your assertion statements here</code></p>
<p><code>}</code></p>
<p>Note that your connection can be a actual DB connection or a mock connection.</p>
<p>I would suggest looking into dependency inversion principle (the D in SOLID principle). This principle can guide you how to structure your code.</p></pre>never-_-laugh: <pre><p>Have you considered using docker? You could stand up a docker env and populate the db with sample data to run your tests. This would just get destroyed every time and keep your live db safe. There is obviously a lot more detail to this and setting it up would require you to familiarize with docker, docker-compose, and maybe write a bash script. The end result is a good way to reproduce a similar env as what your production env looks like and allow you to keep the live db clean.</p></pre>f12_amish: <pre><p>I'm just getting into docker myself and wondered how you give the app access to a docker container. Do you just setup docker on a specified port and have a static DB URL where the app checks for the database?</p></pre>bschwind: <pre><blockquote>
<p>Do you just setup docker on a specified port and have a static DB URL where the app checks for the database?</p>
</blockquote>
<p>This is pretty much how it works. Docker containers can bind to ports on the host network, so a postgres container can bind to port 5432 or any other port you want to specify, and your app will just connect to it like any other database endpoint.</p></pre>never-_-laugh: <pre><p>That is one approach. You can also create a docker network but that may be more than what you need. I would definitely recommend reading the the latest docker docs. I would also say be careful with some of the tutorial people post. Not that they are bad but be mindful of the version of docker-compose they use. There have been added features that change the ideal work flow with docker-compose. So I would try to stick with the official docs or anything that is at least posted very recent.</p></pre>earthboundkid: <pre><p><a href="https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1" rel="nofollow">https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1</a></p></pre>p4r14h: <pre><p>So in practice, when you have multiple environments like dev, staging (integ) and prod you’d have a config class that stores all the runtime configuration (like your SQL DSN). You can then set an ENV var like ‘COMPANY_ENV=production’ which would affect which config file is loaded and serialized into your config class. </p>
<p>For instance, we have:</p>
<p>base.yaml
dev.yaml
production.yaml</p>
<p>In my dev.yaml I have a set of nested structures: ‘datastore.mysql.dsn’</p>
<p>In my main this file is loaded and turned into a config struct that is passed around (similar to your app type) and in my db.go I use Gorm to either create a local SQLite DB for dev or give it a remote connection string for staging/production. </p>
<p>For integration tests you probably want to use the same SQL server as production so you’d need a separate SQL database available in your staging environment. When you start the test suite you can bootstrap the DB and schemas- the only difference is you’d pass in a staging config struct then execute the code you want to test and verify the expected result. </p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传